COPASI API  4.16.103
CLLayoutRenderer.cpp
Go to the documentation of this file.
1 // Copyright (C) 2015 by Pedro Mendes, Virginia Tech Intellectual
2 // Properties, Inc., University of Heidelberg, and The University
3 // of Manchester.
4 // All rights reserved.
5 
6 #ifdef WIN32
7 # define _USE_MATH_DEFINES
8 #pragma comment(lib,"opengl32.lib")
9 #endif
10 
11 #include <limits>
12 #include <cmath>
13 #include <stdio.h>
14 #include <cctype>
15 #include <algorithm>
16 
17 #include <sbml/packages/render/sbml/Transformation.h>
18 
19 // this define is needed so that the glFogCoordf function is found
20 // in linux
21 #define GL_GLEXT_PROTOTYPES
22 #include "CLLayoutRenderer.h"
23 #include "copasi/layout/CLBase.h"
33 #include "copasi/layout/CLGroup.h"
36 #include "copasi/layout/CLStyle.h"
43 #include "copasi/layout/CLImage.h"
47 #include "copasi/layout/CLText.h"
48 #include "copasi/layout/CLCurve.h"
49 #include "copasi/layout/CLayout.h"
51 #include "copasi/layout/CLGlyphs.h"
53 #include "copasi/model/CModel.h"
54 #include "copasi/model/CReaction.h"
56 
57 // opengl includes
58 #ifdef _WIN32
59 # include "windows.h" // Needed for OpenGL
60 # define _USE_MATH_DEFINES // without the following define, M_PI will not be declared under windows
61 // disable warning about unsafe fopen
62 # pragma warning(disable : 4996)
63 #endif // _WIN32
64 
65 #ifdef __APPLE__
66 #include <string>
67 #include <stdlib.h>
68 #include <mach-o/dyld.h>
69 # include "OpenGL/gl.h"
70 # include "OpenGL/glu.h"
71 #else
72 # include "GL/gl.h"
73 # include "GL/glu.h"
74 # include "GL/glext.h"
75 # ifndef _WIN32
76 # define GLX_GLXEXT_PROTOTYPES
77 # include "GL/glx.h"
78 # endif // _WIN32
79 #endif // __APPLE__
80 
81 // specifies how many segments are used to approximate the rounded
82 // corners of a rectangle
83 const unsigned int CLLayoutRenderer::NUM_CORNER_SEGMENTS = 10;
84 
85 // specifies how many segments are used to draw circles and ellipses
86 const unsigned int CLLayoutRenderer::NUM_CIRCLE_SEGMENTS = 60;
87 
88 // specifies how many points to calculate for a cubic bezier curve
89 const unsigned int CLLayoutRenderer::NUM_BEZIER_POINTS = 20;
90 
91 // a constant that is considered to be zero
92 const double CLLayoutRenderer::ALMOST_ZERO = 1e-12;
93 
94 // a constant that determines the maximal size of a gradient texture
95 const unsigned int CLLayoutRenderer::GRADIENT_TEXTURE_SIZE_LIMIT = 512;
96 
97 // a constant that determines the maximal size of a gradient texture
99 
100 /**
101  * constructor for global render information
102  */
103 CLLayoutRenderer::CLLayoutRenderer(CLayout* pLayout, const CLGlobalRenderInformation* pRenderInformation, const CCopasiVector<CLGlobalRenderInformation>* pGlobalRenderInformationList, const CModel* pModel, const std::string& baseDir):
104  mStateList(),
105  mCurrentAttributes(),
106  mpModel(pModel),
107  mpLayout(pLayout),
108  mpResolver(NULL),
109  mBaseDir(baseDir),
110  mZoomFactor(1.0),
111  mAspect(1.0),
112  mX(0.0),
113  mY(0.0),
114  mW(0.0),
115  mH(0.0),
116  mpGlobalRenderInfoList(pGlobalRenderInformationList),
117  mpFontRenderer(NULL),
118  mDeduceSpeciesReferenceRoles(false),
119  mpSelectionBox(NULL),
120  mpImageTexturizer(NULL)
121  , mHighlight(true)
122  , mFogDensity(0.8f)
123  , mGLFunctionsInitialized(false)
124  , mpGlFogCoordfEXT(NULL)
125 {
126  this->mHighlightColor[0] = 0.5;
127  this->mHighlightColor[1] = 0.0;
128  this->mHighlightColor[2] = 0.0;
129  this->mHighlightColor[3] = 1.0;
130  this->mFogColor[0] = 0.5;
131  this->mFogColor[1] = 0.5;
132  this->mFogColor[2] = 0.5;
133  this->mFogColor[3] = 1.0;
134  this->change_style(pRenderInformation);
135 }
136 
137 /**
138  * constructor for local render information
139  */
140 CLLayoutRenderer::CLLayoutRenderer(CLayout* pLayout, const CLLocalRenderInformation* pRenderInformation, const CCopasiVector<CLGlobalRenderInformation>* pGlobalRenderInformationList, const CModel* pModel, const std::string& baseDir):
141  mStateList(),
142  mCurrentAttributes(),
143  mpModel(pModel),
144  mpLayout(pLayout),
145  mpResolver(NULL),
146  mBaseDir(baseDir),
147  mZoomFactor(1.0),
148  mAspect(1.0),
149  mX(0.0),
150  mY(0.0),
151  mW(0.0),
152  mH(0.0),
153  mpGlobalRenderInfoList(pGlobalRenderInformationList),
154  mpFontRenderer(NULL),
155  mDeduceSpeciesReferenceRoles(false),
156  mpSelectionBox(NULL)
157  , mHighlight(true)
158  , mFogDensity(0.8f)
159  , mGLFunctionsInitialized(false)
160  , mpGlFogCoordfEXT(NULL)
161 {
162  this->mHighlightColor[0] = 0.5;
163  this->mHighlightColor[1] = 0.0;
164  this->mHighlightColor[2] = 0.0;
165  this->mHighlightColor[3] = 1.0;
166  this->mFogColor[0] = 0.5;
167  this->mFogColor[1] = 0.5;
168  this->mFogColor[2] = 0.5;
169  this->mFogColor[3] = 1.0;
171  this->change_style(pRenderInformation);
172 }
173 
174 void CLLayoutRenderer::change_style(const CLGlobalRenderInformation* pRenderInformation, bool defaultStyle)
175 {
176  if (this->mpResolver != NULL)
177  {
178  delete this->mpResolver;
179  this->mpResolver = NULL;
180  }
181 
182  if (pRenderInformation)
183  {
184  this->mpResolver = new CLRenderResolver(*pRenderInformation, *this->mpGlobalRenderInfoList);
185  }
186 
187  if (defaultStyle == true)
188  {
189  this->setDeduceSpeciesReferenceRoles(true);
190  }
191 
192  this->clear_cached_data();
193 
194  if (this->mpFontRenderer != NULL)
195  {
196  this->analyse_render_information(mX, mY, mX + mW / this->mZoomFactor, mY + mH / this->mZoomFactor);
197  }
198 
200  {
202  }
203 }
204 
206 {
207  if (this->mpResolver != NULL)
208  {
209  delete this->mpResolver;
210  this->mpResolver = NULL;
211  }
212 
213  if (pRenderInformation)
214  {
215  this->mpResolver = new CLRenderResolver(*pRenderInformation, this->mpLayout->getListOfLocalRenderInformationObjects(), *this->mpGlobalRenderInfoList);
216  }
217 
218  this->clear_cached_data();
219 
220  if (this->mpFontRenderer != NULL)
221  {
222  this->analyse_render_information(mX, mY, mX + mW / this->mZoomFactor, mY + mH / this->mZoomFactor);
223  }
224 
226  {
228  }
229 }
230 
231 /**
232  * destructor.
233  */
235 {
236  if (mpResolver != NULL) delete mpResolver;
237 
238  // delete the data in all the maps
239  this->clear_cached_data();
240 
241  if (this->mpFontRenderer)
242  {
243  delete this->mpFontRenderer;
244  }
245 
246  if (this->mpSelectionBox != NULL)
247  {
248  delete this->mpSelectionBox;
249  }
250 }
251 
252 /**
253  * Extracts the group attributes from the outermost group of a style.
254  */
256 {
258 }
259 
260 /**
261  * Method that draws a line with the given start and end points.
262  * All the other parameter like color, linewidth etc. have to be set
263  * before.
264  */
265 void CLLayoutRenderer::draw_line_segment(double x1, double y1, double z1, double x2, double y2, double z2, double line_width, bool texture, double s1, double s2)
266 {
267  // calculate the 4 points of the rectangle considering the line width
268  // actually the correct thing to do would be to draw a tube, but right now
269  // this is takes to many ressources, so we draw a rectangle and use the same
270  // mapping as for the line endings.
271  // calculate the direction vector
272  double vx1 = x2 - x1;
273  double vy1 = y2 - y1;
274  double vz1 = z2 - z1;
275  double length = sqrt(vx1 * vx1 + vy1 * vy1 + vz1 * vz1);
276 
277  // calculate the normal to this vector
278  double vx2 = 0.0;
279  double vy2 = 0.0;
280  double vz2 = 0.0;
281  double half_width = line_width / 2.0;
282 
283  if (fabs(vx1) < ALMOST_ZERO && vz1 < ALMOST_ZERO)
284  {
285  // scale by the line_width
286  vx2 = -vy1 / length * half_width;
287  vy2 = 0.0;
288  vz2 = 0.0;
289  }
290  else
291  {
292  // scale by the line_width
293  double normY = vy1 / length;
294  vx2 = -normY * vx1 / length * half_width;
295  vy2 = (1 - normY * normY) * half_width;
296  vz2 = -normY * vz1 / length * half_width;
297  double normLength = half_width / sqrt(vx2 * vx2 + vy2 * vy2 + vz2 * vz2);
298  vx2 *= normLength;
299  vy2 *= normLength;
300  vz2 *= normLength;
301  }
302 
303  // calculate the 4 points
304  GLfloat* pDatapoints = new GLfloat[12];
305  pDatapoints[0] = (GLfloat)(x1 + vx2);
306  pDatapoints[1] = (GLfloat)(y1 + vy2);
307  pDatapoints[2] = (GLfloat)(z1 + vz2);
308  pDatapoints[3] = (GLfloat)(x1 - vx2);
309  pDatapoints[4] = (GLfloat)(y1 - vy2);
310  pDatapoints[5] = (GLfloat)(z1 - vz2);
311  pDatapoints[6] = (GLfloat)(x2 + vx2);
312  pDatapoints[7] = (GLfloat)(y2 + vy2);
313  pDatapoints[8] = (GLfloat)(z2 + vz2);
314  pDatapoints[9] = (GLfloat)(x2 - vx2);
315  pDatapoints[10] = (GLfloat)(y2 - vy2);
316  pDatapoints[11] = (GLfloat)(z2 - vz2);
317  // enable the line stippling texture if necessary
318  GLfloat* pTextureCoordinates = NULL;
319 
320  if (texture)
321  {
322  pTextureCoordinates = new GLfloat[4];
323  pTextureCoordinates[0] = (GLfloat)s1;
324  pTextureCoordinates[1] = (GLfloat)s1;
325  pTextureCoordinates[2] = (GLfloat)s2;
326  pTextureCoordinates[3] = (GLfloat)s2;
327  // create an array for texture coordinates
328  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
329  glTexCoordPointer(1, GL_FLOAT, 0, pTextureCoordinates);
330  }
331 
332  // just draw as a triangle strip
333  glEnableClientState(GL_VERTEX_ARRAY);
334  glVertexPointer(3, GL_FLOAT, 0, pDatapoints);
335  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
336  glDisableClientState(GL_VERTEX_ARRAY);
337 
338  // disable the line stippleling texture if necessary
339  if (texture)
340  {
341  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
342  delete[] pTextureCoordinates;
343  }
344 
345  delete[] pDatapoints;
346 }
347 
348 /**
349  * Method that draws a curve from the layout extension.
350  * All the other parameter like color, linewidth etc. have to be set
351  * before.
352  */
353 void CLLayoutRenderer::draw_curve(const CLCurve* pCurve, bool drawBasepoints)
354 {
355  // set some attributes from mCurrentAttributes (stroke, stroke_width,
356  // stroke_dasharray)
357  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
358  assert(pos != this->mColorMap.end());
359  const CLRGBAColor& c = pos->second;
360  glColor4ub(c.mR, c.mG, c.mB, c.mA);
361  size_t i, iMax = pCurve->getNumCurveSegments();
362  const CLLineSegment* pLineSegment = NULL;
363  // apply the current transformation
364  glMatrixMode(GL_MODELVIEW);
365  glPushMatrix();
366 
367  if (memcmp(mCurrentAttributes.mpTransform, CLTransformation::getIdentityMatrix(), 12 * sizeof(double)))
368  {
369  // move back to the current offset
370  glTranslated(this->mCurrentAttributes.mX, this->mCurrentAttributes.mY, this->mCurrentAttributes.mZ);
371  GLdouble* matrix = new GLdouble[16];
373  glMultMatrixd(matrix);
374  delete[] matrix;
375  // move to 0.0,0.0,0.0
376  glTranslated(-this->mCurrentAttributes.mX, -this->mCurrentAttributes.mY, -this->mCurrentAttributes.mZ);
377  }
378 
379  // calculate the data points
380  std::vector<simple_point> v;
381  simple_point p;
382  const CLPoint* pP;
383  const CLPoint* pP2, *pP3, *pP4;
384  GLdouble* pData = NULL;
385  CLPoint lastEnd;
386  double delta_phi = 2 * M_PI / NUM_CIRCLE_SEGMENTS;
387  double phi = 0.0;
388  GLdouble* pCircleData = NULL;
389 
390  // if we need to draw the basepoints of a curve,
391  // we calculate the circle points here.
392  // TODO We could actually move this code somewhere else and
393  // TODO calculate it only once at startup, but for now this works well enough
394  if (drawBasepoints == true)
395  {
396  pCircleData = new GLdouble[3 * (NUM_CIRCLE_SEGMENTS + 2)];
397  pCircleData[0] = 0.0;
398  pCircleData[1] = 0.0;
399  pCircleData[2] = 0.0;
400 
401  for (i = 1; i <= NUM_CIRCLE_SEGMENTS; ++i)
402  {
403  phi = i * delta_phi;
404  pCircleData[i * 3] = cos(phi);
405  pCircleData[i * 3 + 1] = sin(phi);
406  pCircleData[i * 3 + 2] = 0.0;
407  }
408 
409  // close the circle
410  pCircleData[i * 3] = pCircleData[3];
411  pCircleData[i * 3 + 1] = pCircleData[4];
412  pCircleData[i * 3 + 2] = pCircleData[5];
413  }
414 
415  for (i = 0; i < iMax; ++i)
416  {
417  pLineSegment = pCurve->getSegmentAt(i);
418 
419  if (pLineSegment->isBezier())
420  {
421  pP = &pLineSegment->getStart();
422 
423  // check if we have a break in the line
424  if (i != 0 && !((*pP) == lastEnd))
425  {
426  // draw the lines the are currently in v and clear v
427  iMax = v.size();
428 
429  if (iMax > 1)
430  {
431  pData = new GLdouble[3 * iMax];
432  size_t index = 0;
433  const simple_point* pSimple = NULL;
434 
435  for (i = 0; i < iMax; ++i)
436  {
437  pSimple = &v[i];
438  pData[index++] = pSimple->mX;
439  pData[index++] = pSimple->mY;
440  pData[index++] = pSimple->mZ;
441  }
442 
443  // draw the line
444  this->draw_line(iMax, pData);
445  delete[] pData;
446  }
447 
448  v.clear();
449  }
450 
451  pP2 = &pLineSegment->getEnd();
452  lastEnd = *pP2;
453  pP3 = &pLineSegment->getBase1();
454  pP4 = &pLineSegment->getBase2();
455  pData = new GLdouble[3 * NUM_BEZIER_POINTS];
457  pP3->getX(), pP3->getY(), pP3->getZ(),
458  pP4->getX(), pP4->getY(), pP4->getZ(),
459  pP2->getX(), pP2->getY(), pP2->getZ(),
460  NUM_BEZIER_POINTS, pData);
461  size_t j;
462  size_t index = 0;
463 
464  for (j = 0; j < NUM_BEZIER_POINTS; ++j)
465  {
466  p.mX = pData[index++];
467  p.mY = pData[index++];
468  p.mZ = pData[index++];
469  v.push_back(p);
470  }
471 
472  delete[] pData;
473 
474  // draw the base points if requested
475  if (drawBasepoints == true)
476  {
477  // we have to draw four circles in the same color as the line,
478  // but a little wider
480  {
481  size_t j;
482  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
483  const CLRGBAColor& c = pos->second;
484  glColor4ub(c.mR, c.mG, c.mB, c.mA);
485  glPushMatrix();
486  glTranslated(pP->getX(), pP->getY(), pP->getZ());
488  glBegin(GL_TRIANGLE_FAN);
489  glVertex3f(0.0, 0.0, 0.0);
490 
491  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
492  {
493  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
494  }
495 
496  glEnd();
497  glPopMatrix();
498  glPushMatrix();
499  glTranslated(pP2->getX(), pP2->getY(), pP2->getZ());
501  glBegin(GL_TRIANGLE_FAN);
502  glVertex3d(0.0, 0.0, 0.0);
503 
504  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
505  {
506  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
507  }
508 
509  glEnd();
510  glPopMatrix();
511  glPushMatrix();
512  glTranslated(pP3->getX(), pP3->getY(), pP3->getZ());
514  glBegin(GL_TRIANGLE_FAN);
515  glVertex3d(0.0, 0.0, 0.0);
516 
517  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
518  {
519  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
520  }
521 
522  glEnd();
523  glPopMatrix();
524  glPushMatrix();
525  glTranslated(pP4->getX(), pP4->getY(), pP4->getZ());
527  glBegin(GL_TRIANGLE_FAN);
528  glVertex3d(0.0, 0.0, 0.0);
529 
530  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
531  {
532  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
533  }
534 
535  glEnd();
536  glPopMatrix();
537  }
538  }
539  }
540  else
541  {
542  const CLPoint* pP = &pLineSegment->getStart();
543 
544  if (i != 0 && !((*pP) == lastEnd))
545  {
546  // draw the lines that are currently in v and clear v
547  iMax = v.size();
548 
549  if (iMax > 1)
550  {
551  pData = new GLdouble[3 * iMax];
552  size_t index = 0;
553  const simple_point* pSimple = NULL;
554 
555  for (i = 0; i < iMax; ++i)
556  {
557  pSimple = &v[i];
558  pData[index++] = pSimple->mX;
559  pData[index++] = pSimple->mY;
560  pData[index++] = pSimple->mZ;
561  }
562 
563  // draw the line
564  this->draw_line(iMax, pData);
565  delete[] pData;
566  }
567  }
568 
569  p.mX = pP->getX();
570  p.mY = pP->getY();
571  p.mZ = pP->getZ();
572  v.push_back(p);
573 
574  if (drawBasepoints == true)
575  {
576  // we have to draw four circles in the same color as the line,
577  // but a little wider
579  {
580  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
581  const CLRGBAColor& c = pos->second;
582  glColor4ub(c.mR, c.mG, c.mB, c.mA);
583  glPushMatrix();
584  glTranslated(pP->getX(), pP->getY(), pP->getZ());
586  glBegin(GL_TRIANGLE_FAN);
587  glVertex3d(0.0, 0.0, 0.0);
588  size_t j;
589 
590  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
591  {
592  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
593  }
594 
595  glEnd();
596  glPopMatrix();
597  }
598  }
599 
600  pP = &pLineSegment->getEnd();
601  lastEnd = *pP;
602  p.mX = pP->getX();
603  p.mY = pP->getY();
604  p.mZ = pP->getZ();
605  v.push_back(p);
606 
607  if (drawBasepoints == true)
608  {
609  // we have to draw four circles in the same color as the line,
610  // but a little wider
612  {
613  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
614  const CLRGBAColor& c = pos->second;
615  glColor4ub(c.mR, c.mG, c.mB, c.mA);
616  glPushMatrix();
617  glTranslated(pP->getX(), pP->getY(), pP->getZ());
619  glBegin(GL_TRIANGLE_FAN);
620  glVertex3d(0.0, 0.0, 0.0);
621  size_t j;
622 
623  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
624  {
625  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
626  }
627 
628  glEnd();
629  glPopMatrix();
630  }
631  }
632  }
633  }
634 
635  iMax = v.size();
636 
637  if (iMax > 1)
638  {
639  pData = new GLdouble[3 * iMax];
640  size_t index = 0;
641  const simple_point* pSimple = NULL;
642 
643  for (i = 0; i < iMax; ++i)
644  {
645  pSimple = &v[i];
646  pData[index++] = pSimple->mX;
647  pData[index++] = pSimple->mY;
648  pData[index++] = pSimple->mZ;
649  }
650 
651  // draw the line
652  this->draw_line(iMax, pData);
653  delete[] pData;
654 
655  if (drawBasepoints == true)
656  {
657  // we have to draw four circles in the same color as the line,
658  // but a little wider
660  {
661  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
662  const CLRGBAColor& c = pos->second;
663  glColor4ub(c.mR, c.mG, c.mB, c.mA);
664  glPushMatrix();
665  glTranslated(pSimple->mX, pSimple->mY, pSimple->mZ);
667  glBegin(GL_TRIANGLE_FAN);
668  glVertex3d(0.0, 0.0, 0.0);
669  size_t j;
670 
671  for (j = 0; j <= NUM_CIRCLE_SEGMENTS + 1; ++j)
672  {
673  glVertex3d(pCircleData[3 * j], pCircleData[3 * j + 1], pCircleData[3 * j + 2]);
674  }
675 
676  glEnd();
677  glPopMatrix();
678  }
679  }
680  }
681 
682  if (pCircleData != NULL) delete[] pCircleData;
683 
685  {
686  const CLLineSegment* pLS = pCurve->getSegmentAt(0);
687  const CLPoint* pP = &pLS->getStart();
688  CLPoint v;
689 
690  if (!pLS->isBezier())
691  {
692  v = CLPoint(pLS->getStart().getX() - pLS->getEnd().getX(), pLS->getStart().getY() - pLS->getEnd().getY(), pLS->getStart().getZ() - pLS->getEnd().getZ());
693  }
694  else
695  {
696  v = CLPoint(pLS->getStart().getX() - pLS->getBase1().getX(), pLS->getStart().getY() - pLS->getBase1().getY(), pLS->getStart().getZ() - pLS->getBase1().getZ());
697  }
698 
700  }
701 
702  if (!mCurrentAttributes.mEndHead.empty() && mCurrentAttributes.mEndHead != "none")
703  {
704  const CLLineSegment* pLS = pCurve->getSegmentAt(pCurve->getNumCurveSegments() - 1);
705  const CLPoint* pP = &pLS->getEnd();
706  CLPoint v;
707 
708  if (!pLS->isBezier())
709  {
710  v = CLPoint(pLS->getEnd().getX() - pLS->getStart().getX(), pLS->getEnd().getY() - pLS->getStart().getY(), pLS->getEnd().getZ() - pLS->getStart().getZ());
711  }
712  else
713  {
714  v = CLPoint(pLS->getEnd().getX() - pLS->getBase2().getX(), pLS->getEnd().getY() - pLS->getBase2().getY(), pLS->getEnd().getZ() - pLS->getBase2().getZ());
715  }
716 
718  }
719 
720  glMatrixMode(GL_MODELVIEW);
721  glPopMatrix();
722 }
723 
724 /**
725  * Method that draws a curve from the render extension.
726  * All the other parameter like color, linewidth etc. have to be set
727  * before.
728  */
730 {
731  if (!pBB || !pCurve || pCurve->getNumElements() <= 0) return;
732 
733  this->save_current_attributes();
736  // apply the current transformation
737  glMatrixMode(GL_MODELVIEW);
738  glPushMatrix();
739 
740  if (memcmp(mCurrentAttributes.mpTransform, CLTransformation::getIdentityMatrix(), 12 * sizeof(double)))
741  {
742  // move back to the current offset
743  glTranslated(this->mCurrentAttributes.mX, this->mCurrentAttributes.mY, this->mCurrentAttributes.mZ);
744  GLdouble* matrix = new GLdouble[16];
746  glMultMatrixd(matrix);
747  delete[] matrix;
748  // move to 0.0,0.0,0.0
749  glTranslated(-this->mCurrentAttributes.mX, -this->mCurrentAttributes.mY, -this->mCurrentAttributes.mZ);
750  }
751 
752  // set some attributes from mCurrentAttributes (stroke, stroke_width,
753  // stroke_dasharray)
754  if (this->mCurrentAttributes.mStroke != "none")
755  {
756  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
757  assert(pos != this->mColorMap.end());
758  const CLRGBAColor& c = pos->second;
759  glColor4ub(c.mR, c.mG, c.mB, c.mA);
760  size_t i, iMax = pCurve->getNumElements();
761  CLRenderPoint start, end, bp1, bp2;
762  CLPoint p1, p2, p3, p4;
763  const CLRenderPoint* pP = NULL;
764  const CLRenderCubicBezier* pCB = NULL;
765  // the first one has to be a point
766  const CLRenderPoint* pStart = pCurve->getCurveElement(0);
767  p1 = convert_to_absolute(pStart, pBB);
768  std::vector<simple_point> v;
769  // there are going to be at least iMax elements in the vector
770  v.reserve(iMax);
771  simple_point p;
772  p.mX = p1.getX();
773  p.mY = p1.getY();
774  p.mZ = p1.getZ();
775  v.push_back(p);
776  GLdouble* pData = NULL;
777 
778  for (i = 1; i < iMax; ++i)
779  {
780  pP = pCurve->getCurveElement(i);
781  pCB = dynamic_cast<const CLRenderCubicBezier*>(pP);
782 
783  if (pCB != NULL)
784  {
785  end = CLRenderPoint(pCB->x(), pCB->y(), pCB->z());
786  bp1 = CLRenderPoint(pCB->basePoint1_X(), pCB->basePoint1_Y(), pCB->basePoint1_Z());
787  bp2 = CLRenderPoint(pCB->basePoint2_X(), pCB->basePoint2_Y(), pCB->basePoint2_Z());
788  p2 = convert_to_absolute(&end, pBB);
789  p3 = convert_to_absolute(&bp1, pBB);
790  p4 = convert_to_absolute(&bp2, pBB);
791  pData = new GLdouble[3 * NUM_BEZIER_POINTS];
793  p3.getX(), p3.getY(), p3.getZ(),
794  p4.getX(), p4.getY(), p4.getZ(),
795  p2.getX(), p2.getY(), p2.getZ(),
796  NUM_BEZIER_POINTS, pData);
797  size_t j;
798  size_t index = 0;
799 
800  for (j = 0; j < NUM_BEZIER_POINTS; ++j)
801  {
802  p.mX = pData[index++];
803  p.mY = pData[index++];
804  p.mZ = pData[index++];
805  v.push_back(p);
806  }
807 
808  delete[] pData;
809  }
810  else
811  {
812  end = CLRenderPoint(pP->x(), pP->y(), pP->z());
813  p2 = convert_to_absolute(&end, pBB);
814  p.mX = p2.getX();
815  p.mY = p2.getY();
816  p.mZ = p2.getZ();
817  v.push_back(p);
818  }
819 
820  // this end is the next start
821  p1 = p2;
822  }
823 
824  iMax = v.size();
825 
826  if (iMax > 1)
827  {
828  pData = new GLdouble[3 * iMax];
829  size_t index = 0;
830  const simple_point* pSimple = NULL;
831 
832  for (i = 0; i < iMax; ++i)
833  {
834  pSimple = &v[i];
835  pData[index++] = pSimple->mX;
836  pData[index++] = pSimple->mY;
837  pData[index++] = pSimple->mZ;
838  }
839 
840  // draw the line
841  this->draw_line(iMax, pData);
842  delete[] pData;
843  }
844 
845  // map arrow heads
847  {
848  assert(pCurve->getNumElements() > 1);
849  const CLRenderPoint* pStart = pCurve->getCurveElement(0);
850  const CLPoint start = convert_to_absolute(pStart, pBB);
851  CLPoint v;
852  const CLRenderPoint* pEnd = pCurve->getCurveElement(1);
853  const CLRenderCubicBezier* pCB = dynamic_cast<const CLRenderCubicBezier*>(pEnd);
854 
855  if (!pCB)
856  {
857  const CLPoint end = convert_to_absolute(pEnd, pBB);
858  v = CLPoint(start.getX() - end.getX(), start.getY() - end.getY(), start.getZ() - end.getZ());
859  }
860  else
861  {
862  const CLRenderPoint* pEnd = new CLRenderPoint(pCB->basePoint1_X(), pCB->basePoint1_Y(), pCB->basePoint1_Z());
863  const CLPoint end = convert_to_absolute(pEnd, pBB);
864  delete pEnd;
865  v = CLPoint(start.getX() - end.getX(), start.getY() - end.getY(), start.getZ() - end.getZ());
866  }
867 
868  // we have to clear the arrow head attributes before we call the mapping
869  // function.
870  // If we don't do that and the line ending contains a curve, it will try
871  // to map itself to the curve again which is an endless loop.
872  this->save_current_attributes();
873  std::string headId = mCurrentAttributes.mStartHead;
874  this->map_arrow_head(start, v, headId);
875  // set the old attributes again
877  }
878 
879  if (!mCurrentAttributes.mEndHead.empty() && mCurrentAttributes.mEndHead != "none")
880  {
881  const CLRenderPoint* pEnd = pCurve->getCurveElement(pCurve->getNumElements() - 1);
882  const CLPoint end = convert_to_absolute(pEnd, pBB);
883 
884  const CLRenderCubicBezier* pCB = dynamic_cast<const CLRenderCubicBezier*>(pEnd);
885  CLPoint v;
886 
887  if (!pCB)
888  {
889  const CLRenderPoint* pStart = pCurve->getCurveElement(pCurve->getNumElements() - 2);
890  const CLPoint start = convert_to_absolute(pStart, pBB);
891  v = CLPoint(end.getX() - start.getX(), end.getY() - start.getY(), end.getZ() - start.getZ());
892  }
893  else
894  {
895  const CLRenderPoint* pStart = new CLRenderPoint(pCB->basePoint2_X(), pCB->basePoint2_Y(), pCB->basePoint2_Z());
896  const CLPoint start = convert_to_absolute(pStart, pBB);
897  delete pStart;
898  v = CLPoint(end.getX() - start.getX(), end.getY() - start.getY(), end.getZ() - start.getZ());
899  }
900 
901  // we have to clear the arrow head attributes before we call the mapping
902  // function.
903  // If we don't do that and the line ending contains a curve, it will try
904  // to map itself to the curve again which is an endless loop.
905  this->save_current_attributes();
906  std::string headId = mCurrentAttributes.mEndHead;
907  this->map_arrow_head(end, v, headId);
908  // set the old attributes again
910  }
911  }
912 
914  glMatrixMode(GL_MODELVIEW);
915  glPopMatrix();
916 }
917 
918 /**
919  * Converts a given CLRenderPoint which can have relative values into a
920  * layout CLPoint with only absolute values.
921  * Even the absolute values of the point which are relative to the bounding box
922  * are translated into absolute coordinates.
923  */
925 {
926  const CLRelAbsVector& x = pRenderPoint->x();
927  const CLRelAbsVector& y = pRenderPoint->y();
928  const CLRelAbsVector& z = pRenderPoint->z();
929  const CLPoint* pPosition = &pBB->getPosition();
930  const CLDimensions* pDimensions = &pBB->getDimensions();
931  return CLPoint((x.getAbsoluteValue() + pDimensions->getWidth() * x.getRelativeValue() / 100.0) + pPosition->getX(),
932  (y.getAbsoluteValue() + pDimensions->getHeight() * y.getRelativeValue() / 100.0) + pPosition->getY(),
933  (z.getAbsoluteValue() + pDimensions->getDepth() * z.getRelativeValue() / 100.0) + pPosition->getZ());
934 }
935 
936 /**
937  * Method to draw a given layout with a given render resolver.
938  */
940 {
941  // first we need to clear the screen
942  // with the background color
943  glDisable(GL_POLYGON_SMOOTH);
944 
945  if (this->mGLFunctionsInitialized == false)
946  {
948  }
949 
950  glFogi(GL_FOG_MODE, GL_EXP);
951 
952  if (this->mHighlight == true)
953  {
954  glFogfv(GL_FOG_COLOR, this->mHighlightColor);
955  }
956  else
957  {
958  glFogfv(GL_FOG_COLOR, this->mFogColor);
959  }
960 
961  glFogf(GL_FOG_DENSITY, this->mFogDensity);
962  glHint(GL_FOG_HINT, GL_FASTEST);
964  glEnable(GL_FOG);
965  GLfloat highlight = (GLfloat)this->mHighlight;
966  GLfloat notHighlight = (GLfloat)(!this->mHighlight);
967 
968  if (this->mpResolver)
969  {
970  //std::cout << "Drawing layout." << std::endl;
971  const CLColorDefinition* pBackgroundColor = this->mpResolver->getBackgroundColor();
972  GLfloat red = (GLfloat)(pBackgroundColor->getRed() / 255.0);
973  GLfloat green = (GLfloat)(pBackgroundColor->getGreen() / 255.0);
974  GLfloat blue = (GLfloat)(pBackgroundColor->getBlue() / 255.0);
975  GLfloat alpha = (GLfloat)(pBackgroundColor->getAlpha() / 255.0);
976 
977  if (this->mHighlight == false)
978  {
979  // we have to generate fog on the background ourselfes
980  red = (GLfloat)((red + this->mFogColor[0]) * this->mFogDensity);
981  green = (GLfloat)((green + this->mFogColor[1]) * this->mFogDensity);
982  blue = (GLfloat)((blue + this->mFogColor[2]) * this->mFogDensity);
983  alpha = (GLfloat)((alpha + this->mFogColor[3]) * this->mFogDensity);
984  }
985 
986  glClearColor((GLclampf)red,
987  (GLclampf)green,
988  (GLclampf)blue,
989  (GLclampf)alpha);
990  glClear(GL_COLOR_BUFFER_BIT);
991  glPushMatrix();
992  this->mCurrentAttributes.mX = 0.0;
993  this->mCurrentAttributes.mY = 0.0;
994  this->mCurrentAttributes.mZ = 0.0;
997  const CLReactionGlyph* pRG = NULL;
998  const CLGeneralGlyph* pGG = NULL;
999  const CLReferenceGlyph* pRefG = NULL;
1000  const CLMetabReferenceGlyph* pSRG = NULL;
1001  const CLTextGlyph* pTG = NULL;
1002  std::vector<const CLGraphicalObject*>::iterator it = this->mDrawables.begin(), endit = this->mDrawables.end();
1003  const CLGraphicalObject* pGO = NULL;
1004 // this is needed to highlight or fog certain elements in the diagram
1005  const CCopasiObject* pModelObject = NULL;
1006  std::set<const CLGraphicalObject*>::const_iterator end = this->mHighlightedObjects.end();
1007 
1008  while (it != endit)
1009  {
1010  pGO = *it;
1011  pRG = dynamic_cast<const CLReactionGlyph*>(pGO);
1012  pGG = dynamic_cast<const CLGeneralGlyph*>(pGO);
1013  pSRG = dynamic_cast<const CLMetabReferenceGlyph*>(pGO);
1014  pRefG = dynamic_cast<const CLReferenceGlyph*>(pGO);
1015  pTG = dynamic_cast<const CLTextGlyph*>(pGO);
1016  std::map<const CLGraphicalObject*, const CLStyle*>::const_iterator styleIt = this->mStyleMap.find(pGO);
1017 
1018  if (styleIt == this->mStyleMap.end() || styleIt->second == NULL)
1019  {
1020  ++it;
1021  continue;
1022  }
1023 
1024 // this is needed to highlight or fog certain elements in the diagram
1025  const CLGraphicalObject* pGO2 = NULL;
1026 
1027  if (pSRG == NULL)
1028  {
1029  pModelObject = (pGO)->getModelObject();
1030  pGO2 = pGO;
1031  }
1032  else if (pRefG == NULL)
1033  {
1034  pModelObject = (pGO)->getModelObject();
1035  pGO2 = pGO;
1036  }
1037  else
1038  {
1039  // if we have a species reference glyph, we check if the parent reaction glyph is highlighted and if
1040  // it is we also highlight the species reference glyph
1041  assert((*it)->getObjectParent() != NULL && (*it)->getObjectParent()->getObjectParent() != NULL);
1042  assert(dynamic_cast<const CLGraphicalObject*>((*it)->getObjectParent()->getObjectParent()) != NULL);
1043 
1044  if (pGO->getObjectParent() != NULL &&
1045  pGO->getObjectParent()->getObjectParent() != NULL
1046  )
1047  {
1048  pGO2 = dynamic_cast<const CLGraphicalObject*>(pGO->getObjectParent()->getObjectParent());
1049 
1050  if (pGO2 != NULL)
1051  {
1052  pModelObject = pGO2->getModelObject();
1053  }
1054  }
1055  }
1056 
1057  if (this->mpGlFogCoordfEXT != NULL)
1058  {
1059  if (pModelObject != NULL && this->mHighlightedObjects.find(pGO2) != end)
1060  {
1061  (*(this->mpGlFogCoordfEXT))(highlight);
1062  }
1063  else
1064  {
1065  (*(this->mpGlFogCoordfEXT))(notHighlight);
1066  }
1067  }
1068 
1069  if ((pSRG != NULL && pSRG->getCurve().getNumCurveSegments() != 0))
1070  {
1071  // draw the layout curve
1072  // we need to set the state of the OpenGL state machine
1073  // save the curent state
1074  this->save_current_attributes();
1076 
1077  // only draw the line if the stroke width is a positive value
1078  // greater zero and if there is a stroke color defined
1080  {
1081  bool drawBasepoints = false;
1082 
1083  if (this->mSelection.size() == 1 && (*this->mSelection.begin()) == pSRG)
1084  {
1085  drawBasepoints = true;
1086  }
1087 
1088  this->draw_curve(&pSRG->getCurve(), drawBasepoints);
1089  }
1090 
1092 
1093  // if the curve is the only selected item, we draw the base points,
1094  // otherwise we only draw the selection frame
1095  if (this->mSelection.find(const_cast<CLGraphicalObject*>(pGO)) != this->mSelection.end())
1096  {
1097  CLBoundingBox* pBB = getCurveBoundingBox(&pSRG->getCurve());
1098  this->drawSelectionBox(pBB->getPosition().getX(), pBB->getPosition().getY(),
1099  pBB->getDimensions().getWidth(), pBB->getDimensions().getHeight(), this->mSelection.size() == 1);
1100  delete pBB;
1101  }
1102  }
1103  else if ((pRefG != NULL && pRefG->getCurve().getNumCurveSegments() != 0))
1104  {
1105  // draw the layout curve
1106  // we need to set the state of the OpenGL state machine
1107  // save the curent state
1108  this->save_current_attributes();
1110 
1111  // only draw the line if the stroke width is a positive value
1112  // greater zero and if there is a stroke color defined
1114  {
1115  bool drawBasepoints = false;
1116 
1117  if (this->mSelection.size() == 1 && (*this->mSelection.begin()) == pRefG)
1118  {
1119  drawBasepoints = true;
1120  }
1121 
1122  this->draw_curve(&pRefG->getCurve(), drawBasepoints);
1123  }
1124 
1126 
1127  // if the curve is the only selected item, we draw the base points,
1128  // otherwise we only draw the selection frame
1129  if (this->mSelection.find(const_cast<CLGraphicalObject*>(pGO)) != this->mSelection.end())
1130  {
1131  CLBoundingBox* pBB = getCurveBoundingBox(&pRefG->getCurve());
1132  this->drawSelectionBox(pBB->getPosition().getX(), pBB->getPosition().getY(),
1133  pBB->getDimensions().getWidth(), pBB->getDimensions().getHeight(), this->mSelection.size() == 1);
1134  delete pBB;
1135  }
1136  }
1137  else if (pRG != NULL && pRG->getCurve().getNumCurveSegments() != 0)
1138  {
1139  // draw the layout curve
1140  // we need to set the state of the OpenGL state machine
1141  // save the curent state
1142  this->save_current_attributes();
1144 
1145  // only do something if the stroke width is a positive value
1146  // greater 0 and if there is a stroke color defined
1148  {
1149  // set the state
1150  bool drawBasepoints = false;
1151 
1152  if (this->mSelection.size() == 1 && (*this->mSelection.begin()) == pRG)
1153  {
1154  drawBasepoints = true;
1155  }
1156 
1157  this->draw_curve(&pRG->getCurve(), drawBasepoints);
1158  // reset the original state
1159  }
1160 
1162 
1163  // if the curve is the only selected item, we draw the base points,
1164  // otherwise we only draw the selection frame
1165  if (this->mSelection.find(const_cast<CLGraphicalObject*>(pGO)) != this->mSelection.end())
1166  {
1167  CLBoundingBox* pBB = getCurveBoundingBox(&pRG->getCurve());
1168  this->drawSelectionBox(pBB->getPosition().getX(), pBB->getPosition().getY(),
1169  pBB->getDimensions().getWidth(), pBB->getDimensions().getHeight(), this->mSelection.size() == 1);
1170  delete pBB;
1171  }
1172  }
1173  else if (pGG != NULL && pGG->getCurve().getNumCurveSegments() != 0)
1174  {
1175  // draw the layout curve
1176  // we need to set the state of the OpenGL state machine
1177  // save the curent state
1178  this->save_current_attributes();
1180 
1181  // only do something if the stroke width is a positive value
1182  // greater 0 and if there is a stroke color defined
1184  {
1185  // set the state
1186  bool drawBasepoints = false;
1187 
1188  if (this->mSelection.size() == 1 && (*this->mSelection.begin()) == pGG)
1189  {
1190  drawBasepoints = true;
1191  }
1192 
1193  this->draw_curve(&pGG->getCurve(), drawBasepoints);
1194  // reset the original state
1195  }
1196 
1198 
1199  // if the curve is the only selected item, we draw the base points,
1200  // otherwise we only draw the selection frame
1201  if (this->mSelection.find(const_cast<CLGraphicalObject*>(pGO)) != this->mSelection.end())
1202  {
1203  CLBoundingBox* pBB = getCurveBoundingBox(&pGG->getCurve());
1204  this->drawSelectionBox(pBB->getPosition().getX(), pBB->getPosition().getY(),
1205  pBB->getDimensions().getWidth(), pBB->getDimensions().getHeight(), this->mSelection.size() == 1);
1206  delete pBB;
1207  }
1208  }
1209  else if (pTG != NULL)
1210  {
1211  //std::cout << "Drawing CLText*Glyph: " << pTG->getId() << std::endl;
1212  std::map<const CLTextGlyph*, const CLTextTextureSpec*>::const_iterator pos = this->mTextGlyphMap.find(pTG);
1213  assert(pos != this->mTextGlyphMap.end());
1214 
1215  if (pos->second != NULL && pos->second->mTextureName != 0)
1216  {
1217  //std::cout << "Texture for text glyph found." << std::endl;
1218  // in order to position text glyphs corectly, we have to move them up by their mAscent
1219  CLBoundingBox bb = pTG->getBoundingBox();
1220  CLPoint* pP = &bb.getPosition();
1221  bb.setPosition(*pP);
1222  this->draw_text(styleIt->second, &bb, pos->second);
1223 
1224  // draw the selection frame
1225  // if it is the only selected element, we also draw the resize handles
1226  if (this->mSelection.find(const_cast<CLGraphicalObject*>(pGO)) != this->mSelection.end())
1227  {
1228  // we need to adjust the bounding box the same way we adjust the text glyphs
1229  this->drawSelectionBox(bb.getPosition().getX(), bb.getPosition().getY() + pos->second->mAscent,
1230  bb.getDimensions().getWidth(), bb.getDimensions().getHeight(), this->mSelection.size() == 1);
1231  }
1232  }
1233  }
1234  else
1235  {
1236  const CLBoundingBox* pBB = &pGO->getBoundingBox();
1237  this->draw_object(styleIt->second, pBB);
1238 
1239  // draw the selection frame
1240  // if it is the only selected element, we also draw the resize handles
1241  if (this->mSelection.find(const_cast<CLGraphicalObject*>(pGO)) != this->mSelection.end())
1242  {
1243  this->drawSelectionBox(pBB->getPosition().getX(), pBB->getPosition().getY(),
1244  pBB->getDimensions().getWidth(), pBB->getDimensions().getHeight(), this->mSelection.size() == 1);
1245  }
1246  }
1247 
1248  ++it;
1249  }
1250 
1251  // flush the GL queue
1252  glPopMatrix();
1253  // draw the selection box
1254  this->draw_selection_box();
1255  glFlush();
1256  //std::cout << "Drawing finished." << std::endl << std::endl;
1257  }
1258 
1259  glDisable(GL_FOG);
1260 }
1261 
1262 /**
1263  * Method to draw a text object specified by it's bounding box
1264  * and the style with which it should be drawn as well as the actual
1265  * string.
1266  */
1267 void CLLayoutRenderer::draw_text(const CLStyle* pStyle, const CLBoundingBox* pBB, const CLTextTextureSpec* pTexture)
1268 {
1269  // set the attributes
1270  this->save_current_attributes();
1271  // for text elements, only the 1d attributes and the special text
1272  // attributes are relevant
1275 
1276  // we only draw the text if there is a stroke color to draw it with
1277  if (!mCurrentAttributes.mStroke.empty() && mCurrentAttributes.mStroke != "none")
1278  {
1279  // with the new interpretation of the text alignment attributes,
1280  // we have to specify different offset attributes
1281  // if the horizontal laignment is middle, we specify the middle of the box
1282  // as x offset and if the alignment is end, we specify the end as the x offset
1283  // likewise for the vertical alignment and the y offset
1284  double xOffset = 0.0;
1285  double yOffset = 0.0;
1286 
1288  {
1289  xOffset = pBB->getDimensions().getWidth() * 0.5;;
1290  }
1292  {
1293  xOffset = pBB->getDimensions().getWidth();
1294  }
1295 
1297  {
1298  yOffset = pBB->getDimensions().getHeight() * 0.5;
1299  }
1301  {
1302  yOffset = pBB->getDimensions().getHeight();
1303  }
1304 
1305  this->draw_text(pTexture, xOffset, yOffset, 0.0, pBB);
1306  }
1307 
1308  //restore the attributes
1310 }
1311 
1312 /**
1313  * Method to resolve the text that belongs to a text glyph.
1314  */
1315 const std::string CLLayoutRenderer::resolve_text(const CLTextGlyph* pTextGlyph)
1316 {
1317  // CLTextGlyph already knows how to resolve the text
1318  std::string text = pTextGlyph->getText();
1319 
1320  // check if the empty string comes from an explicit text or if it
1321  // is the result of an invalid model reference
1322  if (text.empty() && !pTextGlyph->isTextSet())
1323  {
1324  // maybe unset is not the best text to
1325  // notify the user of a problem, but right now I have no
1326  // better idea
1327  text = "unset";
1328  }
1329 
1330  return text;
1331 }
1332 
1333 /**
1334  * Method to draw an arbitrary object specified by it's bounding box
1335  * and the style with which it should be drawn.
1336  */
1337 void CLLayoutRenderer::draw_object(const CLStyle* pStyle, const CLBoundingBox* pBB)
1338 {
1339  this->draw_group(pStyle->getGroup(), pBB);
1340 }
1341 
1342 /**
1343  * Method to draw an arbitrary object specified by it's bounding box
1344  * and the style with which it should be drawn.
1345  */
1346 void CLLayoutRenderer::draw_group(const CLGroup* pGroup, const CLBoundingBox* pBB)
1347 {
1348  // set the group attributes
1349  this->save_current_attributes();
1356  glMatrixMode(GL_MODELVIEW);
1357  glPushMatrix();
1358 
1359  // apply the current transformation
1360  if (memcmp(mCurrentAttributes.mpTransform, Transformation::getIdentityMatrix(), 12 * sizeof(double)))
1361  {
1362  // move back to the current offset
1363  glTranslated(this->mCurrentAttributes.mX, this->mCurrentAttributes.mY, this->mCurrentAttributes.mZ);
1364  GLdouble* matrix = new GLdouble[16];
1366  glMultMatrixd(matrix);
1367  delete[] matrix;
1368  // move to 0.0,0.0,0.0
1369  glTranslated(-this->mCurrentAttributes.mX, -this->mCurrentAttributes.mY, -this->mCurrentAttributes.mZ);
1370  }
1371 
1372  // draw each element
1373  const CCopasiObject* pObject = NULL;
1374  size_t i, iMax = pGroup->getNumElements();
1375 
1376  for (i = 0; i < iMax; ++i)
1377  {
1378  pObject = pGroup->getElement(i);
1379  assert(pObject != NULL);
1380  // find out what kind of element it is
1381  const CLEllipse* pEllipse = dynamic_cast<const CLEllipse*>(pObject);
1382 
1383  if (pEllipse != NULL)
1384  {
1385  CLLayoutRenderer::draw_ellipse(pEllipse, pBB);
1386  }
1387  else
1388  {
1389  const CLGroup* pRenderGroup = dynamic_cast<const CLGroup*>(pObject);
1390 
1391  if (pRenderGroup != NULL)
1392  {
1393  CLLayoutRenderer::draw_group(pRenderGroup, pBB);
1394  }
1395  else
1396  {
1397  const CLImage* pImage = dynamic_cast<const CLImage*>(pObject);
1398 
1399  if (pImage != NULL)
1400  {
1401  CLLayoutRenderer::draw_image(pImage, pBB);
1402  }
1403  else
1404  {
1405  const CLPolygon* pPolygon = dynamic_cast<const CLPolygon*>(pObject);
1406 
1407  if (pPolygon != NULL)
1408  {
1409  CLLayoutRenderer::draw_polygon(pPolygon, pBB);
1410  }
1411  else
1412  {
1413  const CLRectangle* pRectangle = dynamic_cast<const CLRectangle*>(pObject);
1414 
1415  if (pRectangle != NULL)
1416  {
1417  CLLayoutRenderer::draw_rectangle(pRectangle, pBB);
1418  }
1419  else
1420  {
1421  const CLRenderCurve* pCurve = dynamic_cast<const CLRenderCurve*>(pObject);
1422 
1423  if (pCurve != NULL)
1424  {
1425  CLLayoutRenderer::draw_curve(pCurve, pBB);
1426  }
1427  else
1428  {
1429  const CLText* pText = dynamic_cast<const CLText*>(pObject);
1430 
1431  if (pText != NULL)
1432  {
1433  CLLayoutRenderer::draw_text(pText, pBB);
1434  }
1435  else
1436  {
1437  throw pObject;
1438  }
1439  }
1440  }
1441  }
1442  }
1443  }
1444  }
1445  }
1446 
1447  // restore the attributes
1449  glMatrixMode(GL_MODELVIEW);
1450  glPopMatrix();
1451 }
1452 
1453 /**
1454  * Resize method that is called whenever the GL window is resized.
1455  */
1456 void CLLayoutRenderer::resize(GLsizei w, GLsizei h)
1457 {
1458  // setup viewport, projection etc.:
1459  this->mW = w;
1460  this->mH = h;
1461  glViewport(0, 0, (GLint)w, (GLint)h);
1462  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
1463  glLoadIdentity(); // Reset The Projection Matrix
1464  this->analyse_render_information(mX, mY, mX + mW / this->mZoomFactor, mY + mH / this->mZoomFactor);
1465  gluOrtho2D((GLdouble)mX,
1466  (GLdouble)(mX + w / this->mZoomFactor * this->mAspect),
1467  (GLdouble)(mY + h / this->mZoomFactor),
1468  (GLdouble)mY); // y: 0.0 is bottom left instead of top left as in SBML
1469  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
1470 }
1471 
1472 /**
1473  * Method that converts a color value or a color id into a color array for
1474  * OpenGL.
1475  */
1476 void CLLayoutRenderer::resolve_color(const std::string& color, GLubyte array[4])
1477 {
1478  if (!color.empty() && color != "none")
1479  {
1480  if (color[0] == '#')
1481  {
1482  CLColorDefinition cd;
1483  cd.setColorValue(color);
1484  array[0] = (GLubyte)cd.getRed();
1485  array[1] = (GLubyte)cd.getGreen();
1486  array[2] = (GLubyte)cd.getBlue();
1487  array[3] = (GLubyte)cd.getAlpha();
1488  }
1489  else
1490  {
1491  const CLColorDefinition* pColor = this->mpResolver->getColorDefinition(color);
1492 
1493  if (!pColor)
1494  {
1495  // TODO actually this is an error and should lead to an
1496  // exception
1497  throw 0;
1498  }
1499  else
1500  {
1501  array[0] = (GLubyte)pColor->getRed();
1502  array[1] = (GLubyte)pColor->getGreen();
1503  array[2] = (GLubyte)pColor->getBlue();
1504  array[3] = (GLubyte)pColor->getAlpha();
1505  }
1506  }
1507  }
1508  else
1509  {
1510  array[0] = 0;
1511  array[1] = 0;
1512  array[2] = 0;
1513  array[3] = 0;
1514  }
1515 }
1516 
1517 /**
1518  * Method to draw a render text object.
1519  */
1520 void CLLayoutRenderer::draw_text(const CLText* pText, const CLBoundingBox* pBB)
1521 {
1522  if (pText->isSetText())
1523  {
1524  // set the attributes
1525  this->save_current_attributes();
1526  // for text elements, only the 1d attributes and the special text
1527  // attributes are relevant
1530  // draw the string
1531  double x = pText->getX().getAbsoluteValue() + pText->getX().getRelativeValue() / 100.0 * pBB->getDimensions().getWidth();
1532  double y = pText->getY().getAbsoluteValue() + pText->getY().getRelativeValue() / 100.0 * pBB->getDimensions().getHeight();
1533  double z = pText->getZ().getAbsoluteValue() + pText->getZ().getRelativeValue() / 100.0 * pBB->getDimensions().getDepth();
1534  std::map<const CLText*, const CLTextTextureSpec*>::const_iterator pos = this->mTextMap.find(pText);
1535  assert(pos != this->mTextMap.end());
1536 
1537  if (pos->second != NULL && pos->second->mTextureName != 0)
1538  {
1539  //std::cout << "Drawing text \"" << pText->getText() << "\"." << std::endl;
1540  CLLayoutRenderer::draw_text(pos->second, x, y, z, pBB);
1541  }
1542 
1543  //restore the attributes
1545  }
1546 }
1547 
1548 /**
1549  * Method to draw a string at the given position within the given bounding box.
1550  */
1551 void CLLayoutRenderer::draw_text(const CLTextTextureSpec* pTexture, double x, double y, double z, const CLBoundingBox* pBB)
1552 {
1553  //std::cout << "Drawing text with texture at " << pTexture << std::endl;
1554  if (pTexture != NULL && pBB != NULL)
1555  {
1556  // create a texture for the text.
1557  // map the origin of the texture to x,y,z
1558  // map the texture point at textwidth, textheight to x+textwidth,
1559  // y+textheight
1560  // This might lead to text that goes beyone the dimensions of the bounding
1561  // box, but since the user has asked for a text of this size, we just draw
1562  // it
1563  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
1564  assert(pos != this->mColorMap.end());
1565  const CLRGBAColor& c = pos->second;
1566  glColor4ub(c.mR, c.mG, c.mB, c.mA);
1567  double xOffset = x + pBB->getPosition().getX();
1568  double yOffset = y + pBB->getPosition().getY();
1569  double zOffset = z + pBB->getPosition().getZ();
1570 
1571  // position the text according to how the anchor is set
1573  {
1574  // the new interpretation is that the horizontal center of the text is at xOffset,yOffset
1575  xOffset -= pTexture->mTextWidth / (2.0 * this->mZoomFactor);
1576  }
1578  {
1579  // the new interpretation is that xOffset specifies the horizontal end of the text, so
1580  // the start has to be placed at xOffset-pTexture->mTextWidth / this->mZoomFactor
1581  xOffset -= pTexture->mTextWidth / this->mZoomFactor;
1582  }
1583 
1584  // do vertical positioning
1586  {
1587  // the text is vertically centered in the box
1588  yOffset -= pTexture->mTextHeight / (2.0 * this->mZoomFactor);
1589  }
1591  {
1592  // the lower edge of the text is located at the top edge of the box
1593  // since heigher y values are downward, this alligns the text at
1594  // the lower end of the box
1595  yOffset -= pTexture->mTextHeight / this->mZoomFactor;
1596  }
1597 
1598  // the yOffset has to consider the mAscent of the text because the
1599  // placement of the text should be relative to the baseline
1600  //std::cout << "current bounding box position: " << pBB->getPosition().getX() << "," << pBB->getPosition().getY() << std::endl;
1601  //std::cout << "texture size: " << pTexture->textureWidth << "x" << pTexture->textureHeight << std::endl;
1602  //std::cout << "text size: " << pTexture->textWidth << "x" << pTexture->textHeight << std::endl;
1603  //std::cout << "text mAscent: " << pTexture->mAscent << std::endl;
1604  //std::cout << "y offset at: " << y << std::endl;
1605  //std::cout << "placing baseline at: " << yOffset << std::endl;
1606  //std::cout << "the upper side of the textured box will be located at: " << yOffset << std::endl;
1607 
1608  //
1609  // we draw a rectangle in the current stroke color. At places where the texture is black, the underlying color should be seen.
1610  // load the texture
1611  // enable 2D texturing
1612  glEnable(GL_TEXTURE_2D);
1613  glBindTexture(GL_TEXTURE_2D, pTexture->mTextureName);
1614  glMatrixMode(GL_MODELVIEW);
1615  glPushMatrix();
1616  glTranslated(xOffset, yOffset, zOffset);
1617 
1618  // apply the current transformation
1619  if (memcmp(mCurrentAttributes.mpTransform, Transformation::getIdentityMatrix(), 12 * sizeof(double)))
1620  {
1621  // move back to the current offset
1622  glTranslated(this->mCurrentAttributes.mX, this->mCurrentAttributes.mY, this->mCurrentAttributes.mZ);
1623  GLdouble* matrix = new GLdouble[16];
1625  glMultMatrixd(matrix);
1626  delete[] matrix;
1627  // move to 0.0,0.0,0.0
1628  glTranslated(-this->mCurrentAttributes.mX, -this->mCurrentAttributes.mY, -this->mCurrentAttributes.mZ);
1629  }
1630 
1631  //std::cout << "zoom factor: " << this->mZoomFactor << std::endl;
1632  //std::cout << "Drawing texture " << pTexture->mTextureName << " with:" << std::endl;
1633  //std::cout << "text height: " << pTexture->mTextHeight << " text width: " << pTexture->mTextWidth << std::endl;
1634  //std::cout << "texture height: " << pTexture->mTextureHeight << " texture width: " << pTexture->mTextureWidth << std::endl;
1635  //std::cout << "texture scale: " << pTexture->mScale << std::endl;
1636  double widthRatio = pTexture->mTextWidth /** pTexture->mScale*/ / pTexture->mTextureWidth;
1637  //std::cout << "width ratio: " << widthRatio << std::endl;
1638  double heightRatio = pTexture->mTextHeight /** pTexture->mScale*/ / pTexture->mTextureHeight;
1639  //std::cout << "height ratio: " << heightRatio << std::endl;
1640  glBegin(GL_POLYGON);
1641  glTexCoord2f(0.0, 1.0);
1642  glVertex3f(0.0, 0.0, 0.0);
1643  glTexCoord2d(0.0, 1.0 - heightRatio);
1644  glVertex3d(0.0, pTexture->mTextHeight / pTexture->mScale, 0.0);
1645  glTexCoord2d(widthRatio, 1.0 - heightRatio);
1646  glVertex3d(pTexture->mTextWidth / pTexture->mScale, pTexture->mTextHeight / pTexture->mScale, 0.0);
1647  glTexCoord2d(widthRatio, 1.0);
1648  glVertex3d(pTexture->mTextWidth / pTexture->mScale, 0.0, 0.0);
1649  glEnd();
1650  glPopMatrix();
1651  // disable the 2D texture again
1652  glDisable(GL_TEXTURE_2D);
1653  }
1654 }
1655 
1656 /**
1657  * Method to draw a render ellipse object.
1658  */
1659 void CLLayoutRenderer::draw_ellipse(const CLEllipse* pEllipse, const CLBoundingBox* pBB)
1660 {
1661  // store and change the attributes
1662  this->save_current_attributes();
1664  // draw the ellipse
1665  // first we calculate the data points for the ellipse
1666  double x = pBB->getPosition().getX() + pEllipse->getCX().getAbsoluteValue() + pEllipse->getCX().getRelativeValue() / 100.0 * pBB->getDimensions().getWidth();
1667  double y = pBB->getPosition().getY() + pEllipse->getCY().getAbsoluteValue() + pEllipse->getCY().getRelativeValue() / 100.0 * pBB->getDimensions().getHeight();
1668  double z = pBB->getPosition().getZ() + pEllipse->getCZ().getAbsoluteValue() + pEllipse->getCZ().getRelativeValue() / 100.0 * pBB->getDimensions().getDepth();
1669  double rx = pEllipse->getRX().getAbsoluteValue() + pEllipse->getRX().getRelativeValue() / 100.0 * pBB->getDimensions().getWidth();
1670  double ry = pEllipse->getRY().getAbsoluteValue() + pEllipse->getRY().getRelativeValue() / 100.0 * pBB->getDimensions().getHeight();
1671  // we add an additional datapoint to close the loop. This way we don't need
1672  // the draw_loop method, but all is handled in draw_line
1673  // this also makes line stippling easier for OpenGL < 2.0
1674  GLdouble* pData = new GLdouble[3 * (NUM_CIRCLE_SEGMENTS + 1)];
1675  unsigned int i;
1676  double delta_phi = 2 * M_PI / NUM_CIRCLE_SEGMENTS;
1677  double phi = 0.0;
1678  size_t index = 0;
1679 
1680  for (i = 0; i < NUM_CIRCLE_SEGMENTS; ++i)
1681  {
1682  // TODO it would be enough to calculate only one quadrant
1683  phi = i * delta_phi;
1684  pData[index++] = rx * rx * cos(phi) / sqrt(rx * rx * pow(cos(phi), 2) + ry * ry * pow(sin(phi), 2));
1685  pData[index++] = ry * ry * sin(phi) / sqrt(rx * rx * pow(cos(phi), 2) + ry * ry * pow(sin(phi), 2));
1686  pData[index++] = 0.0;
1687  }
1688 
1689  // close the loop
1690  pData[index++] = pData[0];
1691  pData[index++] = pData[1];
1692  pData[index] = pData[2];
1693  this->draw_datapoints(pData, NUM_CIRCLE_SEGMENTS + 1, pBB, false, (GLfloat)x, (GLfloat)y, (GLfloat)z);
1694  delete[] pData;
1695  // restore the attributes
1697 }
1698 
1699 /**
1700  * Method to draw a render image object.
1701  */
1702 void CLLayoutRenderer::draw_image(const CLImage* pImage, const CLBoundingBox* pBB)
1703 {
1704  // the ransformation attributes are the only ones that influence the
1705  // drawing of an image
1706  this->save_current_attributes();
1708  // draw the actual image
1709  const CLTextureSpec* pTexture = NULL;
1710 
1711  if (pImage->isSetImageReference())
1712  {
1713  std::string reference = pImage->getImageReference();
1714  std::map<std::string, const CLTextureSpec*>::const_iterator pos = this->mImageMap.find(reference);
1715 
1716  if (pos == this->mImageMap.end())
1717  {
1718  // we need to create the texture
1719  if (this->mpImageTexturizer != NULL)
1720  {
1721  pTexture = (*this->mpImageTexturizer)(reference, this->mBaseDir);
1722  this->mImageMap[reference] = pTexture;
1723  pos = this->mImageMap.find(reference);
1724  assert(pos != this->mImageMap.end());
1725  }
1726  }
1727 
1728  assert(pos != this->mImageMap.end());
1729  pTexture = pos->second;
1730  //assert(pTexture);
1731  }
1732 
1733  if (pTexture)
1734  {
1735  // draw the texture in the correct place
1736  // load the texture
1737  assert(pTexture->mTextureName != 0);
1738  glBindTexture(GL_TEXTURE_2D, pTexture->mTextureName);
1739  const CLDimensions* d = &pBB->getDimensions();
1740  double width = pImage->getWidth().getAbsoluteValue() + pImage->getWidth().getRelativeValue() / 100.0 * d->getWidth();
1741  double height = pImage->getHeight().getAbsoluteValue() + pImage->getHeight().getRelativeValue() / 100.0 * d->getHeight();
1742  CLRenderPoint p(pImage->getX(), pImage->getY());
1744  // apply the current transformation
1745  glMatrixMode(GL_MODELVIEW);
1746  glPushMatrix();
1747 
1748  if (memcmp(mCurrentAttributes.mpTransform, Transformation::getIdentityMatrix(), 12 * sizeof(double)))
1749  {
1750  // move back to the current offset
1751  glTranslated(this->mCurrentAttributes.mX, this->mCurrentAttributes.mY, this->mCurrentAttributes.mZ);
1752  GLdouble* matrix = new GLdouble[16];
1754  glMultMatrixd(matrix);
1755  delete[] matrix;
1756  // move to 0.0,0.0,0.0
1757  glTranslated(-this->mCurrentAttributes.mX, -this->mCurrentAttributes.mY, -this->mCurrentAttributes.mZ);
1758  }
1759 
1760  // enable 2D texturing
1761  glEnable(GL_TEXTURE_2D);
1762  glMatrixMode(GL_MODELVIEW);
1763  glPushMatrix();
1764  glTranslated(point.getX(), point.getY(), point.getZ());
1765  glBegin(GL_POLYGON);
1766  glTexCoord2d(0.0, 0.0);
1767  glVertex3d(0.0, 0.0, 0.0);
1768  glTexCoord2d(0.0, 1.0);
1769  glVertex3d(0.0, height, 0.0);
1770  glTexCoord2d(1.0, 1.0);
1771  glVertex3d(width, height, 0.0);
1772  glTexCoord2d(1.0, 0.0);
1773  glVertex3d(width, 0.0, 0.0);
1774  glEnd();
1775  glPopMatrix();
1776  // disable the 2D texture again
1777  glDisable(GL_TEXTURE_2D);
1778  }
1779  else
1780  {
1781  // TODO at least create some kind of error message
1782  }
1783 
1784  // restore the attributes
1786  glMatrixMode(GL_MODELVIEW);
1787  glPopMatrix();
1788 }
1789 
1790 /**
1791  * Method to draw a render polygon object.
1792  */
1793 void CLLayoutRenderer::draw_polygon(const CLPolygon* pPolygon, const CLBoundingBox* pBB)
1794 {
1795  /*
1796  * Old code to draw the polygon when it consists of only straight lines.
1797  size_t numPoints=pPolygon->getNumElements()+1;
1798  if(numPoints>1)
1799  {
1800  // store and change the attributes
1801  this->save_current_attributes();
1802  CLLayoutRenderer::extract_2d_attributes(pPolygon,&mCurrentAttributes);
1803  // create the data points
1804  GLdouble* pData=new GLdouble[3*numPoints];
1805  size_t i,iMax=numPoints-1;
1806  CLPoint p;
1807  const CLRenderPoint* pP;
1808  size_t index=0;
1809  for(i=0;i<iMax;++i)
1810  {
1811  pP=pPolygon->getElement(i);
1812  assert(pP);
1813  p=CLLayoutRenderer::convert_to_absolute(pP,pBB);
1814  pData[index++]=p.getX();
1815  pData[index++]=p.getY();
1816  pData[index++]=p.getZ();
1817  }
1818  pData[index++]=pData[0];
1819  pData[index++]=pData[1];
1820  pData[index]=pData[2];
1821  */
1822 
1823  // the first one has to be a point
1824  // store and change the attributes
1825  if (pPolygon->getNumElements() > 1)
1826  {
1827  this->save_current_attributes();
1829  const CLRenderPoint* pStart = pPolygon->getElement(0);
1830  CLRenderPoint end, bp1, bp2;
1831  CLPoint p1 = convert_to_absolute(pStart, pBB);
1832  CLPoint p2, p3, p4;
1833  std::vector<simple_point> v;
1834  // there are going to be at least iMax elements in the vector
1835  size_t i, iMax = pPolygon->getNumElements();
1836  v.reserve(iMax);
1837  simple_point p;
1838  p.mX = p1.getX();
1839  p.mY = p1.getY();
1840  p.mZ = p1.getZ();
1841  v.push_back(p);
1842  GLdouble* pData = NULL;
1843  const CLRenderPoint* pP;
1844  const CLRenderCubicBezier* pCB;
1845 
1846  for (i = 1; i < iMax; ++i)
1847  {
1848  pP = pPolygon->getElement(i);
1849  pCB = dynamic_cast<const CLRenderCubicBezier*>(pP);
1850 
1851  if (pCB != NULL)
1852  {
1853  end = CLRenderPoint(pCB->x(), pCB->y(), pCB->z());
1854  bp1 = CLRenderPoint(pCB->basePoint1_X(), pCB->basePoint1_Y(), pCB->basePoint1_Z());
1855  bp2 = CLRenderPoint(pCB->basePoint2_X(), pCB->basePoint2_Y(), pCB->basePoint2_Z());
1856  p2 = convert_to_absolute(&end, pBB);
1857  p3 = convert_to_absolute(&bp1, pBB);
1858  p4 = convert_to_absolute(&bp2, pBB);
1859  pData = new GLdouble[3 * NUM_BEZIER_POINTS];
1861  p3.getX(), p3.getY(), p3.getZ(),
1862  p4.getX(), p4.getY(), p4.getZ(),
1863  p2.getX(), p2.getY(), p2.getZ(),
1864  NUM_BEZIER_POINTS, pData);
1865  size_t j;
1866  size_t index = 0;
1867 
1868  for (j = 0; j < NUM_BEZIER_POINTS; ++j)
1869  {
1870  p.mX = pData[index++];
1871  p.mY = pData[index++];
1872  p.mZ = pData[index++];
1873  v.push_back(p);
1874  }
1875 
1876  delete[] pData;
1877  }
1878  else
1879  {
1880  end = CLRenderPoint(pP->x(), pP->y(), pP->z());
1881  p2 = convert_to_absolute(&end, pBB);
1882  p.mX = p2.getX();
1883  p.mY = p2.getY();
1884  p.mZ = p2.getZ();
1885  v.push_back(p);
1886  }
1887 
1888  // this end is the next start
1889  p1 = p2;
1890  }
1891 
1892  iMax = v.size();
1893 
1894  if (iMax > 1)
1895  {
1896  pData = new GLdouble[3 * (iMax + 1)];
1897  size_t index = 0;
1898  const simple_point* pSimple = NULL;
1899 
1900  for (i = 0; i < iMax; ++i)
1901  {
1902  pSimple = &v[i];
1903  pData[index++] = pSimple->mX;
1904  pData[index++] = pSimple->mY;
1905  pData[index++] = pSimple->mZ;
1906  }
1907 
1908  pData[index++] = pData[0];
1909  pData[index++] = pData[1];
1910  pData[index] = pData[2];
1911  // draw the polygon
1912  this->draw_datapoints(pData, iMax + 1, pBB, true);
1913  delete[] pData;
1914  }
1915 
1916  // restore the attributes
1918  }
1919 }
1920 
1921 /**
1922  * Method to draw a render rectangle object.
1923  */
1925 {
1926  // store and change the attributes
1927  this->save_current_attributes();
1929  // draw the rectangle
1930  // first we calculate the points of the rectangle
1931  CLRenderPoint rp(pRectangle->getX(), pRectangle->getY(), pRectangle->getZ());
1933  CLRelAbsVector v = pRectangle->getWidth();
1934  double width = v.getAbsoluteValue() + (v.getRelativeValue() / 100.0) * pBB->getDimensions().getWidth();
1935  v = pRectangle->getHeight();
1936  double height = v.getAbsoluteValue() + (v.getRelativeValue() / 100.0) * pBB->getDimensions().getHeight();
1937  v = pRectangle->getRadiusX();
1938  double rx = v.getAbsoluteValue() + (v.getRelativeValue() / 100.0) * width;
1939  v = pRectangle->getRadiusY();
1940  double ry = v.getAbsoluteValue() + (v.getRelativeValue() / 100.0) * height;
1941 
1942  // make sure rx and ry are not greater than half the width or height of the
1943  // rectangle.
1944  if (rx > width / 2.0) rx = width / 2.0;
1945 
1946  if (ry > height / 2.0) ry = height / 2.0;
1947 
1948  size_t numPoints = 4;
1949 
1950  if (rx > 0.0 && ry > 0.0)
1951  {
1952  // we have four corners
1953  // plus 4 points for the straight lines
1954  numPoints = 4 * (NUM_CORNER_SEGMENTS + 1);
1955  }
1956 
1957  // we need to reserve space for all data points and each point has three
1958  // values
1959  // we add an additonal data point to close the loop
1960  GLdouble* pData = new GLdouble[(numPoints + 1) * 3];
1961  double x = p.getX();
1962  double y = p.getY();
1963  double z = p.getZ();
1964  // now we fill the data array
1965  size_t index = 0;
1966 
1967  if (rx > 0.0 && ry > 0.0)
1968  {
1969  size_t i = 0;
1970  pData[i++] = 0.0;
1971  pData[i++] = ry;
1972  pData[i++] = 0.0;
1973  pData[i++] = 0.0;
1974  pData[i++] = height - ry;
1975  pData[i++] = 0.0;
1976  i += (NUM_CORNER_SEGMENTS - 1) * 3;
1977  pData[i++] = rx;
1978  pData[i++] = height;
1979  pData[i++] = 0.0;
1980  pData[i++] = width - rx;
1981  pData[i++] = height;
1982  pData[i++] = 0.0;
1983  i += (NUM_CORNER_SEGMENTS - 1) * 3;
1984  pData[i++] = width;
1985  pData[i++] = height - ry;
1986  pData[i++] = 0.0;
1987  pData[i++] = width;
1988  pData[i++] = ry;
1989  pData[i++] = 0.0;
1990  i += (NUM_CORNER_SEGMENTS - 1) * 3;
1991  pData[i++] = width - rx;
1992  pData[i++] = 0.0;
1993  pData[i++] = 0.0;
1994  pData[i++] = rx;
1995  pData[i++] = 0.0;
1996  pData[i] = 0.0;
1997  double delta = M_PI / (2.0 * NUM_CORNER_SEGMENTS);
1998  double phi = delta;
1999  double dx, dy, dx_inv, dy_inv;
2000  index = 0;
2001 
2002  for (i = 0; i < NUM_CORNER_SEGMENTS - 1; ++i)
2003  {
2004  index = (2 + i) * 3;
2005  dx = rx * sin(phi);
2006  dy = ry * cos(phi);
2007  dx_inv = rx * sin(M_PI / 2.0 - phi);
2008  dy_inv = ry * cos(M_PI / 2.0 - phi);
2009  // the first corner is mirrored, so we switch dx and dy
2010  pData[index] = rx - dx_inv;
2011  pData[index + 1] = height - ry + dy_inv;
2012  pData[index + 2] = 0.0;
2013  // the second corner is actually the one, we calculate
2014  index += (NUM_CORNER_SEGMENTS + 1) * 3;
2015  pData[index] = width - rx + dx;
2016  pData[index + 1] = height - ry + dy;
2017  pData[index + 2] = 0.0;
2018  // third corner is again mirrored
2019  index += (NUM_CORNER_SEGMENTS + 1) * 3;
2020  pData[index] = width - rx + dx_inv;
2021  pData[index + 1] = ry - dy_inv;
2022  pData[index + 2] = 0.0;
2023  // the fourth corner
2024  index += (NUM_CORNER_SEGMENTS + 1) * 3;
2025  pData[index] = rx - dx;
2026  pData[index + 1] = ry - dy;
2027  pData[index + 2] = 0.0;
2028  phi += delta;
2029  }
2030  }
2031  else
2032  {
2033  // first corner
2034  pData[0] = 0.0;
2035  pData[1] = 0.0;
2036  pData[2] = 0.0;
2037  // second corner
2038  pData[3] = 0.0;
2039  pData[4] = height;
2040  pData[5] = 0.0;
2041  // third corner
2042  pData[6] = width;
2043  pData[7] = height;
2044  pData[8] = 0.0;
2045  // fourth corner
2046  pData[9] = width;
2047  pData[10] = 0.0;
2048  pData[11] = 0.0;
2049  }
2050 
2051  index = 3 * numPoints;
2052  // we close the loop
2053  pData[index++] = pData[0];
2054  pData[index++] = pData[1];
2055  pData[index] = pData[2];
2056  this->draw_datapoints(pData, numPoints + 1, pBB, false, (GLfloat)x, (GLfloat)y, (GLfloat)z);
2057  // delete the data again
2058  delete[] pData;
2059  //
2060  // restore the attributes
2062 }
2063 
2064 /**
2065  * Extracts the group attributes from the given group.
2066  */
2068 {
2069  CLLayoutRenderer::extract_2d_attributes(pGroup, attributes);
2070  CLLayoutRenderer::extract_text_attributes(pGroup, attributes);
2072 }
2073 
2074 /**
2075  * Extracts the attributes from the given one dimensional object.
2076  */
2078 {
2079  extract_transformation_attributes(pObject, attributes);
2080 
2081  if (pObject->isSetStroke())
2082  {
2083  attributes->mStroke = pObject->getStroke();
2084  }
2085 
2086  if (pObject->isSetStrokeWidth())
2087  {
2088  attributes->mStrokeWidth = pObject->getStrokeWidth();
2089  }
2090 
2091  if (pObject->isSetDashArray())
2092  {
2093  attributes->mStrokeDasharray = pObject->getDashArray();
2094  }
2095 }
2096 
2097 /**
2098  * Extracts the attributes from the given two dimensional object.
2099  */
2101 {
2102  CLLayoutRenderer::extract_1d_attributes(pObject, attributes);
2103 
2104  if (pObject->isSetFill())
2105  {
2106  attributes->mFill = pObject->getFillColor();
2107  }
2108 
2109  if (pObject->isSetFillRule())
2110  {
2111  attributes->mFillRule = pObject->getFillRule();
2112  }
2113 }
2114 
2115 /**
2116  * Extracts the attributes from the given transformation object.
2117  */
2119 {
2120  attributes->mpTransform = new double[12];
2121 
2122  if (pObject->isSetMatrix())
2123  {
2124  memcpy(attributes->mpTransform, pObject->getMatrix(), 12 * sizeof(double));
2125  }
2126  else
2127  {
2128  memcpy(attributes->mpTransform, Transformation::getIdentityMatrix(), 12 * sizeof(double));
2129  }
2130 }
2131 
2132 /**
2133  * This method calculates a texture for a given gradient definition and a
2134  * given size.
2135  * The data object has to be a vector that can store RGBA values for a rectangle
2136  * of the given size. The memory has to be allocated before calling the method.
2137  * The scale specifies by how much the original box has been scaled.
2138  */
2139 void CLLayoutRenderer::create_gradient_texture(unsigned int size, GLubyte* pData, const CLGradientBase* pGradient, double z_value)
2140 {
2141  if (pGradient)
2142  {
2143  const CLLinearGradient* pLG = dynamic_cast<const CLLinearGradient*>(pGradient);
2144 
2145  if (pLG != NULL)
2146  {
2147  CLLayoutRenderer::create_linear_gradient_texture(size, pData, pLG, z_value);
2148  }
2149  else
2150  {
2151  const CLRadialGradient* pRG = dynamic_cast<const CLRadialGradient*>(pGradient);
2152 
2153  if (pRG != NULL)
2154  {
2155  CLLayoutRenderer::create_radial_gradient_texture(size, pData, pRG, z_value);
2156  }
2157  else
2158  {
2159  throw 0;
2160  }
2161  }
2162  }
2163 }
2164 
2165 /**
2166  * This method calculates a texture for a given linear gradient definition
2167  * and a given size.
2168  * The data object has to be a vector that can store RGBA values for a rectangle
2169  * of the given size. The memory has to be allocated before calling the method.
2170  * The scale specifies by how much the original box has been scaled.
2171  */
2172 void CLLayoutRenderer::create_linear_gradient_texture(unsigned int size, GLubyte* pData, const CLLinearGradient* pGradient, double /*z_value*/)
2173 {
2174  // first calculate the gradient vector
2175  // we need to consider the scale
2176  CLGradientStops stops(pGradient, this->mColorMap);
2177  double x1 = pGradient->getXPoint1().getAbsoluteValue() * this->mZoomFactor + (pGradient->getXPoint1().getRelativeValue() / 100.0 * size);
2178  double y1 = pGradient->getYPoint1().getAbsoluteValue() * this->mZoomFactor + (pGradient->getYPoint1().getRelativeValue() / 100.0 * size);
2179  double z1 = pGradient->getZPoint1().getAbsoluteValue() * this->mZoomFactor + (pGradient->getZPoint1().getRelativeValue() / 100.0 * 0.0);
2180  double x2 = pGradient->getXPoint2().getAbsoluteValue() * this->mZoomFactor + (pGradient->getXPoint2().getRelativeValue() / 100.0 * size);
2181  double y2 = pGradient->getYPoint2().getAbsoluteValue() * this->mZoomFactor + (pGradient->getYPoint2().getRelativeValue() / 100.0 * size);
2182  double z2 = pGradient->getZPoint2().getAbsoluteValue() * this->mZoomFactor + (pGradient->getZPoint2().getRelativeValue() / 100.0 * 0.0);
2183  // for each point we need to calculate the distance from p1 along the
2184  // vector v(p1,p2)
2185  // first we need the plane that is perpendicular to the vector v(p1,p2)
2186  // the we need to calculate the distance of a plane from that range of
2187  // planes to p1 and normalize it with the length of v(p1,p2)
2188  // the distance can be positive or negative, this makes a difference for
2189  // the gradient calculation
2190 
2191  // rel_distance is the distance with respect to the original gradient vector
2192  // that is, a value of 1.0 means that the point is exactly as far from the
2193  // plane as the gradient vector is long
2194  double deltax = x2 - x1;
2195  double deltay = y2 - y1;
2196  double deltaz = z2 - z1;
2197  double length = sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);
2198 
2199  // line through x1,y1 that is perpendicular to v(p1,p2)
2200  if (fabs(deltax) < ALMOST_ZERO)
2201  {
2202  // the gradient is along the y axis
2203  unsigned int i, j, iMax = size, jMax = size;
2204  double rel_distance = 0.0;
2205  unsigned int index = 0;
2206 
2207  for (i = 0; i < iMax; ++i)
2208  {
2209  for (j = 0; j < jMax; ++j)
2210  {
2211  rel_distance = (double)(i - y1) / length;
2212  stops.update_color(rel_distance);
2213  pData[index] = stops.color().mR;
2214  pData[++index] = stops.color().mG;
2215  pData[++index] = stops.color().mB;
2216  pData[++index] = stops.color().mA;
2217  ++index;
2218  }
2219  }
2220  }
2221  else if (fabs(deltay) < ALMOST_ZERO)
2222  {
2223  // the gradient is along the x axis
2224  unsigned int i, j, iMax = size, jMax = size;
2225  double rel_distance = 0.0;
2226  unsigned int index = 0;
2227 
2228  for (i = 0; i < iMax; ++i)
2229  {
2230  for (j = 0; j < jMax; ++j)
2231  {
2232  rel_distance = (double)(j - x1) / length;
2233  stops.update_color(rel_distance);
2234  pData[index] = stops.color().mR;
2235  pData[++index] = stops.color().mG;
2236  pData[++index] = stops.color().mB;
2237  pData[++index] = stops.color().mA;
2238  ++index;
2239  }
2240  }
2241  }
2242  else
2243  {
2244  double slope = deltay / deltax;
2245  double inv_slope = -deltax / deltay;
2246  double c_inv = y1 - inv_slope * x1;
2247  double c;
2248  // y=s*x+c;
2249  // intersection of line
2250  unsigned int i, j, iMax = size, jMax = size;
2251  double rel_distance = 0.0;
2252  double px, py;
2253  double r = inv_slope - slope;
2254  unsigned int index = 0;
2255 
2256  for (i = 0; i < iMax; ++i)
2257  {
2258  for (j = 0; j < jMax; ++j)
2259  {
2260  c = i - slope * j;
2261  // x*slope+c=x*inv_slope+inv_c
2262  // x*slope-x*inv_slope=inv_c-c
2263  // x= (inv_c-c)/(slope-inv_slope)
2264  px = (c - c_inv) / r;
2265  py = slope * px + c;
2266  rel_distance = sqrt((j - px) * (j - px) + (i - py) * (i - py)) / length;
2267 
2268  // check if the distance is actually negative
2269  if ((j - px)*deltax + (i - py)*deltay <= 0)
2270  {
2271  rel_distance = -rel_distance;
2272  }
2273 
2274  stops.update_color(rel_distance);
2275  pData[index] = stops.color().mR;
2276  pData[++index] = stops.color().mG;
2277  pData[++index] = stops.color().mB;
2278  pData[++index] = stops.color().mA;
2279  ++index;
2280  }
2281  }
2282  }
2283 }
2284 
2285 /**
2286  * This method calculates a texture for a given radial gradient
2287  * definition and a given size.
2288  * The data object has to be a vector that can store RGBA values for a rectangle
2289  * of the given size. The memory has to be allocated before calling the method.
2290  * The scale specifies by how much the original box has been scaled.
2291  */
2292 void CLLayoutRenderer::create_radial_gradient_texture(unsigned int size, GLubyte* pData, const CLRadialGradient* pGradient, double /*z_value*/)
2293 {
2294  // experiments with the focal point in firefox
2295  // - the radius seems to be aplied to the x and the y axis, so if a radius
2296  // of 50% is used in a rectangle, the circle is not a circle, but an
2297  // ellipse
2298 
2299  // spreadMethod Pad:
2300  // if the focal point is on the circle, everything that
2301  // is outside the circle and on the outside of the tangent with the circle
2302  // and the focal point is the stop color at 0%, everythin outside the
2303  // circle on the inside of the tangent is black, the color in the ellipse
2304  // depends on the distance from the focal point and the circle edge
2305  // in Inkscape this is different, everything outside the circle is the
2306  // color of the 100% stop, everything else is patterned
2307  // if the focal point is within the circle/ellipse, the whole shape has a
2308  // pattern where everything outside the circle/ellipse has the color of the
2309  // 100% stop
2310 
2311  // spreadMethod reflect:
2312  // if the focal point is on the circle, everything has the color at 0% stop
2313  // in Inkscape this is different, everything outside the tangent is the
2314  // color of the 100% stop, everything else is patterned
2315  // if the focal point is within the circle/ellipse, the whole shape has a
2316  // pattern
2317 
2318  // spreadMethod repeat:
2319  // if the focal point is on the circle, everything has the color at 0% stop
2320  // in Inkscape this is different, everything outside the tangent is the
2321  // color of the 100% stop, everything else is patterned
2322  // if the focal point is within the circle/ellipse, the whole shape has a
2323  // pattern
2324 
2325  CLGradientStops stops(pGradient, this->mColorMap);
2326  double cx = pGradient->getCenterX().getAbsoluteValue() * this->mZoomFactor + pGradient->getCenterX().getRelativeValue() / 100.0 * size;
2327  double cy = pGradient->getCenterY().getAbsoluteValue() * this->mZoomFactor + pGradient->getCenterY().getRelativeValue() / 100.0 * size;
2328  double fx = pGradient->getFocalPointX().getAbsoluteValue() * this->mZoomFactor + pGradient->getFocalPointX().getRelativeValue() / 100.0 * size;
2329  double fy = pGradient->getFocalPointY().getAbsoluteValue() * this->mZoomFactor + pGradient->getFocalPointY().getRelativeValue() / 100.0 * size;
2330  double rx = pGradient->getRadius().getAbsoluteValue() * this->mZoomFactor + pGradient->getRadius().getRelativeValue() / 100.0 * size;
2331  double ry = pGradient->getRadius().getAbsoluteValue() * this->mZoomFactor + pGradient->getRadius().getRelativeValue() / 100.0 * size;
2332  // TODO create an error if the radius is negative
2333 
2334  // TODO correct the focal point if it is outside the circle
2335 
2336  double delta_x, delta_y, slope, A, B, C, a;
2337  unsigned int i, j;
2338  // ellipse (x-cx)^2 / rx^2 + (y-cy)^2 / ry^2 = 1
2339  // -> x=+- sqrt(1-((x-cx)/rx)^2)*ry+cx
2340  // -> y=+- sqrt(1-((y-cy)/ry)^2)*rx+cy
2341  // for horizontal and vertical lines through the focal point fx,fy
2342  // we can already precalculate the value under the square root
2343  // which determines the number of solutions
2344  // for vertical lines (x=fx) it is
2345  const double s = 1 - pow((fx - cx) / rx, 2);
2346  // for horizontal lines (y=fy) it is
2347  const double t = 1 - pow((fy - cy) / ry, 2);
2348  double u = 0.0, solution1x = 0.0, solution1y = 0.0, solution2x = 0.0, solution2y = 0.0;
2349  unsigned int index = 0;
2350 
2351  for (i = 0; i < size; ++i)
2352  {
2353  for (j = 0; j < size; ++j)
2354  {
2355  delta_x = j - fx;
2356  delta_y = i - fy;
2357  double rel_distance = 1.0;
2358 
2359  if (fabs(delta_x) < ALMOST_ZERO && fabs(delta_y) < ALMOST_ZERO)
2360  {
2361  // we are on the focal point, so the rel_distance is 0.0
2362  rel_distance = 0.0;
2363  }
2364  else if (fabs(delta_x) < ALMOST_ZERO)
2365  {
2366  // use s
2367  if (s > 0.0)
2368  {
2369  // there are two crossing points
2370  solution1x = fx;
2371  // -> y=+- sqrt(s)*ry+cy
2372  solution1y = sqrt(s) * ry + cy;
2373  solution2x = fx;
2374  solution2y = -sqrt(s) * ry + cy;
2375  // we have to find out which one is the correct one
2376  // the correct one is not necessarily the one that is
2377  // closer, but the one that lies on the same side of the
2378  // focal point f(fx,fy) as the point we are looking at P(j,i)
2379  // so if the dot product is positive, we have the correct
2380  // value
2381  double dotProduct = ((double)j - fx) * (solution1x - fx) + ((double)i - fy) * (solution1y - fy);
2382 
2383  if (dotProduct <= 0)
2384  {
2385  assert(((double)j - fx) * (solution2x - fx) + ((double)i - fy) * (solution2y - fy) > 0);
2386  solution1x = solution2x;
2387  solution1y = solution2y;
2388  }
2389  }
2390  else if (fabs(s) < ALMOST_ZERO)
2391  {
2392  // there is only a tangent, so the focal points is directly
2393  // on the circle
2394  solution1x = fx;
2395  // -> y=+- sqrt(s)*rx+cy
2396  solution1y = cy;
2397  }
2398  else
2399  {
2400  // there is no crossing between the line and the circle,
2401  // since the focal points is always within the circle, this
2402  // should be impossible
2403  throw 0;
2404  }
2405  }
2406  else if (fabs(delta_y) < ALMOST_ZERO)
2407  {
2408  // use t
2409  if (t > 0.0)
2410  {
2411  // there are two crossing points
2412  solution1y = fy;
2413  // -> x=+- sqrt(t)*rx+cx
2414  solution1x = sqrt(t) * rx + cx;
2415  solution2y = fy;
2416  solution2x = -sqrt(t) * rx + cx;
2417  // we have to find out which one is the correct one
2418  // the correct one is not necessarily the one that is
2419  // closer, but the one that lies on the same side of the
2420  // focal point f(fx,fy) as the point we are looking at P(j,i)
2421  // so if the dot product is positive, we have the correct
2422  // value
2423  double dotProduct = ((double)j - fx) * (solution1x - fx) + ((double)i - fy) * (solution1y - fy);
2424 
2425  if (dotProduct < 0)
2426  {
2427  assert(((double)j - fx) * (solution2x - fx) + ((double)i - fy) * (solution2y - fy) > 0);
2428  solution1x = solution2x;
2429  solution1y = solution2y;
2430  }
2431  }
2432  else if (fabs(t) < ALMOST_ZERO)
2433  {
2434  // there is only a tangent, so the focal points is directly
2435  // on the circle
2436  solution1y = fy;
2437  // -> x=+- sqrt(t)*ry+cx
2438  solution1x = cx;
2439  }
2440  else
2441  {
2442  // there is no crossing between the line and the circle,
2443  // since the focal points is always within the circle, this
2444  // should be impossible
2445  throw 0;
2446  }
2447  }
2448  else
2449  {
2450  slope = delta_y / delta_x;
2451  // for the general case, we have to calculate the value under
2452  // the square root for every single point
2453  // a=fy-slope*fx;
2454  // u=B^2-4*A*C
2455  // u=(-2*cx*(ry)^2+2*rx^2*slope*(a-cy))^2-4*(ry^2+rx^2*slope^2)*(ry^2*cx^2+rx^2*(a-cy)^2-rx^2*ry^2)
2456  // u=(-2*cx*(ry)^2+2*rx^2*slope*(fy-slope*fx-cy))^2-4*(ry^2+rx^2*slope^2)*(ry^2*cx^2+rx^2*(fy-slope*fx-cy)^2-rx^2*ry^2)
2457  a = fy - slope * fx;
2458  A = ry * ry + rx * rx * slope * slope;
2459  B = -2 * cx * ry * ry + 2 * rx * rx * slope * (a - cy);
2460  C = ry * ry * cx * cx + rx * rx * pow(a - cy, 2) - rx * rx * ry * ry;
2461  u = B * B - 4 * A * C;
2462 
2463  if (u > 0.0)
2464  {
2465  // two solutions
2466  solution1x = (-B + sqrt(u)) / (2 * A);
2467  solution2x = (-B - sqrt(u)) / (2 * A);
2468  solution1y = solution1x * slope + a;
2469  solution2y = solution2x * slope + a;
2470  // we have to find out which one is the correct one
2471  // the correct one is not necessarily the one that is
2472  // closer, but the one that lies on the same side of the
2473  // focal point f(fx,fy) as the point we are looking at P(j,i)
2474  // so if the dot product is positive, we have the correct
2475  // value
2476  double dotProduct = ((double)j - fx) * (solution1x - fx) + ((double)i - fy) * (solution1y - fy);
2477 
2478  if (dotProduct < 0)
2479  {
2480  assert(((double)j - fx) * (solution2x - fx) + ((double)i - fy) * (solution2y - fy) > 0);
2481  solution1x = solution2x;
2482  solution1y = solution2y;
2483  }
2484  }
2485  else if (fabs(u) < ALMOST_ZERO)
2486  {
2487  // one solution, the focal point is on the circle
2488  solution1x = (-2 * slope * (fy - slope * fx) + cy) / (2 * (ry * ry + rx * rx * slope * slope));
2489  solution1y = solution1x * slope + (fy - slope * fx);
2490  }
2491  else
2492  {
2493  // no solution. Since the focal point is always in or on
2494  // the circle, this should be impossible
2495  throw 0;
2496  }
2497  }
2498 
2499  // now we have the correct crosing point in solution1x and
2500  // solution1y
2501  // we have to find out if the solution coincides with the focal
2502  // point
2503  // TODO, actually this could simplify the code above. If we get
2504  // only one solution, we actually know that it must be the focal
2505  // point
2506  if (fabs(solution1x - fx) >= ALMOST_ZERO || fabs(solution1y - fx) >= ALMOST_ZERO)
2507  {
2508  // we calculate the distance from the focal point to the
2509  // current point
2510  // and we calculate the distance from the focal point to the
2511  // crossing point
2512  // TODO we can save one square root calculation if we only
2513  // calculate the square root of the rel_distance
2514  double distance_f_c = sqrt(pow((solution1x - fx), 2) + pow((solution1y - fy), 2));
2515  double distance_f_p = sqrt(pow((j - fx), 2) + pow((i - fy), 2));
2516  rel_distance = distance_f_p / distance_f_c;
2517  }
2518 
2519  stops.update_color(rel_distance);
2520  pData[index] = stops.color().mR;
2521  pData[++index] = stops.color().mG;
2522  pData[++index] = stops.color().mB;
2523  pData[++index] = stops.color().mA;
2524  ++index;
2525  }
2526  }
2527 }
2528 
2529 /**
2530  * Maps the relative distance to a color and set the color as an rgb value in
2531  * pData. pData has to be the pointer where 4 GLfloat values are going to be
2532  * stored.
2533  */
2534 void CLLayoutRenderer::map_gradient_color(double rel_distance, const CLGradientBase* pGradient, GLubyte* pData)
2535 {
2536  if (rel_distance < 0.0)
2537  {
2538  switch (pGradient->getSpreadMethod())
2539  {
2540  case CLGradientBase::PAD:
2541  rel_distance = 0.0;
2542  break;
2543 
2545  // if the part of the distance before the decimal point
2546  // is devisible by 2, we just remove it
2547  // otherwise, we remove it and subtract the result from
2548  // 1.0
2549  rel_distance = fabs(rel_distance);
2550 
2551  if (((unsigned int)floor(rel_distance)) % 2 != 0)
2552  {
2553  rel_distance = 1.0 - (rel_distance - floor(rel_distance));
2554  }
2555  else
2556  {
2557  rel_distance = rel_distance - floor(rel_distance);
2558  }
2559 
2560  break;
2561 
2563  // we only need everything after the decimal point
2564  // and we need to subtract from 1.0
2565  rel_distance = rel_distance - floor(rel_distance);
2566  break;
2567  }
2568  }
2569 
2570  if (rel_distance > 1.0)
2571  {
2572  switch (pGradient->getSpreadMethod())
2573  {
2574  case CLGradientBase::PAD:
2575  rel_distance = 1.0;
2576  break;
2577 
2579 
2580  // if the part of the distance before the decimal point
2581  // is devisible by 2, we just remove it
2582  // otherwise, we remove it and subtract the result from
2583  // 1.0
2584  if (((unsigned int)floor(rel_distance)) % 2 != 0)
2585  {
2586  rel_distance = 1.0 - (rel_distance - floor(rel_distance));
2587  }
2588  else
2589  {
2590  rel_distance = rel_distance - floor(rel_distance);
2591  }
2592 
2593  break;
2594 
2596  // we only need everything after the decimal point
2597  rel_distance = rel_distance - floor(rel_distance);
2598  break;
2599  }
2600  }
2601 
2602  // we need to find the stop that has an offset lower than the
2603  // rel_distance and we need to find the stop that has an offset
2604  // larget than rel_distance
2605  assert(pGradient->getNumGradientStops() >= 2);
2606  const CLGradientStop *lower = NULL, *higher = NULL, *current = NULL;
2607  size_t k, kMax = pGradient->getNumGradientStops();
2608 
2609  for (k = 0; k < kMax; ++k)
2610  {
2611  current = pGradient->getGradientStop(k);
2612 
2613  if (current->getOffset().getRelativeValue() / 100.0 <= rel_distance)
2614  {
2615  lower = current;
2616  }
2617 
2618  if (!higher && current->getOffset().getRelativeValue() / 100.0 >= rel_distance)
2619  {
2620  higher = current;
2621  break;
2622  }
2623  }
2624 
2625  // check if we have a stop lower and one higher
2626  GLubyte* pColorData1 = new GLubyte[4];
2627  std::map<std::string, CLRGBAColor>::const_iterator pos;
2628 
2629  if (lower && higher)
2630  {
2631  if (lower == higher)
2632  {
2633  pos = this->mColorMap.find(lower->getStopColor());
2634  assert(pos != this->mColorMap.end());
2635  pColorData1[0] = pos->second.mR;
2636  pColorData1[1] = pos->second.mG;
2637  pColorData1[2] = pos->second.mB;
2638  pColorData1[3] = pos->second.mA;
2639  }
2640  else
2641  {
2642  GLfloat* pColorData2 = new GLfloat[4];
2643  pos = this->mColorMap.find(lower->getStopColor());
2644  assert(pos != this->mColorMap.end());
2645  pColorData1[0] = pos->second.mR;
2646  pColorData1[1] = pos->second.mG;
2647  pColorData1[2] = pos->second.mB;
2648  pColorData1[3] = pos->second.mA;
2649  pos = this->mColorMap.find(higher->getStopColor());
2650  assert(pos != this->mColorMap.end());
2651  pColorData2[0] = pos->second.mR;
2652  pColorData2[1] = pos->second.mG;
2653  pColorData2[2] = pos->second.mB;
2654  pColorData2[3] = pos->second.mA;
2655  // calculate the correct value
2656  GLfloat ratio = (GLfloat)((rel_distance - lower->getOffset().getRelativeValue() / 100.0) / ((higher->getOffset().getRelativeValue() - lower->getOffset().getRelativeValue()) / 100.0));
2657  pColorData1[0] = (GLubyte)(pColorData1[0] + ratio * (GLfloat)(pColorData2[0] - pColorData1[0]));
2658  pColorData1[1] = (GLubyte)(pColorData1[1] + ratio * (GLfloat)(pColorData2[1] - pColorData1[1]));
2659  pColorData1[2] = (GLubyte)(pColorData1[2] + ratio * (GLfloat)(pColorData2[2] - pColorData1[2]));
2660  pColorData1[3] = (GLubyte)(pColorData1[3] + ratio * (GLfloat)(pColorData2[3] - pColorData1[3]));
2661  delete[] pColorData2;
2662  }
2663  }
2664  else if (lower)
2665  {
2666  pos = this->mColorMap.find(lower->getStopColor());
2667  assert(pos != this->mColorMap.end());
2668  pColorData1[0] = pos->second.mR;
2669  pColorData1[1] = pos->second.mG;
2670  pColorData1[2] = pos->second.mB;
2671  pColorData1[3] = pos->second.mA;
2672  }
2673  else if (higher)
2674  {
2675  pos = this->mColorMap.find(higher->getStopColor());
2676  assert(pos != this->mColorMap.end());
2677  pColorData1[0] = pos->second.mR;
2678  pColorData1[1] = pos->second.mG;
2679  pColorData1[2] = pos->second.mB;
2680  pColorData1[3] = pos->second.mA;
2681  }
2682 
2683  pData[0] = pColorData1[0];
2684  pData[1] = pColorData1[1];
2685  pData[2] = pColorData1[2];
2686  pData[3] = pColorData1[3];
2687  delete[] pColorData1;
2688 }
2689 
2690 /**
2691  * Method to draw a render polygon from a set of datapoints
2692  */
2693 void CLLayoutRenderer::draw_datapoints(GLdouble* pData, size_t numPoints, const CLBoundingBox* pBB, bool doTesselation, float xOffset, float yOffset, float zOffset)
2694 {
2695  glMatrixMode(GL_MODELVIEW);
2696  glPushMatrix();
2697  glTranslatef(xOffset, yOffset, zOffset);
2698 
2699  // apply the current transformation
2700  if (memcmp(mCurrentAttributes.mpTransform, Transformation::getIdentityMatrix(), 12 * sizeof(double)))
2701  {
2702  // move back to the current offset
2703  glTranslated(this->mCurrentAttributes.mX, this->mCurrentAttributes.mY, this->mCurrentAttributes.mZ);
2704  GLdouble* matrix = new GLdouble[16];
2706  glMultMatrixd(matrix);
2707  delete[] matrix;
2708  // move to 0.0,0.0,0.0
2709  glTranslated(-this->mCurrentAttributes.mX, -this->mCurrentAttributes.mY, -this->mCurrentAttributes.mZ);
2710  }
2711 
2712  // draw the rectangle
2713  // first we draw the filled part
2714  if (this->mCurrentAttributes.mFillRule != CLGraphicalPrimitive2D::UNSET && this->mCurrentAttributes.mFill != "none")
2715  {
2716 
2717  // tesselate the polygon to make sure that it is drawn correctly by
2718  // OpenGL
2719  GLUtesselator* pTess = NULL;
2720 
2721  if (doTesselation)
2722  {
2723  pTess = gluNewTess();
2724  }
2725 
2726  bool singleColor = true;
2727  // set the stepsize for the datapoints to 3
2728  // which means that the data array only contains vertex information
2729  // if we have a gradient, we have to set it to 5 later on
2730  // because the data now contains texture coordinates as well
2731  unsigned int stepsize = 3;
2732  GLdouble* pOrigData = pData;
2733  GLdouble* pNewData = NULL;
2734  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mFill);
2735 
2736  if (pos != this->mColorMap.end())
2737  {
2738  const CLRGBAColor& c = pos->second;
2739  glColor4ub(c.mR, c.mG, c.mB, c.mA);
2740  }
2741  else
2742  {
2743  singleColor = false;
2744  // enable tesselation if a gradient is specified
2745  pTess = gluNewTess();
2746  // it could be a gradient
2747  std::map<std::string, std::pair<const CLGradientBase*, CLTextureSpec*> >::const_iterator pos = this->mGradientMap.find(mCurrentAttributes.mFill);
2748  assert(pos != this->mGradientMap.end());
2749  const CLTextureSpec* pTexSpec = pos->second.second;
2750  assert(pTexSpec != NULL);
2751  unsigned int i;
2752 #ifdef _WIN32
2753  gluTessCallback(pTess, GLU_TESS_BEGIN, (GLvoid(__stdcall *)())glBegin);
2754  gluTessCallback(pTess, GLU_TESS_END, glEnd);
2755  gluTessCallback(pTess, GLU_TESS_VERTEX, (GLvoid(__stdcall *)())CLLayoutRenderer::VERTEX_CALLBACK_GRADIENT);
2756  gluTessCallback(pTess, GLU_TESS_COMBINE, (GLvoid(__stdcall *)())CLLayoutRenderer::COMBINE_CALLBACK_GRADIENT);
2757 #else
2758  gluTessCallback(pTess, GLU_TESS_BEGIN, (GLvoid(*)())glBegin);
2759  gluTessCallback(pTess, GLU_TESS_END, glEnd);
2760  gluTessCallback(pTess, GLU_TESS_VERTEX, (GLvoid(*)())CLLayoutRenderer::VERTEX_CALLBACK_GRADIENT);
2761  gluTessCallback(pTess, GLU_TESS_COMBINE, (GLvoid(*)())CLLayoutRenderer::COMBINE_CALLBACK_GRADIENT);
2762 #endif // _WIN32
2763  stepsize = 5;
2764  pNewData = new GLdouble[5 * numPoints];
2765  // assign the texture coordinates
2766  double width = pBB->getDimensions().getWidth();
2767  double height = pBB->getDimensions().getHeight();
2768 
2769  for (i = 0; i < numPoints; ++i)
2770  {
2771  pNewData[5 * i] = pData[3 * i];
2772  pNewData[5 * i + 1] = pData[3 * i + 1];
2773  pNewData[5 * i + 2] = pData[3 * i + 2];
2774  pNewData[5 * i + 3] = (pData[3 * i] + xOffset - pBB->getPosition().getX()) / width;
2775  pNewData[5 * i + 4] = (pData[3 * i + 1] + yOffset - pBB->getPosition().getY()) / height;
2776  }
2777 
2778  pData = pNewData;
2779 
2780  // load the texture
2781  if (pTexSpec->mTextureName != 0)
2782  {
2783  glBindTexture(GL_TEXTURE_2D, pTexSpec->mTextureName);
2784  // enable 2D texturing
2785  glEnable(GL_TEXTURE_2D);
2786  }
2787 
2788  /*
2789  else
2790  {
2791  std::cerr << "Texture not bound." << std::endl;
2792  }
2793  */
2794  }
2795 
2796  if (pTess && singleColor)
2797  {
2798  // we don't need to do anythong special during the tesselation since
2799  // the whole object only has one color
2800 #ifdef _WIN32
2801  gluTessCallback(pTess, GLU_TESS_BEGIN, (GLvoid(__stdcall *)())glBegin);
2802  gluTessCallback(pTess, GLU_TESS_END, (GLvoid(__stdcall *)())glEnd);
2803  gluTessCallback(pTess, GLU_TESS_VERTEX, (GLvoid(__stdcall *)())glVertex3dv);
2804  gluTessCallback(pTess, GLU_TESS_COMBINE, (GLvoid(__stdcall *)())CLLayoutRenderer::COMBINE_CALLBACK);
2805 #else
2806  gluTessCallback(pTess, GLU_TESS_BEGIN, (GLvoid(*)())glBegin);
2807  gluTessCallback(pTess, GLU_TESS_END, (GLvoid(*)())glEnd);
2808  gluTessCallback(pTess, GLU_TESS_VERTEX, (GLvoid(*)())glVertex3dv);
2809  gluTessCallback(pTess, GLU_TESS_COMBINE, (GLvoid(*)())CLLayoutRenderer::COMBINE_CALLBACK);
2810 #endif // _WIN32
2811  }
2812 
2813  if (pTess)
2814  {
2815  // specify the fill rule to the tesselator EVENODD, NONZERO
2816  switch (this->mCurrentAttributes.mFillRule)
2817  {
2819  // this should not happen
2820  CCopasiMessage(CCopasiMessage::EXCEPTION, "Invalid Render Information: No fill rule specified");
2821  break;
2822 
2824  gluTessProperty(pTess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
2825  break;
2826 
2828  gluTessProperty(pTess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
2829  break;
2830 
2832  // this should not happen
2833  // inherit has to be replaced by something meaningfull
2834  CCopasiMessage(CCopasiMessage::EXCEPTION, "Invalid Render Information: fill rule \"INHERIT\" specified");
2835  break;
2836  }
2837 
2838 #ifdef _WIN32
2839  gluTessCallback(pTess, GLU_TESS_ERROR, (GLvoid(__stdcall *)())CLLayoutRenderer::TESS_ERROR);
2840 #else
2841  gluTessCallback(pTess, GLU_TESS_ERROR, (GLvoid(*)())CLLayoutRenderer::TESS_ERROR);
2842 #endif // _WIN32
2843  gluTessBeginPolygon(pTess, NULL);
2844  gluTessBeginContour(pTess);
2845  // specify the actual vertex data
2846  size_t j = 0, jMax = numPoints - 1;
2847 
2848  while (j < jMax)
2849  {
2850  gluTessVertex(pTess, &pData[j * stepsize], &pData[j * stepsize]);
2851  ++j;
2852  }
2853 
2854  gluTessEndContour(pTess);
2855  gluTessEndPolygon(pTess);
2856  gluDeleteTess(pTess);
2857  pData = pOrigData;
2858 
2859  if (pNewData)
2860  {
2861  delete[] pNewData;
2862  }
2863  }
2864  else
2865  {
2866  // it must be a single colored object, so we just draw the vertex array
2867  glEnableClientState(GL_VERTEX_ARRAY);
2868  glVertexPointer(3, GL_DOUBLE, 0, pData);
2869  glDrawArrays(GL_POLYGON, 0, (GLsizei)(numPoints - 1));
2870  glDisableClientState(GL_VERTEX_ARRAY);
2871  }
2872 
2873  if (!singleColor)
2874  {
2875  // disable texturing again
2876  glDisable(GL_TEXTURE_2D);
2877  }
2878  }
2879 
2880  //
2881  // next we draw the edge
2882  //
2883  if (this->mCurrentAttributes.mStrokeWidth > 0.0 && this->mCurrentAttributes.mStroke != "none")
2884  {
2885  std::map<std::string, CLRGBAColor>::const_iterator pos = this->mColorMap.find(mCurrentAttributes.mStroke);
2886  assert(pos != this->mColorMap.end());
2887  const CLRGBAColor& c = pos->second;
2888  glColor4ub(c.mR, c.mG, c.mB, c.mA);
2889  this->draw_line(numPoints, pData);
2890  // draw the final cap
2891  size_t index = (numPoints - 2) * 3;
2892  draw_cap(pData[index], pData[index + 1], pData[index + 2], pData[0], pData[1], pData[2], pData[3], pData[4], pData[5], mCurrentAttributes.mStrokeWidth);
2893  }
2894 
2895  glPopMatrix();
2896 }
2897 
2898 /**
2899  * Maps the given arrow head to the given line segment.
2900  */
2901 void CLLayoutRenderer::map_arrow_head(const CLPoint& mapTo, const CLPoint& direction, const std::string& headId)
2902 {
2903  // the line ending that is to be drawn is identified by headId
2904  // the origin of the line ending has to be mapped to mapTo
2905  // the line ending might have to be rotated to fit the vector called
2906  // direction
2907  //
2908  // first we get the line ending
2909  if (headId != "" && headId != "none")
2910  {
2911  const CLLineEnding* pLineEnding = this->mpResolver->getLineEnding(headId);
2912  assert(pLineEnding);
2913  // TODO create an error if the line ending was not found
2914  glMatrixMode(GL_MODELVIEW);
2915  glPushMatrix();
2916  glLoadIdentity();
2917  glTranslated(mapTo.getX(), mapTo.getY(), mapTo.getZ());
2918 
2919  // now we need to figure out the rotation
2920  if (pLineEnding->getIsEnabledRotationalMapping())
2921  {
2922  //std::cout << "direction: " << "(" << direction.getX() << "," << direction.getY() << "," << direction.getZ() << ")" << std::endl;
2923  double length = sqrt(direction.getX() * direction.getX() + direction.getY() * direction.getY() + direction.getZ() * direction.getZ());
2924  //std::cout << "length: " << length << std::endl;
2925  CLPoint normDirection(direction.getX() / length, direction.getY() / length, direction.getZ() / length);
2926  //std::cout << "norm direction: " << "(" << normDirection.getX() << "," << normDirection.getY() << "," << normDirection.getZ() << ")" << std::endl;
2927  double x_2 = 0.0;
2928  double y_2 = 0.0;
2929  double z_2 = 0.0;
2930 
2931  if (fabs(direction.getX()) < ALMOST_ZERO && direction.getZ() < ALMOST_ZERO)
2932  {
2933  x_2 = -normDirection.getY();
2934  y_2 = 0.0;
2935  z_2 = 0.0;
2936  }
2937  else
2938  {
2939  x_2 = -normDirection.getY() * normDirection.getX();
2940  y_2 = 1 - normDirection.getY() * normDirection.getY();
2941  z_2 = -normDirection.getY() * normDirection.getZ();
2942  }
2943 
2944  length = sqrt(x_2 * x_2 + y_2 * y_2 + z_2 * z_2);
2945  x_2 /= length;
2946  y_2 /= length;
2947  z_2 /= length;
2948  //std::cout << "second vector: " << "(" << x_2 << "," << y_2 << "," << z_2 << ")" << std::endl;
2949  //std::cout << std::endl;
2950  //std::cout << std::endl;
2951  GLdouble matrix[16] = {normDirection.getX(), normDirection.getY(), normDirection.getZ(), 0.0, x_2, y_2, z_2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0};
2952  glMultMatrixd(matrix);
2953  }
2954 
2955  this->save_current_attributes();
2956  // transformations in line endings are relative to the line endings
2957  // viewport
2958  mCurrentAttributes.mX = 0.0;
2959  mCurrentAttributes.mY = 0.0;
2960  mCurrentAttributes.mZ = 0.0;
2963  // clear the current arrow head information to avoid an endless loop
2964  mCurrentAttributes.mStartHead = "none";
2965  mCurrentAttributes.mEndHead = "none";
2966  // clear the transformation information since it does not belong to the
2967  // information that is inherited.
2968  memcpy(mCurrentAttributes.mpTransform, Transformation::getIdentityMatrix(), 12 * sizeof(double));
2969  // draw the line ending
2970  this->draw_group(pLineEnding->getGroup(), pLineEnding->getBoundingBox());
2971  // restore the matrix
2973  glPopMatrix();
2974  }
2975 }
2976 
2978 {
2979  const char *szError = (const char*)gluErrorString(error);
2980  CCopasiMessage(CCopasiMessage::EXCEPTION, "tesselation error: %s", szError);
2981 }
2982 
2983 void CLLayoutRenderer::COMBINE_CALLBACK(GLdouble coords[3], GLdouble** /*vertex_data[4]*/, GLfloat* /*weight[4]*/, GLdouble** dataOut)
2984 {
2985  GLdouble* vertex;
2986  //int i;
2987  vertex = new GLdouble[3];
2988  vertex[0] = coords[0];
2989  vertex[1] = coords[1];
2990  vertex[2] = coords[2];
2991  *dataOut = vertex;
2992 }
2993 
2994 void CLLayoutRenderer::COMBINE_CALLBACK_GRADIENT(GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4], GLdouble** dataOut)
2995 {
2996  GLdouble* vertex;
2997  vertex = new GLdouble[5];
2998  vertex[0] = coords[0];
2999  vertex[1] = coords[1];
3000  vertex[2] = coords[2];
3001  vertex[3] = weight[0] * vertex_data[0][3]
3002  + weight[1] * vertex_data[1][3]
3003  + weight[2] * vertex_data[2][3]
3004  + weight[3] * vertex_data[3][3];
3005  vertex[4] = weight[0] * vertex_data[0][4]
3006  + weight[1] * vertex_data[1][4]
3007  + weight[2] * vertex_data[2][4]
3008  + weight[3] * vertex_data[3][4];
3009  *dataOut = vertex;
3010 }
3011 
3013 {
3014  const GLdouble* pointer = (GLdouble*)vertex;
3015  // the last two are the texture coordinates
3016  glTexCoord2dv(pointer + 3);
3017  // the first three double values are the vertex coordiantes
3018  glVertex3dv(pointer);
3019 }
3020 
3021 void CLLayoutRenderer::setZoomFactor(double zoomFactor)
3022 {
3023  this->mZoomFactor = zoomFactor;
3024 }
3025 
3027 {
3028  return this->mZoomFactor;
3029 }
3030 
3031 /**
3032  * Calculates the points for a cubic bezier curve.
3033  * The algorithm calculates numPoints and stores them in pData. pData
3034  * has to be an array with enough space for numPoints*3 GLfloat items.
3035  */
3036 void CLLayoutRenderer::calculate_cubicbezier(double sx, double sy, double sz, double p1x, double p1y, double p1z, double p2x, double p2y, double p2z, double ex, double ey, double ez, unsigned int numPoints, GLdouble* pData)
3037 {
3038  unsigned int index = 0;
3039  pData[index++] = sx;
3040  pData[index++] = sy;
3041  pData[index++] = sz;
3042  unsigned int i, iMax = numPoints - 1;
3043  double stepSize = 1.0 / (float)(iMax);
3044  double t = 0.0;
3045  double t_square = 0.0;
3046  double t_cube = 0.0;
3047  double oneMinusT = 0.0;
3048  double oneMinusT_square = 0.0;
3049  double oneMinusT_cube = 0.0;
3050  double a, b;
3051 
3052  for (i = 1; i < iMax; ++i)
3053  {
3054  t = i * stepSize;
3055  oneMinusT = 1.0 - t;
3056  oneMinusT_square = oneMinusT * oneMinusT;
3057  oneMinusT_cube = oneMinusT * oneMinusT_square;
3058  t_square = t * t;;
3059  t_cube = t_square * t;
3060  a = 3 * t * oneMinusT_square;
3061  b = 3 * t_square * oneMinusT;
3062  pData[index++] = sx * oneMinusT_cube + p1x * a + p2x * b + ex * t_cube;
3063  pData[index++] = sy * oneMinusT_cube + p1y * a + p2y * b + ey * t_cube;
3064  pData[index++] = sz * oneMinusT_cube + p1z * a + p2z * b + ez * t_cube;
3065  }
3066 
3067  pData[index++] = ex;
3068  pData[index++] = ey;
3069  pData[index++] = ez;
3070 }
3071 
3072 /**
3073  * Method to draw a line made up of a set of points.
3074  */
3075 void CLLayoutRenderer::draw_line(size_t numPoints, GLdouble* pData)
3076 {
3077  // a line has to have more than one point
3078  if (numPoints > 1)
3079  {
3080  // create the texture for line stippling
3081  const CLLineStippleTexture* pTexture = NULL;
3082  std::map<const std::vector<unsigned int>, const CLLineStippleTexture*>::const_iterator pos = this->mLinestippleMap.find(this->mCurrentAttributes.mStrokeDasharray);
3083 
3084  if (pos != this->mLinestippleMap.end())
3085  {
3086  pTexture = pos->second;
3087  }
3088 
3089  GLfloat* pTextureCoordinates = NULL;
3090  GLdouble* pOrigData = pData;
3091  size_t iMax = numPoints;
3092 
3093  if (pTexture != NULL)
3094  {
3095  // segment the
3096  // datapoints into pieces that fit the texture pattern
3097  // and create the texture coordinates
3098  std::vector<simple_point> v;
3099  // we need at least numPoints elements in the vector
3100  v.reserve(numPoints);
3101  this->segment_data(pTexture->mPatternLength, (double)pTexture->mPatternLength / (double)pTexture->mTextureLength, numPoints, pData, v);
3102 
3103  if (v.size() != numPoints)
3104  {
3105  iMax = (unsigned int)v.size();
3106  // we have to create a new dataset
3107  pData = new GLdouble[3 * iMax];
3108  pTextureCoordinates = new GLfloat[iMax];
3109  std::vector<simple_point>::const_iterator it = v.begin(), endit = v.end();
3110  unsigned int index = 0;
3111  unsigned int index2 = 0;
3112 
3113  while (it != endit)
3114  {
3115  pData[index++] = it->mX;
3116  pData[index++] = it->mY;
3117  pData[index++] = it->mZ;
3118  pTextureCoordinates[index2++] = (GLfloat)it->mS;
3119  ++it;
3120  }
3121  }
3122  else
3123  {
3124  // create an array for the texture coordinates
3125  pTextureCoordinates = new GLfloat[iMax];
3126  std::vector<simple_point>::const_iterator it = v.begin(), endit = v.end();
3127  unsigned int index = 0;
3128 
3129  while (it != endit)
3130  {
3131  pTextureCoordinates[index++] = (GLfloat)it->mS;
3132  ++it;
3133  }
3134  }
3135 
3136  if (pTexture->mTextureName != 0)
3137  {
3138  glBindTexture(GL_TEXTURE_1D, pTexture->mTextureName);
3139  // enable 1D texturing
3140  glEnable(GL_TEXTURE_1D);
3141  }
3142  }
3143 
3144  unsigned int i;
3145  // the loop does not go to the very last point, but stops one before that
3146  --iMax;
3147  unsigned int index = 0;
3148 
3149  for (i = 0; i < iMax; ++i)
3150  {
3151  if (pTextureCoordinates)
3152  {
3153  draw_line_segment(pData[index], pData[index + 1], pData[index + 2], pData[index + 3], pData[index + 4], pData[index + 5], mCurrentAttributes.mStrokeWidth, true, 0.0, pTextureCoordinates[i + 1]);
3154  }
3155  else
3156  {
3157  draw_line_segment(pData[index], pData[index + 1], pData[index + 2], pData[index + 3], pData[index + 4], pData[index + 5], mCurrentAttributes.mStrokeWidth);
3158  }
3159 
3160  index += 3;
3161 
3162  // don't draw a cap after the last segment
3163  if (i != iMax - 1)
3164  {
3165  draw_cap(pData[index - 3], pData[index - 2], pData[index - 1], pData[index], pData[index + 1], pData[index + 2], pData[index + 3], pData[index + 4], pData[index + 5], mCurrentAttributes.mStrokeWidth);
3166  }
3167  }
3168 
3169  // if we created new datapoints, we have to delete them again
3170  if (pOrigData != pData)
3171  {
3172  delete[] pData;
3173  pData = pOrigData;
3174  }
3175 
3176  // delete any texture coordinates we have created
3177  if (pTextureCoordinates != NULL)
3178  {
3179  delete[] pTextureCoordinates;
3180  glDisable(GL_TEXTURE_1D);
3181  }
3182  }
3183 }
3184 
3185 void CLLayoutRenderer::createGLMatrix(const double* const matrix, GLdouble* glMatrix)
3186 {
3187  glMatrix[0] = matrix[0];
3188  glMatrix[1] = matrix[1];
3189  glMatrix[2] = matrix[2];
3190  glMatrix[3] = 0.0;
3191  glMatrix[4] = matrix[3];
3192  glMatrix[5] = matrix[4];
3193  glMatrix[6] = matrix[5];
3194  glMatrix[7] = 0.0;
3195  glMatrix[8] = matrix[6];
3196  glMatrix[9] = matrix[7];
3197  glMatrix[10] = matrix[8];
3198  glMatrix[11] = 0.0;
3199  glMatrix[12] = matrix[9];
3200  glMatrix[13] = matrix[10];
3201  glMatrix[14] = matrix[11];
3202  glMatrix[15] = 1.0;
3203 }
3204 
3205 /**
3206  * Creates a 1D texture for the line stippling.
3207  * The caller is responsible for deleting the returned object.
3208  * If the dasharray does not contain a valid stipple pattern NULL is
3209  * returned.
3210  */
3211 CLLineStippleTexture* CLLayoutRenderer::createLineStippleTexture(const std::vector<unsigned int>& dasharray)
3212 {
3213  return new CLLineStippleTexture(dasharray);
3214 }
3215 
3216 void CLLayoutRenderer::segment_data(double length, double ratio, size_t numPoints, GLdouble* pData, std::vector<simple_point>& v)
3217 {
3218  double current_distance = 0.0;
3219  size_t i, iMax = numPoints;
3220  simple_point start = {pData[0], pData[1], pData[2], 0.0};
3221  // add the first point
3222  v.push_back(start);
3223  double vx, vy, vz, l;
3224  double current_repeat, end_repeat;
3225  size_t current_repeat_i, end_repeat_i;
3226  simple_point p;
3227  simple_point end;
3228  size_t index = 3;
3229 
3230  for (i = 1; i < iMax; ++i)
3231  {
3232  end.mX = pData[index++];
3233  end.mY = pData[index++];
3234  end.mZ = pData[index++];
3235  vx = end.mX - start.mX;
3236  vy = end.mY - start.mY;
3237  vz = end.mZ - start.mZ;
3238  l = sqrt(vx * vx + vy * vy + vz * vz);
3239  current_repeat = current_distance / length;
3240  current_repeat_i = (unsigned int)floor(current_repeat);
3241  end_repeat = (current_distance + l) / length;
3242  end_repeat_i = (unsigned int)floor(end_repeat);
3243 
3244  if (current_repeat_i == end_repeat_i)
3245  {
3246  // we don't need an additonal data point,
3247  }
3248  else
3249  {
3250  // normalize v to the langth of the pattern
3251  double factor = length / l;
3252  vx *= factor;
3253  vy *= factor;
3254  vz *= factor;
3255  // we need additional data points
3256  current_repeat = (double)current_repeat_i;
3257  current_repeat += 1.0;
3258  unsigned int step = 1;
3259 
3260  while (current_repeat < end_repeat)
3261  {
3262  // new datapoint at start+step*v with texture coordinate
3263  // current_repeat
3264  p.mX = step * vx + start.mX;
3265  p.mY = step * vy + start.mY;
3266  p.mZ = step * vz + start.mZ;
3267  p.mS = ratio;
3268  current_repeat += 1.0;
3269  v.push_back(p);
3270  step += 1;
3271  }
3272  }
3273 
3274  // we add the end point to the list
3275  // and set the texture coordinate for it
3276  end.mS = (end_repeat - floor(end_repeat));
3277 
3278  if (fabs(end.mS) < ALMOST_ZERO)
3279  {
3280  end.mS = ratio;
3281  }
3282  else
3283  {
3284  end.mS *= ratio;
3285  }
3286 
3287  v.push_back(end);
3288  start = end;
3289  current_distance += length;
3290  }
3291 }
3292 
3293 /**
3294  * Sets a function that is able to generate a texture spec from
3295  * some given font settings and a piece of text.
3296  */
3298 {
3299  this->mpFontRenderer = pFontRenderer;
3300 }
3301 
3302 /**
3303  * Analyses the render information and creates some of the textures.
3304  * First it determines which object are drawn based on the viewport
3305  * coordinates that are passed in.
3306  * Next it resolves the styles for all the objects that are to be drawn
3307  * and it determines the size of the textures.
3308  * Last it creates all textures.
3309  */
3310 void CLLayoutRenderer::analyse_render_information(double lx, double ly, double rx, double ry)
3311 {
3312  // go through the complete render information and resolve all colors
3313  // go through the list of gradients and create CLText*ureSpec objects for each
3314  // one and put it in a map
3315  // go through the complete layout and find the largest usage of any
3316  // gradient
3317  // Create the gradients
3318  // go through the layout and find all line stipple patterns and create the
3319  // textures placeholders for them
3320 
3321  // store the styles that have already been processed so that we process
3322  // each style only once
3323  if (rx - lx > ALMOST_ZERO && ry - ly > ALMOST_ZERO)
3324  {
3325  this->update_drawables(lx, ly, rx, ry);
3326  this->update_style_information();
3328  }
3329 }
3330 
3331 /**
3332  * This method goes through all layout objects and checks them if they
3333  * are within the current viewport. If they are, they are added to the list of objects that are drawn.
3334  * An object is within the viewport if it's bounding box or parts of it
3335  * are within the viewport.
3336  * For curves, each start, end and base point is checked and if any
3337  * of those is within the viewport, the curve is added to the list.
3338  * The viewport is specified by its lower left (lx,ly) and upper right
3339  * point (rx,ry).
3340  */
3341 void CLLayoutRenderer::update_drawables(double lx, double ly, double rx, double ry)
3342 {
3343  this->mDrawables.clear();
3344  std::vector<CLGraphicalObject*> v = this->getObjectsInBoundingBox(lx, ly, rx, ry);
3345  std::vector<CLGraphicalObject*>::iterator it = v.begin(), endit = v.end();
3346 
3347  while (it != endit)
3348  {
3349  this->mDrawables.push_back(*it);
3350  ++it;
3351  }
3352 
3353  // update the association maps
3354  // we store which species reference glyph is associated with which species glyph
3355  // and which text glyph is associated with which graphical object
3356  this->update_associations();
3357 }
3358 
3359 /**
3360  * This method goes throught the list of all drawables and tries to resolve the
3361  * style for each object.
3362  */
3364 {
3365  this->mStyleMap.clear();
3366 
3367  if (this->mpResolver != NULL)
3368  {
3369  std::vector<const CLGraphicalObject*>::const_iterator it = this->mDrawables.begin();
3370  std::vector<const CLGraphicalObject*>::const_iterator endit = this->mDrawables.end();
3371  const CLStyle* pStyle = NULL;
3372 
3373  while (it != endit)
3374  {
3375  pStyle = this->mpResolver->resolveStyle(*it);
3376 
3377  if (pStyle)
3378  {
3379  this->mStyleMap[*it] = pStyle;
3380  }
3381 
3382  ++it;
3383  }
3384  }
3385 
3386  //std::map<const CLGraphicalObject*, const CLStyle*>::const_iterator it2 = this->mStyleMap.begin(), endit2 = this->mStyleMap.end();
3387 }
3388 
3389 /**
3390  * This method goes through all styles that are used by the current drawables
3391  * and figures out which gradients, colors and line stipples are used and
3392  * creates them.
3393  */
3395 {
3396  // only update the textures and colors for the current drawables that actually have a
3397  // style
3398  std::map<const CLGraphicalObject*, const CLStyle*>::const_iterator it = this->mStyleMap.begin();
3399  std::map<const CLGraphicalObject*, const CLStyle*>::const_iterator endit = this->mStyleMap.end();
3400  const CLReactionGlyph* pRG = NULL;
3401  const CLMetabReferenceGlyph* pSRG = NULL;
3402  const CLTextGlyph* pTG = NULL;
3403  double maxDimension = 0.0;
3404  const CLBoundingBox* pBB = NULL;
3405 
3406  while (it != endit)
3407  {
3408  assert(it->first != NULL);
3409  assert(it->second != NULL);
3410  pRG = dynamic_cast<const CLReactionGlyph*>(it->first);
3411  pSRG = dynamic_cast<const CLMetabReferenceGlyph*>(it->first);
3412  pTG = dynamic_cast<const CLTextGlyph*>(it->first);
3413  pBB = &it->first->getBoundingBox();
3414 
3415  if ((pRG != NULL && pRG->getCurve().getNumCurveSegments() != 0) || (pSRG != NULL && pSRG->getCurve().getNumCurveSegments() != 0))
3416  {
3417  // we set the maxDimension to 0 so that we don't calculate
3418  // gradients for the objects where the curve from the layout is
3419  // drawn anyway
3420  maxDimension = 0.0;
3421  }
3422  else if (pTG != NULL)
3423  {
3424  // it is a text glyph, so there can't be a gradient or line
3425  // stipple, but we need to create a texture for the font
3426  maxDimension = 0.0;
3427  std::string text = CLLayoutRenderer::resolve_text(pTG);
3428  // get the font spec for the group
3429  CLFontSpec fontSpec = this->getFontSpec(it->second->getGroup(), pBB->getDimensions().getHeight(), CLFontSpec());
3430  std::map<CLFontSpec, std::map<std::string, CLTextTextureSpec*> >::iterator pos = this->mFontTextureMap.find(fontSpec);
3431 
3432  if (pos == this->mFontTextureMap.end())
3433  {
3434  std::map<std::string, CLTextTextureSpec*> m;
3435  this->mFontTextureMap[fontSpec] = m;
3436  pos = this->mFontTextureMap.find(fontSpec);
3437  assert(pos != this->mFontTextureMap.end());
3438  }
3439 
3440  std::map<std::string, CLTextTextureSpec*>::iterator pos2 = pos->second.find(text);
3441 
3442  if (pos2 == pos->second.end() || pos2->second->mTextureName == 0)
3443  {
3444  // add the texture although it might be NULL
3445  //std::cout << "Creating new texture for text glyph: " << pTG->getId() << std::endl;
3446  std::pair<CLTextTextureSpec*, GLubyte*> texture = (*this->mpFontRenderer)(fontSpec.mFamily, fontSpec.mSize, text, fontSpec.mWeight, fontSpec.mStyle, this->mZoomFactor);
3447 
3448  //std::cout << "Created texture at " << pTexture << " for text \"" << text << "\"" << std::endl;
3449  //std::cout << "texture id: " << pTexture->mTextureName << std::endl;
3450  if (texture.first != NULL && texture.second != NULL)
3451  {
3452  glGenTextures(1, &texture.first->mTextureName);
3453  assert(texture.first->mTextureName != 0);
3454  glBindTexture(GL_TEXTURE_2D, texture.first->mTextureName);
3455  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3456  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3457  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3458  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3459  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3460  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei)texture.first->mTextureWidth, (GLsizei)texture.first->mTextureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture.second);
3461  delete[] texture.second;
3462  }
3463 
3464  pos->second[text] = texture.first;
3465  this->mTextGlyphMap[pTG] = texture.first;
3466  }
3467  else
3468  {
3469  // check if the texture has the correct scale and if not, rescale it
3470  //std::pair<double, double> size = this->mpFontRenderer->getTextureSize(fontSpec, text);
3471  CLTextTextureSpec* pTexture = pos2->second;
3472 
3473  //std::cout << "Existing texture found: " << pTexture->mTextureName;
3474  if (pTexture != NULL && pTexture->mScale != this->mZoomFactor)
3475  {
3476  //std::cout << "Need to rescale texture." << std::endl;
3477  // we create a new larger texture
3478  double newScale = pTexture->mMaxScale;
3479 
3480  if (pTexture->mTextureName != 0)
3481  {
3482  glDeleteTextures(1, &pTexture->mTextureName);
3483  }
3484 
3485  delete pTexture;
3486  pos2->second = NULL;
3487 
3488  if (fabs(newScale + 1.0) < ALMOST_ZERO)
3489  {
3490  newScale = this->mZoomFactor;
3491  }
3492 
3493  std::pair<CLTextTextureSpec*, GLubyte*> texture = (*this->mpFontRenderer)(fontSpec.mFamily, fontSpec.mSize, text, fontSpec.mWeight, fontSpec.mStyle, newScale);
3494 
3495  // check if the texture has a size that is supported
3496  //std::cout << "Created texture at " << pTexture << " for text \"" << text << "\"" << std::endl;
3497  //std::cout << "texture id: " << pTexture->mTextureName << std::endl;
3498  if (texture.first != NULL && texture.second != NULL)
3499  {
3500  glGenTextures(1, &texture.first->mTextureName);
3501  assert(texture.first->mTextureName != 0);
3502  glBindTexture(GL_TEXTURE_2D, texture.first->mTextureName);
3503  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3504  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3505  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3506  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3507  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3508  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei)texture.first->mTextureWidth, (GLsizei)texture.first->mTextureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture.second);
3509  delete[] texture.second;
3510  }
3511 
3512  pos2->second = texture.first;
3513  //std::cout << "rescaled texture id: " << pTexture->mTextureName << std::endl;
3514  this->mTextGlyphMap[pTG] = texture.first;
3515  }
3516  else
3517  {
3518  //std::cout << "Using texture as is." << std::endl;
3519  this->mTextGlyphMap[pTG] = pos2->second;
3520  }
3521  }
3522  }
3523  else
3524  {
3525  maxDimension = (pBB->getDimensions().getWidth() > pBB->getDimensions().getHeight()) ? pBB->getDimensions().getWidth() : pBB->getDimensions().getHeight();
3526  // we also have to consider the zoom factor
3527  // if we do it here, we only have to multiply once
3528  maxDimension *= this->mZoomFactor;
3529  }
3530 
3531  this->update_textures_and_colors(it->second->getGroup(), maxDimension, pBB->getDimensions().getHeight());
3532  ++it;
3533  }
3534 
3535  // go through all line endings and find the colors and update the textures
3536  // line endings can contain images, gradients and/or text elements
3537  std::map<std::string, const CLLineEnding*>::const_iterator lineendingIt = this->mLineEndingMap.begin(), lineendingEndit = this->mLineEndingMap.end();
3538  double w = 0.0;
3539  double h = 0.0;
3540  const CLLineEnding* pLE = NULL;
3541 
3542  while (lineendingIt != lineendingEndit)
3543  {
3544  pLE = lineendingIt->second;
3545  w = pLE->getBoundingBox()->getDimensions().getWidth();
3546  h = pLE->getBoundingBox()->getDimensions().getHeight();
3547  this->update_textures_and_colors(pLE->getGroup(), ((w > h) ? w : h), h);
3548  ++lineendingIt;
3549  }
3550 
3551  // actually create the gradient textures
3552  //std::cout << "Creating gradient textures." << std::endl;
3553  std::map<std::string, std::pair<const CLGradientBase*, CLTextureSpec*> >::iterator textureIt = this->mGradientMap.begin(), textureEndit = this->mGradientMap.end();
3554  CLTextureSpec* pSpec = NULL;
3555  const CLGradientBase* pGradient = NULL;
3556 
3557  while (textureIt != textureEndit)
3558  {
3559  pSpec = textureIt->second.second;
3560 
3561  if (pSpec->mTextureName == 0)
3562  {
3563  pGradient = textureIt->second.first;
3564  //std::cout << "Creating texture for gradient: " << pGradient->getId() << std::endl;
3565  unsigned int exponent = (unsigned int)ceil(log(pSpec->mTextWidth) / log(2.0));
3566  // make sure a texture of this size is supported, else we lower the size
3567  unsigned int val = (1 << exponent);
3568  GLint width = 0;
3569  glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, val, val, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3570  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
3571 
3572  while (width == 0 && val > 1)
3573  {
3574  // divide the size by two in each direction
3575  val = (val >> 1);
3576  glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, val, val, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3577  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
3578  }
3579 
3580  if (width == 0)
3581  {
3582  // delete the texture since we can not draw it anyway
3583  //std::cout << "Could not create a texture." << std::endl;
3584  pSpec->mTextureName = 0;
3585  }
3586  else
3587  {
3588  if (val < (unsigned int)(1 << exponent))
3589  {
3590  // create a warning that the texture had to be scaled down
3591  std::cerr << "Gradient texture to large for current openGL implementation. Scaling down." << std::endl;
3592  }
3593 
3594  // create the texture
3595  pSpec->mTextureWidth = val;
3596  pSpec->mTextureHeight = val;
3597  pSpec->mTextWidth = val;
3598  pSpec->mTextHeight = val;
3599  pSpec->mTextureName = 0;
3600  GLubyte* textureData = NULL;
3601 
3602  try
3603  {
3604  glGenTextures(1, &pSpec->mTextureName);
3605  // make sure a usable texture name ha been created
3606  assert(pSpec->mTextureName != 0);
3608  GLubyte* textureData = new GLubyte[4 * val * val];
3609  create_gradient_texture(val, textureData, pGradient);
3610  glBindTexture(GL_TEXTURE_2D, pSpec->mTextureName);
3611  assert(pSpec->mTextureName != 0);
3612  //std::cout << "new texture created with id: " << pSpec->mTextureName << std::endl;
3613  //std::cout << "size: " << val << std::endl;
3614  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3615  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3616  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3617  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3618  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3619  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, val, val, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
3620  delete[] textureData;
3621  }
3622  catch (...)
3623  {
3624  if (textureData != NULL)
3625  {
3626  delete[] textureData;
3627  }
3628  }
3629  }
3630  }
3631 
3632  ++textureIt;
3633  }
3634 }
3635 
3636 /**
3637  * Goes through all render objects in the given group and updates the color
3638  * map and gradients.
3639  * The maxDimension parameter is the maximum of the width and the height of the
3640  * corresponding layout object. This value is needed to determine the size of gradient textures.
3641  */
3642 void CLLayoutRenderer::update_textures_and_colors(const CLGroup* pGroup, double maxDimension, double height, const std::string& parentFill, CLFontSpec parentFontSpec)
3643 {
3644  std::string strokeId;
3645  std::string fillId;
3646  std::string newParentFill = parentFill;
3647  CLFontSpec newParentFontSpec = this->getFontSpec(pGroup, height, parentFontSpec);
3648  std::string gradientId;
3649  const std::vector<unsigned int>* pDashArray;
3650  GLubyte colorA[4];
3651 
3652  if (pGroup->isSetFill())
3653  {
3654  fillId = pGroup->getFillColor();
3655 
3656  // the fill id can actually be the id of a gradient
3657  // try to resolve the color and add it to the map
3658  // if it can't be resolved it might be a gradient, so resolve the
3659  // gradient and create a CLText*ureSpec for it
3660  // if there already is a CLText*ureSpec object for this gradient, we might
3661  // have to update the size
3662  if (this->mColorMap.find(fillId) == this->mColorMap.end())
3663  {
3664  try
3665  {
3666  this->resolve_color(fillId, colorA);
3667  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
3668  this->mColorMap[fillId] = c;
3669  }
3670  catch (...)
3671  {
3672  // it could be a gradient
3673  if (this->mGradientMap.find(fillId) == this->mGradientMap.end())
3674  {
3675  const CLGradientBase* pGradient = mpResolver->getGradientBase(fillId);
3676  assert(pGradient);
3677  // add all colors in the gradient base to the color map
3678  this->update_colors(pGradient);
3679  CLTextureSpec* pSpec = new CLTextureSpec();
3680  pSpec->mTextureName = 0;
3681  pSpec->mTextWidth = pSpec->mTextHeight = 0;
3682  this->mGradientMap[fillId] = std::pair<const CLGradientBase*, CLTextureSpec*>(pGradient, pSpec);
3683  newParentFill = fillId;
3684  }
3685  }
3686  }
3687  }
3688 
3689  if (pGroup->isSetStroke())
3690  {
3691  strokeId = pGroup->getStroke();
3692 
3693  // this must be a color string or a color id
3694  // resolve the color and add it to the map
3695  if (this->mColorMap.find(strokeId) == this->mColorMap.end())
3696  {
3697  this->resolve_color(strokeId, colorA);
3698  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
3699  this->mColorMap[strokeId] = c;
3700  }
3701  }
3702 
3703  if (pGroup->isSetDashArray())
3704  {
3705  pDashArray = &(pGroup->getDashArray());
3706  // create the 1D texture for it if necessary
3707  std::map<const std::vector<unsigned int>, const CLLineStippleTexture*>::const_iterator pos = this->mLinestippleMap.find(*pDashArray);
3708 
3709  if (pos == this->mLinestippleMap.end())
3710  {
3711  const CLLineStippleTexture* pTexture = this->createLineStippleTexture(*pDashArray);
3712  this->mLinestippleMap[*pDashArray] = pTexture;
3713  }
3714  }
3715 
3716  if (pGroup->isSetStartHead())
3717  {
3718  std::string headId = pGroup->getStartHead();
3719 
3720  if (this->mLineEndingMap.find(headId) == this->mLineEndingMap.end())
3721  {
3722  const CLLineEnding* pLineEnding = this->mpResolver->getLineEnding(headId);
3723  assert(pLineEnding != NULL);
3724  this->mLineEndingMap[headId] = pLineEnding;
3725  }
3726  }
3727 
3728  if (pGroup->isSetEndHead())
3729  {
3730  std::string headId = pGroup->getEndHead();
3731 
3732  if (this->mLineEndingMap.find(headId) == this->mLineEndingMap.end())
3733  {
3734  const CLLineEnding* pLineEnding = this->mpResolver->getLineEnding(headId);
3735  assert(pLineEnding != NULL);
3736  this->mLineEndingMap[headId] = pLineEnding;
3737  }
3738  }
3739 
3740  const CLGraphicalPrimitive1D* pP1D = NULL;
3741 
3742  const CLGraphicalPrimitive2D* pP2D = NULL;
3743 
3744  const CLText* pText = NULL;
3745 
3746  const CLGroup* pChildGroup = NULL;
3747 
3748  const CLRenderCurve* pCurve = NULL;
3749 
3750  size_t i, iMax = pGroup->getNumElements();
3751 
3752  for (i = 0; i < iMax; ++i)
3753  {
3754  // if elements don't set their own stroke or fill color, the inherit it
3755  // from their parent group
3756  // the same is true for the line stipple
3757  pP1D = dynamic_cast<const CLGraphicalPrimitive1D*>(pGroup->getElement(i));
3758 
3759  if (pP1D != NULL)
3760  {
3761  // the object can have a stroke color and a line stipple
3762  pP2D = dynamic_cast<const CLGraphicalPrimitive2D*>(pP1D);
3763  pText = dynamic_cast<const CLText*>(pP1D);
3764  pCurve = dynamic_cast<const CLRenderCurve*>(pP1D);
3765 
3766  if (pP2D != NULL)
3767  {
3768  pChildGroup = dynamic_cast<const CLGroup*>(pP2D);
3769 
3770  if (pChildGroup != NULL)
3771  {
3772  // recursively call this method
3773  this->update_textures_and_colors(pChildGroup, maxDimension, height, newParentFill);
3774  }
3775  else
3776  {
3777  if (pP2D->isSetStroke())
3778  {
3779  strokeId = pP2D->getStroke();
3780 
3781  // this must be a color string or a color id
3782  // resolve the color and add it to the map
3783  if (this->mColorMap.find(strokeId) == this->mColorMap.end())
3784  {
3785  this->resolve_color(strokeId, colorA);
3786  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
3787  this->mColorMap[strokeId] = c;
3788  }
3789  }
3790 
3791  if (pP2D->isSetDashArray())
3792  {
3793  pDashArray = &pP2D->getDashArray();
3794  // create the 1D texture for it if necessary
3795  std::map<const std::vector<unsigned int>, const CLLineStippleTexture*>::const_iterator pos = this->mLinestippleMap.find(*pDashArray);
3796 
3797  if (pos == this->mLinestippleMap.end())
3798  {
3799  const CLLineStippleTexture* pTexture = this->createLineStippleTexture(*pDashArray);
3800  this->mLinestippleMap[*pDashArray] = pTexture;
3801  }
3802  }
3803 
3804  if (pP2D->isSetFill())
3805  {
3806  fillId = pP2D->getFillColor();
3807 
3808  // the fill id can actually be the id of a gradient
3809  // try to resolve the color and add it to the map
3810  // if it can't be resolved it might be a gradient, so resolve the
3811  // gradient and create a CLText*ureSpec for it
3812  // if there already is a CLText*ureSpec object for this gradient, we might
3813  // have to update the size
3814  if (this->mColorMap.find(fillId) == this->mColorMap.end())
3815  {
3816  try
3817  {
3818  this->resolve_color(fillId, colorA);
3819  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
3820  this->mColorMap[fillId] = c;
3821  }
3822  catch (...)
3823  {
3824  // it could be a gradient
3825  std::map<std::string, std::pair<const CLGradientBase*, CLTextureSpec*> >::iterator pos = this->mGradientMap.find(fillId);
3826 
3827  if (pos == this->mGradientMap.end())
3828  {
3829  // we have to create a new CLText*ureSpec
3830  const CLGradientBase* pGradient = mpResolver->getGradientBase(fillId);
3831  assert(pGradient);
3832  // add all colors in the gradient base to the color map
3833  this->update_colors(pGradient);
3834  CLTextureSpec* pSpec = new CLTextureSpec();
3835  pSpec->mTextureName = 0;
3836  pSpec->mTextWidth = pSpec->mTextHeight = maxDimension;
3837  this->mGradientMap[fillId] = std::pair<const CLGradientBase*, CLTextureSpec*>(pGradient, pSpec);
3838  }
3839  else
3840  {
3841  if (maxDimension > pos->second.second->mTextWidth)
3842  {
3843  // update the dimensions of the texture
3844  pos->second.second->mTextWidth = pos->second.second->mTextHeight = maxDimension;
3845 
3846  // delete the old data if necessary
3847  if (pos->second.second->mTextureName != 0)
3848  {
3849  glDeleteTextures(1, &pos->second.second->mTextureName);
3850  pos->second.second->mTextureName = 0;
3851  }
3852  }
3853  }
3854  }
3855  }
3856  }
3857  else if (!newParentFill.empty())
3858  {
3859  // we might have inherited a fillId from the parent
3860  // group so we might have to update the size of the
3861  // texture
3862  std::map<std::string, std::pair<const CLGradientBase*, CLTextureSpec*> >::iterator pos = this->mGradientMap.find(fillId);
3863 
3864  if (pos != this->mGradientMap.end() && maxDimension > pos->second.second->mTextWidth)
3865  {
3866  pos->second.second->mTextWidth = pos->second.second->mTextHeight = maxDimension;
3867 
3868  if (pos->second.second->mTextureName != 0)
3869  {
3870  glDeleteTextures(1, &pos->second.second->mTextureName);
3871  pos->second.second->mTextureName = 0;
3872  }
3873  }
3874  }
3875  }
3876  }
3877  else if (pText != NULL)
3878  {
3879  if (pText->isSetStroke())
3880  {
3881  strokeId = pText->getStroke();
3882 
3883  // this must be a color string or a color id
3884  // resolve the color and add it to the map
3885  if (this->mColorMap.find(strokeId) == this->mColorMap.end())
3886  {
3887  this->resolve_color(strokeId, colorA);
3888  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
3889  this->mColorMap[strokeId] = c;
3890  }
3891  }
3892 
3893  std::string text = pText->getText();
3894  // get the font spec for the group
3895  CLFontSpec fontSpec = this->getFontSpec(pText, height, newParentFontSpec);
3896  std::map<CLFontSpec, std::map<std::string, CLTextTextureSpec*> >::iterator pos = this->mFontTextureMap.find(fontSpec);
3897 
3898  if (pos == this->mFontTextureMap.end())
3899  {
3900  std::map<std::string, CLTextTextureSpec*> s;
3901  this->mFontTextureMap[fontSpec] = s;
3902  pos = this->mFontTextureMap.find(fontSpec);
3903  assert(pos != this->mFontTextureMap.end());
3904  }
3905 
3906  std::map<std::string, CLTextTextureSpec*>::iterator pos2 = pos->second.find(text);
3907 
3908  if (pos2 == pos->second.end())
3909  {
3910  // add the texture although it might be NULL
3911  std::pair<CLTextTextureSpec*, GLubyte*> texture = (*this->mpFontRenderer)(fontSpec.mFamily, fontSpec.mSize, text, fontSpec.mWeight, fontSpec.mStyle, this->mZoomFactor);
3912 
3913  //std::cout << "No texture found. Created texture at " << pTexture << " for text \"" << text << "\"" << std::endl;
3914  //std::cout << "texture id: " << pTexture->mTextureName << std::endl;
3915  if (texture.first != NULL && texture.second != NULL)
3916  {
3917  glGenTextures(1, &texture.first->mTextureName);
3918  assert(texture.first->mTextureName != 0);
3919  glBindTexture(GL_TEXTURE_2D, texture.first->mTextureName);
3920  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3921  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3922  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3923  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3924  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3925  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei)texture.first->mTextureWidth, (GLsizei)texture.first->mTextureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture.second);
3926  delete[] texture.second;
3927  }
3928 
3929  pos->second[text] = texture.first;
3930  this->mTextMap[pText] = texture.first;
3931  }
3932  else
3933  {
3934  // check if the texture is large enough
3935  //std::pair<double, double> size = this->mpFontRenderer->getTextureSize(fontSpec, text);
3936  CLTextTextureSpec* pTexture = pos2->second;
3937 
3938  if (pTexture != NULL && pTexture->mScale != this->mZoomFactor)
3939  {
3940  //std::cout << "We create a larger texture for texture at " << pTexture << "." << std::endl;
3941  // we create a new larger texture
3942  double newScale = pTexture->mMaxScale;
3943 
3944  if (pTexture->mTextureName != 0)
3945  {
3946  //std::cout << "We delete the current OpenGL texture:" << pTexture->mTextureName << std::endl;
3947  glDeleteTextures(1, &pTexture->mTextureName);
3948  }
3949 
3950  delete pTexture;
3951  pTexture = NULL;
3952  pos2->second = NULL;
3953 
3954  if (fabs(newScale + 1.0) < ALMOST_ZERO)
3955  {
3956  newScale = this->mZoomFactor;
3957  }
3958 
3959  std::pair<CLTextTextureSpec*, GLubyte*> texture = (*this->mpFontRenderer)(fontSpec.mFamily, fontSpec.mSize, text, fontSpec.mWeight, fontSpec.mStyle, newScale);
3960 
3961  //std::cout << "Created texture at " << pTexture << " for text \"" << text << "\"" << std::endl;
3962  //std::cout << "texture id: " << pTexture->mTextureName << std::endl;
3963  if (texture.first != NULL && texture.second != NULL)
3964  {
3965  glGenTextures(1, &texture.first->mTextureName);
3966  assert(texture.first->mTextureName != 0);
3967  glBindTexture(GL_TEXTURE_2D, texture.first->mTextureName);
3968  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3969  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3970  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3971  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3972  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3973  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei)texture.first->mTextureWidth, (GLsizei)texture.first->mTextureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture.second);
3974  delete[] texture.second;
3975  }
3976 
3977  pos2->second = texture.first;
3978  this->mTextMap[pText] = texture.first;
3979  }
3980  else
3981  {
3982  //std::cout << "We are reusing the texture at " << pos2->second << std::endl;
3983  this->mTextMap[pText] = pos2->second;
3984  }
3985  }
3986  }
3987  else
3988  {
3989  if (pP1D->isSetStroke())
3990  {
3991  strokeId = pP1D->getStroke();
3992 
3993  // this must be a color string or a color id
3994  // resolve the color and add it to the map
3995  if (this->mColorMap.find(strokeId) == this->mColorMap.end())
3996  {
3997  this->resolve_color(strokeId, colorA);
3998  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
3999  this->mColorMap[strokeId] = c;
4000  }
4001  }
4002 
4003  if (pP1D->isSetDashArray())
4004  {
4005  pDashArray = &pP1D->getDashArray();
4006  // create the 1D texture for it if necessary
4007  std::map<const std::vector<unsigned int>, const CLLineStippleTexture*>::const_iterator pos = this->mLinestippleMap.find(*pDashArray);
4008 
4009  if (pos == this->mLinestippleMap.end())
4010  {
4011  const CLLineStippleTexture* pTexture = this->createLineStippleTexture(*pDashArray);
4012  this->mLinestippleMap[*pDashArray] = pTexture;
4013  }
4014  }
4015 
4016  if (pCurve != NULL)
4017  {
4018  if (pCurve->isSetStartHead())
4019  {
4020  std::string headId = pCurve->getStartHead();
4021 
4022  if (this->mLineEndingMap.find(headId) == this->mLineEndingMap.end())
4023  {
4024  const CLLineEnding* pLineEnding = this->mpResolver->getLineEnding(headId);
4025  assert(pLineEnding != NULL);
4026  this->mLineEndingMap[headId] = pLineEnding;
4027  }
4028  }
4029 
4030  if (pCurve->isSetEndHead())
4031  {
4032  std::string headId = pCurve->getEndHead();
4033 
4034  if (this->mLineEndingMap.find(headId) == this->mLineEndingMap.end())
4035  {
4036  const CLLineEnding* pLineEnding = this->mpResolver->getLineEnding(headId);
4037  assert(pLineEnding != NULL);
4038  this->mLineEndingMap[headId] = pLineEnding;
4039  }
4040  }
4041  }
4042  }
4043  }
4044  }
4045 }
4046 
4047 /**
4048  * This method creates a FontSpec object from the font attributes in the given
4049  * group.
4050  * The height that is passed to the method is the height for the bounding box
4051  * and it used to calculate the font size if it has a relative component.
4052  */
4053 CLFontSpec CLLayoutRenderer::getFontSpec(const CLGroup* pGroup, double boxHeight, const CLFontSpec& parentFontSpec)
4054 {
4055  CLFontSpec spec(parentFontSpec);
4056 
4057  if (pGroup->isSetFontFamily())
4058  {
4059  spec.mFamily = pGroup->getFontFamily();
4060  }
4061 
4062  if (pGroup->isSetFontSize())
4063  {
4064  spec.mSize = pGroup->getFontSize().getAbsoluteValue() + pGroup->getFontSize().getRelativeValue() / 100.0 * boxHeight;
4065  }
4066 
4067  if (pGroup->isSetFontWeight())
4068  {
4069  spec.mWeight = pGroup->getFontWeight();
4070  }
4071 
4072  if (pGroup->isSetFontStyle())
4073  {
4074  spec.mStyle = pGroup->getFontStyle();
4075  }
4076 
4077  return spec;
4078 }
4079 
4080 /**
4081  * This method creates a FontSpec object from the font attributes in the given
4082  * text element.
4083  * The height that is passed to the method is the height for the bounding box
4084  * and it used to calculate the font size if it has a relative component.
4085  */
4086 CLFontSpec CLLayoutRenderer::getFontSpec(const CLText* pText, double boxHeight, const CLFontSpec& parentFontSpec)
4087 {
4088  CLFontSpec spec(parentFontSpec);
4089 
4090  if (pText->isSetFontFamily())
4091  {
4092  spec.mFamily = pText->getFontFamily();
4093  }
4094 
4095  if (pText->isSetFontSize())
4096  {
4097  spec.mSize = pText->getFontSize().getAbsoluteValue() + pText->getFontSize().getRelativeValue() / 100.0 * boxHeight;
4098  }
4099 
4100  if (pText->isSetFontWeight())
4101  {
4102  spec.mWeight = pText->getFontWeight();
4103  }
4104 
4105  if (pText->isSetFontStyle())
4106  {
4107  spec.mStyle = pText->getFontStyle();
4108  }
4109 
4110  return spec;
4111 }
4112 
4113 /**
4114  * This method sets the left edge of the viewport.
4115  */
4117 {
4118  this->mX = x;
4119 }
4120 
4121 /**
4122  * This method sets the upper edge of the viewport.
4123  */
4125 {
4126  this->mY = y;
4127 }
4128 
4129 /**
4130  * This methods extracts all colors from the given gradient and adds them to the
4131  * color map.
4132  */
4134 {
4135  GLubyte colorA[4];
4136  size_t i, iMax = pGradient->getNumGradientStops();
4137  const CLGradientStop* pStop = NULL;
4138 
4139  for (i = 0; i < iMax; ++i)
4140  {
4141  pStop = pGradient->getGradientStop(i);
4142  std::string colorString = pStop->getStopColor();
4143 
4144  if (!colorString.empty())
4145  {
4146  this->resolve_color(colorString, colorA);
4147  CLRGBAColor c = {colorA[0], colorA[1], colorA[2], colorA[3]};
4148  this->mColorMap[colorString] = c;
4149  }
4150  }
4151 }
4152 
4154 {
4155  this->mStateList.push(this->mCurrentAttributes);
4156 }
4157 
4159 {
4160  this->mCurrentAttributes = this->mStateList.top();
4161  this->mStateList.pop();
4162 }
4163 
4164 void CLLayoutRenderer::draw_cap(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double stroke_width)
4165 {
4166  // calculate the direction vector
4167  double vx1 = x2 - x1;
4168  double vy1 = y2 - y1;
4169  double vz1 = z2 - z1;
4170  double length = sqrt(vx1 * vx1 + vy1 * vy1 + vz1 * vz1);
4171 
4172  double normLength1, normLength2;
4173 
4174  // calculate the normal to this vector
4175  double vx2 = 0.0;
4176  double vy2 = 0.0;
4177  double vz2 = 0.0;
4178  double half_width = stroke_width / 2.0;
4179 
4180  if (fabs(vx1) < ALMOST_ZERO && vz1 < ALMOST_ZERO)
4181  {
4182  // scale by the stroke_width
4183  vx2 = -vy1 / length * half_width;
4184  vy2 = 0.0;
4185  vz2 = 0.0;
4186  }
4187  else
4188  {
4189  // scale by the stroke_width
4190  double normY = vy1 / length;
4191  vx2 = -normY * vx1 / length * half_width;
4192  vy2 = (1 - normY * normY) * half_width;
4193  vz2 = -normY * vz1 / length * half_width;
4194  normLength1 = half_width / sqrt(vx2 * vx2 + vy2 * vy2 + vz2 * vz2);
4195  vx2 *= normLength1;
4196  vy2 *= normLength1;
4197  vz2 *= normLength1;
4198  }
4199 
4200  // calculate the direction vector
4201  double vx3 = x3 - x2;
4202  double vy3 = y3 - y2;
4203  double vz3 = z3 - z2;
4204  double length2 = sqrt(vx3 * vx3 + vy3 * vy3 + vz3 * vz3);
4205 
4206  // calculate the normal to this vector
4207  double vx4 = 0.0;
4208  double vy4 = 0.0;
4209  double vz4 = 0.0;
4210 
4211  if (fabs(vx3) < ALMOST_ZERO && vz3 < ALMOST_ZERO)
4212  {
4213  // scale by the stroke_width
4214  vx4 = -vy3 / length2 * half_width;
4215  vy4 = 0.0;
4216  vz4 = 0.0;
4217  }
4218  else
4219  {
4220  // scale by the stroke_width
4221  double normY = vy3 / length2;
4222  vx4 = -normY * vx3 / length2 * half_width;
4223  vy4 = (1 - normY * normY) * half_width;
4224  vz4 = -normY * vz3 / length2 * half_width;
4225  normLength2 = half_width / sqrt(vx4 * vx4 + vy4 * vy4 + vz4 * vz4);
4226  vx4 *= normLength2;
4227  vy4 *= normLength2;
4228  vz4 *= normLength2;
4229  }
4230 
4231  double length3 = sqrt(pow(x3 - x1, 2) + pow(y3 - y1, 2) + pow(z3 - z1, 2));
4232  double angle = (vx2 * vx4 + vy2 * vy4 + vz2 * vz4) / (sqrt(vx2 * vx2 + vy2 * vy2 + vz2 * vz2) * sqrt(vx4 * vx4 + vy4 * vy4 + vz4 * vz4));
4233  angle = acos(angle);
4234 
4235  // if the angle is not +/-0 or +/-180°
4236  if ((fabs(angle) > ALMOST_ZERO) && (fabs((fabs(angle) - M_PI)) / M_PI > ALMOST_ZERO))
4237  {
4238  // if the angle is greater 0, the gap is at the lower edge
4239  // else the gap is at the upper edge
4240  // calculate the center of the first line segment box
4241  // in order to draw the cap, we have to find the box point at the start
4242  // edge of the second segment that is not inside the box determined by the first segment.
4243  // This is the point that is further away from the center of the first box.
4244  double xx = (x2 + x1) / 2.0;
4245  double yy = (y2 + y1) / 2.0;
4246  double zz = (z2 + z1) / 2.0;
4247  double x21 = x2 + vx4;
4248  double y21 = y2 + vy4;
4249  double z21 = z2 + vz4;
4250  double x22 = x2 - vx4;
4251  double y22 = y2 - vy4;
4252  double z22 = z2 - vz4;
4253  double distance1 = sqrt(pow(x21 - xx, 2) + pow(y21 - yy, 2) + pow(z21 - zz, 2));
4254 
4255  if (distance1 > sqrt(pow(x22 - xx, 2) + pow(y22 - yy, 2) + pow(z22 - zz, 2)))
4256  {
4257  xx = (x3 + x2) / 2.0;
4258  yy = (y3 + y2) / 2.0;
4259  zz = (y3 + y2) / 2.0;
4260  double x23 = x2 + vx2;
4261  double y23 = y2 + vy2;
4262  double z23 = z2 + vz2;
4263  x22 = x2 - vx2;
4264  y22 = y2 - vy2;
4265  z22 = z2 - vz2;
4266  double distance1 = sqrt(pow(x23 - xx, 2) + pow(y23 - yy, 2) + pow(z23 - zz, 2));
4267 
4268  if (distance1 > sqrt(pow(x22 - xx, 2) + pow(y22 - yy, 2) + pow(z22 - zz, 2)))
4269  {
4270  if (angle < M_PI / 2.0 && (length3 > length && length3 > length2))
4271  {
4272  glBegin(GL_TRIANGLES);
4273  glVertex3d(x2, y2, z2);
4274  glVertex3d(x21, y21, z21);
4275  glVertex3d(x23, y23, z23);
4276  glEnd();
4277  }
4278  else
4279  {
4280  // we need to draw two triangles
4281  const CLPoint* pP = CLLayoutRenderer::calculate_intersection(x21, y21, z21, vx3, vy3, vz3, x23, y23, z23, vx1, vy1, vz1);
4282  glBegin(GL_TRIANGLE_FAN);
4283  glVertex3d(pP->getX(), pP->getY(), pP->getZ());
4284  glVertex3d(x21, y21, z21);
4285  glVertex3d(x2, y2, z2);
4286  glVertex3d(x23, y23, z23);
4287  glEnd();
4288  delete pP;
4289  }
4290  }
4291  else
4292  {
4293  if (angle < M_PI / 2.0 && (length3 > length && length3 > length2))
4294  {
4295  glBegin(GL_TRIANGLES);
4296  glVertex3d(x2, y2, z2);
4297  glVertex3d(x21, y21, z21);
4298  glVertex3d(x22, y22, z22);
4299  glEnd();
4300  }
4301  else
4302  {
4303  // we need to draw two triangles
4304  const CLPoint* pP = CLLayoutRenderer::calculate_intersection(x21, y21, z21, vx3, vy3, vz3, x22, y22, z22, vx1, vy1, vz1);
4305  glBegin(GL_TRIANGLE_FAN);
4306  glVertex3d(pP->getX(), pP->getY(), pP->getZ());
4307  glVertex3d(x21, y21, z21);
4308  glVertex3d(x2, y2, z2);
4309  glVertex3d(x22, y22, z22);
4310  glEnd();
4311  delete pP;
4312  }
4313  }
4314  }
4315  else
4316  {
4317  xx = (x3 + x2) / 2.0;
4318  yy = (y3 + y2) / 2.0;
4319  zz = (y3 + y2) / 2.0;
4320  double x23 = x2 + vx2;
4321  double y23 = y2 + vy2;
4322  double z23 = z2 + vz2;
4323  x21 = x2 - vx2;
4324  y21 = y2 - vy2;
4325  z21 = z2 - vz2;
4326  double distance1 = sqrt(pow(x23 - xx, 2) + pow(y23 - yy, 2) + pow(z23 - zz, 2));
4327 
4328  if (distance1 > sqrt(pow(x21 - xx, 2) + pow(y21 - yy, 2) + pow(z21 - zz, 2)))
4329  {
4330  if (angle < M_PI / 2.0 && length3 > length && length3 > length2)
4331  {
4332  glBegin(GL_TRIANGLES);
4333  glVertex3d(x2, y2, z2);
4334  glVertex3d(x22, y22, z22);
4335  glVertex3d(x23, y23, z23);
4336  glEnd();
4337  }
4338  else
4339  {
4340  // we need to draw two triangles
4341  const CLPoint* pP = CLLayoutRenderer::calculate_intersection(x22, y22, z22, vx3, vy3, vz3, x23, y23, z23, vx1, vy1, vz1);
4342  glBegin(GL_TRIANGLE_FAN);
4343  glVertex3d(pP->getX(), pP->getY(), pP->getZ());
4344  glVertex3d(x22, y22, z22);
4345  glVertex3d(x2, y2, z2);
4346  glVertex3d(x23, y23, z23);
4347  glEnd();
4348  delete pP;
4349  }
4350  }
4351  else
4352  {
4353  if (angle < M_PI / 2.0 && length3 > length && length3 > length2)
4354  {
4355  glBegin(GL_TRIANGLES);
4356  glVertex3d(x2, y2, z2);
4357  glVertex3d(x22, y22, z22);
4358  glVertex3d(x21, y21, z21);
4359  glEnd();
4360  }
4361  else
4362  {
4363  // we need to draw two triangles
4364  const CLPoint* pP = CLLayoutRenderer::calculate_intersection(x22, y22, z22, vx3, vy3, vz3, x21, y21, z21, vx1, vy1, vz1);
4365  glBegin(GL_TRIANGLE_FAN);
4366  glVertex3d(pP->getX(), pP->getY(), pP->getZ());
4367  glVertex3d(x22, y22, z22);
4368  glVertex3d(x2, y2, z2);
4369  glVertex3d(x21, y21, z21);
4370  glEnd();
4371  delete pP;
4372  }
4373  }
4374  }
4375  }
4376 }
4377 
4379 {
4380  std::map<std::string, std::pair<const CLGradientBase*, CLTextureSpec*> >::iterator it1 = this->mGradientMap.begin(), endit1 = this->mGradientMap.end();
4381 
4382  while (it1 != endit1)
4383  {
4384  if (it1->second.second != NULL)
4385  {
4386  if (it1->second.second->mTextureName != 0)
4387  {
4388  glDeleteTextures(1, &it1->second.second->mTextureName);
4389  }
4390 
4391  delete it1->second.second;
4392  }
4393 
4394  ++it1;
4395  }
4396 
4397  this->mGradientMap.clear();
4398  std::map<CLFontSpec, std::map<std::string, CLTextTextureSpec*> >::iterator it2 = this->mFontTextureMap.begin(), endit2 = this->mFontTextureMap.end();
4399 
4400  while (it2 != endit2)
4401  {
4402  std::map<std::string, CLTextTextureSpec*>::const_iterator it3 = it2->second.begin(), endit3 = it2->second.end();
4403 
4404  while (it3 != endit3)
4405  {
4406  if (it3->second != NULL)
4407  {
4408  if (it3->second->mTextureName != 0)
4409  {
4410  glDeleteTextures(1, &it3->second->mTextureName);
4411  }
4412 
4413  delete it3->second;
4414  }
4415 
4416  ++it3;
4417  }
4418 
4419  it2->second.clear();
4420  ++it2;
4421  }
4422 
4423  this->mFontTextureMap.clear();
4424  std::map<const std::vector<unsigned int>, const CLLineStippleTexture*>::const_iterator it3 = this->mLinestippleMap.begin(), endit3 = this->mLinestippleMap.end();
4425 
4426  while (it3 != endit3)
4427  {
4428  if (it3->second != NULL)
4429  {
4430  delete it3->second;
4431  }
4432 
4433  ++it3;
4434  }
4435 
4436  this->mLinestippleMap.clear();
4437  // delete all image textures
4438  std::map<std::string, const CLTextureSpec*>::iterator it4 = this->mImageMap.begin(), endit4 = this->mImageMap.end();
4439 
4440  while (it4 != endit4)
4441  {
4442  if (it4->second != NULL)
4443  {
4444  if (it4->second->mTextureName != 0)
4445  {
4446  glDeleteTextures(1, &it4->second->mTextureName);
4447  }
4448 
4449  delete it4->second;
4450  }
4451 
4452  ++it4;
4453  }
4454 
4455  this->mImageMap.clear();
4456  this->mColorMap.clear();
4457  this->mStyleMap.clear();
4458  this->mDrawables.clear();
4459  this->mLineEndingMap.clear();
4460  this->mTextGlyphMap.clear();
4461  this->mTextMap.clear();
4462 }
4463 
4464 /**
4465  * Calculates the intersection point between two lines in 2D.
4466  * The intersection point is returned.
4467  * If the lines are parallel, a point with two NaN values is returned.
4468  * All numbers <= ALMOST_ZERO are considered to be 0.
4469  */
4470 std::pair<double, double> CLLayoutRenderer::calculate_intersection_point_2d(double p1x, double p1y, double sx, double sy, double p2x, double p2y, double tx, double ty)
4471 {
4472  std::pair<double, double> result = std::pair<double, double>(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
4473 
4474  if (fabs(sx) <= ALMOST_ZERO)
4475  {
4476  // two options -> a) tx is also 0.0 -> parallel lines
4477  // b) intersection is at p1x,??
4478  if (fabs(tx) > ALMOST_ZERO)
4479  {
4480  result = std::pair<double, double>(p1x, (p1x - p2x) * (ty / tx) + p2y);
4481  }
4482  }
4483  else if (fabs(sy) <= ALMOST_ZERO)
4484  {
4485  // two options a) ty is also 0.0 -> parallel lines
4486  // b) intersection at ??,p1y
4487 
4488  if (fabs(tx) > ALMOST_ZERO)
4489  {
4490  if (fabs(ty) > ALMOST_ZERO)
4491  {
4492  result = std::pair<double, double>((p1y - p2y) * (tx / ty) - p2x, p1y);
4493  }
4494  }
4495  else
4496  {
4497  result = std::pair<double, double>(p2x, p1y);
4498  }
4499  }
4500  else if (fabs(tx) <= ALMOST_ZERO)
4501  {
4502  // intersection is at p2x,??
4503  if (fabs(sy) > ALMOST_ZERO)
4504  {
4505  result = std::pair<double, double>(p2x, (p2x - p1x) * (sy / sx) + p1y);
4506  }
4507  }
4508  else if (fabs(ty) <= ALMOST_ZERO)
4509  {
4510  // intersection at ??,p2y
4511  result = std::pair<double, double>((p2y - p1y) * (sx / sy) - p1x, p2y);
4512  }
4513  else
4514  {
4515  // most general case
4516  // two options a) lines are parallel
4517  // b) intersection at
4518  //
4519  // check if the lines are parallel
4520  if (fabs(fabs((sx * tx + sy * ty) / (sqrt(sx * sx + sy * sy)*sqrt(tx * tx + ty * ty))) - 1) > ALMOST_ZERO)
4521  {
4522  double ss = sy / sx;
4523  double st = ty / tx;
4524  double ys = p1y - p1x * ss;
4525  double yt = p2y - p2x * st;
4526  // if not, the intersection is at
4527  double x = (ss - st) / (yt - ys);
4528  double y = ss * x + ys;
4529  result = std::pair<double, double>(x, y);
4530  }
4531  }
4532 
4533  return result;
4534 }
4535 
4536 /**
4537  * Calculates wether 2d segments intersect within the length of the segments.
4538  * Calls calculate_intersection_point_2d.
4539  */
4540 bool CLLayoutRenderer::segments_intersect_2d(double p1x1, double p1y1, double p1x2, double p1y2, double p2x1, double p2y1, double p2x2, double p2y2)
4541 {
4542  bool result = false;
4543  // calculate the slope deltas from the endpoints
4544  double sx = p1x2 - p1x1;
4545  double sy = p1y2 - p1y1;
4546  double tx = p2x2 - p2x1;
4547  double ty = p2y2 - p2y1;
4548 
4549  // check that the segments have a length
4550  if ((fabs(sx) > ALMOST_ZERO || fabs(sy) > ALMOST_ZERO) && (fabs(tx) > ALMOST_ZERO || fabs(ty) > ALMOST_ZERO))
4551  {
4552  std::pair<double, double> p = CLLayoutRenderer::calculate_intersection_point_2d(p1x1, p1y1, sx, sy, p2x1, p2y1, tx, ty);
4553 
4554  // check for NaN results -> no intersection
4555  if (p.first == p.first && p.second == p.second)
4556  {
4557  double max_x1 = (p1x1 > p1x2) ? p1x1 : p1x2;
4558  double max_x2 = (p2x1 > p2x2) ? p2x1 : p2x2;
4559  double max_y1 = (p1y1 > p1y2) ? p1y1 : p1y2;
4560  double max_y2 = (p2y1 > p2y2) ? p2y1 : p2y2;
4561 
4562  double min_x1 = (p1x1 <= p1x2) ? p1x1 : p1x2;
4563  double min_x2 = (p2x1 <= p2x2) ? p2x1 : p2x2;
4564  double min_y1 = (p1y1 <= p1y2) ? p1y1 : p1y2;
4565  double min_y2 = (p2y1 <= p2y2) ? p2y1 : p2y2;
4566  result = (p.first >= min_x1 && p.first >= min_x2 && p.first <= max_x1 && p.first <= max_x2 &&
4567  p.second >= min_y1 && p.second >= min_y2 && p.second <= max_y1 && p.second <= max_y2);
4568  }
4569  }
4570 
4571  return result;
4572 }
4573 
4574 CLPoint* CLLayoutRenderer::calculate_intersection(double p1x, double p1y, double p1z, double sx, double sy, double sz,
4575  double p2x, double p2y, double p2z, double tx, double ty, double tz)
4576 {
4577  CLPoint* pP = NULL;
4578 
4579  if (fabs(sx) > ALMOST_ZERO)
4580  {
4581  double m = (p1y - p2y + ((p2x / sx) - (p1x / sx)) * sy) / (ty - tx * sy / sx);
4582  double n = (p2x + m * tx - p1x) / sx;
4583  pP = new CLPoint(p2x + m * tx, p2y + m * ty, p2z + m * tz);
4584  CLPoint* pP2 = new CLPoint(p1x + n * sx, p1y + n * sy, p1z + n * sz);
4585 
4586  if (fabs(pP->getX()) > ALMOST_ZERO)
4587  {
4588  assert(fabs((pP->getX() - pP2->getX()) / pP->getX()) < 1e-6);
4589  }
4590  else
4591  {
4592  assert(fabs(pP2->getX()) < ALMOST_ZERO);
4593  }
4594 
4595  if (fabs(pP->getY()) > ALMOST_ZERO)
4596  {
4597  assert(fabs((pP->getY() - pP2->getY()) / pP->getY()) < 1e-6);
4598  }
4599  else
4600  {
4601  assert(fabs(pP2->getY()) < ALMOST_ZERO);
4602  }
4603 
4604  if (fabs(pP->getZ()) > ALMOST_ZERO)
4605  {
4606  assert(fabs((pP->getZ() - pP2->getZ()) / pP->getZ()) < 1e-6);
4607  }
4608  else
4609  {
4610  assert(fabs(pP2->getZ()) < ALMOST_ZERO);
4611  }
4612 
4613  delete pP2;
4614  }
4615  else if (fabs(tx) > ALMOST_ZERO)
4616  {
4617  // first line is in the yz plane or a plane parallel to it
4618  // the intersection must be at p2x=p1x
4619  double m = (p1x - p2x) / tx;
4620  pP = new CLPoint(p2x + m * tx, p2y + m * ty, p2z + m * tz);
4621 
4622  if (fabs(sy) > ALMOST_ZERO)
4623  {
4624  double n = (pP->getY() - p1y) / sy;
4625  CLPoint* pP2 = new CLPoint(p1x + n * sx, p1y + n * sy, p1z + n * sz);
4626 
4627  if (fabs(pP->getX()) > ALMOST_ZERO)
4628  {
4629  assert(fabs((pP->getX() - pP2->getX()) / pP->getX()) < 1e-6);
4630  }
4631  else
4632  {
4633  assert(fabs(pP2->getX()) < ALMOST_ZERO);
4634  }
4635 
4636  if (fabs(pP->getY()) > ALMOST_ZERO)
4637  {
4638  assert(fabs((pP->getY() - pP2->getY()) / pP->getY()) < 1e-6);
4639  }
4640  else
4641  {
4642  assert(fabs(pP2->getY()) < ALMOST_ZERO);
4643  }
4644 
4645  if (fabs(pP->getZ()) > ALMOST_ZERO)
4646  {
4647  assert(fabs((pP->getZ() - pP2->getZ()) / pP->getZ()) < 1e-6);
4648  }
4649  else
4650  {
4651  assert(fabs(pP2->getZ()) < ALMOST_ZERO);
4652  }
4653 
4654  delete pP2;
4655  }
4656  else if (fabs(sz) > ALMOST_ZERO)
4657  {
4658  double n = (pP->getZ() - p1z) / sz;
4659  CLPoint* pP2 = new CLPoint(p1x + n * sx, p1y + n * sy, p1z + n * sz);
4660 
4661  if (fabs(pP->getX()) > ALMOST_ZERO)
4662  {
4663  assert(fabs((pP->getX() - pP2->getX()) / pP->getX()) < 1e-6);
4664  }
4665  else
4666  {
4667  assert(fabs(pP2->getX()) < ALMOST_ZERO);
4668  }
4669 
4670  if (fabs(pP->getY()) > ALMOST_ZERO)
4671  {
4672  assert(fabs((pP->getY() - pP2->getY()) / pP->getY()) < 1e-6);
4673  }
4674  else
4675  {
4676  assert(fabs(pP2->getY()) < ALMOST_ZERO);
4677  }
4678 
4679  if (fabs(pP->getZ()) > ALMOST_ZERO)
4680  {
4681  assert(fabs((pP->getZ() - pP2->getZ()) / pP->getZ()) < 1e-6);
4682  }
4683  else
4684  {
4685  assert(fabs(pP2->getZ()) < ALMOST_ZERO);
4686  }
4687 
4688  delete pP2;
4689  }
4690  }
4691  else
4692  {
4693  // both lines don't have a component in the x direction, so both must
4694  // lie in the yz plane or a plane parallel to it
4695  // there can only be an intersection if the x values are the same
4696  if (fabs(p1x - p2x) < ALMOST_ZERO)
4697  {
4698  // they are in the same plane
4699  if (fabs(sy) > ALMOST_ZERO)
4700  {
4701  double m = (p1z - p2z + (p2y / sy) * sz) / (tz - ty / sy * sz);
4702  double n = (p2y + m * ty) / sy;
4703  pP = new CLPoint(p2x + m * tx, p2y + m * ty, p2z + m * tz);
4704  CLPoint* pP2 = new CLPoint(p1x + n * sx, p1y + n * sy, p1z + n * sz);
4705 
4706  if (fabs(pP->getX()) > ALMOST_ZERO)
4707  {
4708  assert(fabs((pP->getX() - pP2->getX()) / pP->getX()) < 1e-6);
4709  }
4710  else
4711  {
4712  assert(fabs(pP2->getX()) < ALMOST_ZERO);
4713  }
4714 
4715  if (fabs(pP->getY()) > ALMOST_ZERO)
4716  {
4717  assert(fabs((pP->getY() - pP2->getY()) / pP->getY()) < 1e-6);
4718  }
4719  else
4720  {
4721  assert(fabs(pP2->getY()) < ALMOST_ZERO);
4722  }
4723 
4724  if (fabs(pP->getZ()) > ALMOST_ZERO)
4725  {
4726  assert(fabs((pP->getZ() - pP2->getZ()) / pP->getZ()) < 1e-6);
4727  }
4728  else
4729  {
4730  assert(fabs(pP2->getZ()) < ALMOST_ZERO);
4731  }
4732 
4733  delete pP2;
4734  }
4735  else if (fabs(ty) > ALMOST_ZERO)
4736  {
4737  double n = (p2z - p1z + (p2y / ty) * tz) / (sz - sy / ty * tz);
4738  double m = (p1y + n * sy) / ty;
4739  pP = new CLPoint(p2x + m * tx, p2y + m * ty, p2z + m * tz);
4740  CLPoint* pP2 = new CLPoint(p1x + n * sx, p1y + n * sy, p1z + n * sz);
4741 
4742  if (fabs(pP->getX()) > ALMOST_ZERO)
4743  {
4744  assert(fabs((pP->getX() - pP2->getX()) / pP->getX()) < 1e-6);
4745  }
4746  else
4747  {
4748  assert(fabs(pP2->getX()) < ALMOST_ZERO);
4749  }
4750 
4751  if (fabs(pP->getY()) > ALMOST_ZERO)
4752  {
4753  assert(fabs((pP->getY() - pP2->getY()) / pP->getY()) < 1e-6);
4754  }
4755  else
4756  {
4757  assert(fabs(pP2->getY()) < ALMOST_ZERO);
4758  }
4759 
4760  if (fabs(pP->getZ()) > ALMOST_ZERO)
4761  {
4762  assert(fabs((pP->getZ() - pP2->getZ()) / pP->getZ()) < 1e-6);
4763  }
4764  else
4765  {
4766  assert(fabs(pP2->getZ()) < ALMOST_ZERO);
4767  }
4768 
4769  delete pP2;
4770  }
4771  else if (fabs(sz) > ALMOST_ZERO)
4772  {
4773  double m = (p1y - p2y + (p2z / sz) * sy) / (ty - tz / sz * sy);
4774  double n = (p2z + m * tz) / sz;
4775  pP = new CLPoint(p2x + m * tx, p2y + m * ty, p2z + m * tz);
4776  CLPoint* pP2 = new CLPoint(p1x + n * sx, p1y + n * sy, p1z + n * sz);
4777 
4778  if (fabs(pP->getX()) > ALMOST_ZERO)
4779  {
4780  assert(fabs((pP->getX() - pP2->getX()) / pP->getX()) < 1e-6);
4781  }
4782  else
4783  {
4784  assert(fabs(pP2->getX()) < ALMOST_ZERO);
4785  }
4786 
4787  if (fabs(pP->getY()) > ALMOST_ZERO)
4788  {
4789  assert(fabs((pP->getY() - pP2->getY()) / pP->getY()) < 1e-6);
4790  }
4791  else
4792  {
4793  assert(fabs(pP2->getY()) < ALMOST_ZERO);
4794  }
4795 
4796  if (fabs(pP->getZ()) > ALMOST_ZERO)
4797  {
4798  assert(fabs((pP->getZ() - pP2->getZ()) / pP->getZ()) < 1e-6);
4799  }
4800  else
4801  {
4802  assert(fabs(pP2->getZ()) < ALMOST_ZERO);
4803  }
4804 
4805  delete pP2;
4806  }
4807  }
4808  }
4809 
4810  return pP;
4811 }
4812 
4813 /**
4814  * Sets whether the render extension is to deduce specie reference roles from associated
4815  * model objects if there are any.
4816  */
4818 {
4819  if (deduce != this->mDeduceSpeciesReferenceRoles)
4820  {
4821  this->mDeduceSpeciesReferenceRoles = deduce;
4822 
4823  if (this->mDeduceSpeciesReferenceRoles == true)
4824  {
4825  // try to deduce the roles and maybe update the style map
4826  if (this->mpLayout != NULL)
4827  {
4828  size_t i, iMax = this->mpLayout->getListOfReactionGlyphs().size();
4829 
4830  if (iMax > 0)
4831  {
4832  CLReactionGlyph* pRG = NULL;
4833  CLMetabReferenceGlyph* pSRG = NULL;
4834  std::set<CLMetabReferenceGlyph*> speciesReferenceGlyphs;
4835  size_t j, jMax;
4836 
4837  for (i = 0; i < iMax; ++i)
4838  {
4839  pRG = this->mpLayout->getListOfReactionGlyphs()[i];
4840  jMax = pRG->getListOfMetabReferenceGlyphs().size();
4841 
4842  for (j = 0; j < jMax; ++j)
4843  {
4844  pSRG = pRG->getListOfMetabReferenceGlyphs()[j];
4845 
4846  if (pSRG->getObjectRole().empty() && !pSRG->getModelObjectKey().empty())
4847  {
4848  speciesReferenceGlyphs.insert(pSRG);
4849  }
4850  }
4851  }
4852 
4853  if (!speciesReferenceGlyphs.empty())
4854  {
4855  // collect all the species references with their putative role
4856  if (this->mpModel != NULL)
4857  {
4858  iMax = this->mpModel->getReactions().size();
4859 
4860  if (iMax > 0)
4861  {
4862  const CReaction* pReaction = NULL;
4863  const CChemEqElement* pSSR = NULL;
4864  std::map<std::string, std::string> speciesReferenceRoles;
4865 
4866  for (i = 0; i < iMax; ++i)
4867  {
4868  pReaction = this->mpModel->getReactions()[i];
4869  jMax = pReaction->getChemEq().getSubstrates().size();
4870 
4871  for (j = 0; j < jMax; ++j)
4872  {
4873  pSSR = pReaction->getChemEq().getSubstrates()[j];
4874 
4875  if (pReaction->isReversible())
4876  {
4877  speciesReferenceRoles[pSSR->getKey()] = "product";
4878  }
4879  else
4880  {
4881  speciesReferenceRoles[pSSR->getKey()] = "substrate";
4882  }
4883  }
4884 
4885  jMax = pReaction->getChemEq().getProducts().size();
4886 
4887  for (j = 0; j < jMax; ++j)
4888  {
4889  pSSR = pReaction->getChemEq().getProducts()[j];
4890  speciesReferenceRoles[pSSR->getKey()] = "product";
4891  }
4892 
4893  jMax = pReaction->getChemEq().getModifiers().size();
4894 
4895  for (j = 0; j < jMax; ++j)
4896  {
4897  pSSR = pReaction->getChemEq().getModifiers()[j];
4898  speciesReferenceRoles[pSSR->getKey()] = "modifier";
4899  }
4900  }
4901 
4902  if (!speciesReferenceRoles.empty())
4903  {
4904  std::set<CLMetabReferenceGlyph*>::iterator it = speciesReferenceGlyphs.begin(), endit = speciesReferenceGlyphs.end();
4905  std::map<std::string, std::string>::const_iterator pos, mapEnd = speciesReferenceRoles.end();
4906 
4907  // now we assign roles to the species reference glyphs
4908  while (it != endit)
4909  {
4910  pos = speciesReferenceRoles.find((*it)->getModelObjectKey());
4911 
4912  if (pos != mapEnd)
4913  {
4914  this->mSpeciesReferencesWithDeducedRole[*it] = pos->second;
4915  }
4916 
4917  ++it;
4918  }
4919 
4920  // check if we have to update the style mapping
4921  if (!this->mSpeciesReferencesWithDeducedRole.empty())
4922  {
4923  this->clear_cached_data();
4924 
4925  if (this->mpFontRenderer != NULL)
4926  {
4927  this->analyse_render_information(mX, mY, mX + mW / this->mZoomFactor, mY + mH / this->mZoomFactor);
4928  }
4929  }
4930  }
4931  }
4932  }
4933  }
4934  }
4935  }
4936  }
4937  else
4938  {
4939  // clear all deduced roles and update the style map
4940  if (!this->mSpeciesReferencesWithDeducedRole.empty())
4941  {
4942  this->mSpeciesReferencesWithDeducedRole.clear();
4943  // update the style mapping
4944  this->clear_cached_data();
4945 
4946  if (this->mpFontRenderer != NULL)
4947  {
4948  this->analyse_render_information(mX, mY, mX + mW / this->mZoomFactor, mY + mH / this->mZoomFactor);
4949  }
4950  }
4951  }
4952  }
4953 }
4954 
4955 /**
4956  * Returns true or false depending on whether the render extension
4957  * deduces specie reference roles from associated
4958  * model objects if there are any.
4959  */
4961 {
4962  return this->mDeduceSpeciesReferenceRoles;
4963 }
4964 
4965 /**
4966  * This method adds a graphical object to the set of selected objects.
4967  */
4969 {
4970  assert(pObject != NULL);
4971 
4972  if (this->mSelection.insert(pObject).second == true)
4973  {
4974  this->draw_layout();
4975  }
4976 }
4977 
4978 /**
4979  * This method removes the given object from the selection if
4980  * it is selected.
4981  */
4983 {
4984  std::set<CLGraphicalObject*>::iterator pos = this->mSelection.find(pObject);
4985 
4986  if (pos != this->mSelection.end())
4987  {
4988  this->mSelection.erase(pos);
4989  this->draw_layout();
4990  }
4991 }
4992 
4993 /**
4994  * This method returns a reference to the set of selected objects.
4995  */
4996 std::set<CLGraphicalObject*>& CLLayoutRenderer::getSelection()
4997 {
4998  return this->mSelection;
4999 }
5000 
5001 /**
5002  * This method returns a const reference to the set of selected objects.
5003  */
5004 const std::set<CLGraphicalObject*>& CLLayoutRenderer::getSelection() const
5005 {
5006  return this->mSelection;
5007 }
5008 
5009 /**
5010  * This method returns true if the given object is part of the selection and false otherwise.
5011  */
5013 {
5014  return (this->mSelection.find(const_cast<CLGraphicalObject*>(pObject)) != this->mSelection.end());
5015 }
5016 
5017 /**
5018  * This method clears the selection.
5019  */
5021 {
5022  if (!this->mSelection.empty())
5023  {
5024  this->mSelection.clear();
5025  }
5026 }
5027 
5028 /**
5029  * This method returns all objects at the given 2D model coordinates.
5030  * The depth value is ignored.
5031  */
5032 std::multiset<CLGraphicalObject*, compareGraphicalObjectsBySize> CLLayoutRenderer::getObjectsAt(double x, double y)
5033 {
5034  // if the given point is within the current viewport, we only have to check the current drawbles
5035  // else we have to look at all layout elements.
5036  std::multiset<CLGraphicalObject*, compareGraphicalObjectsBySize> hits;
5037 
5038  if ((x >= this->mX) && (x <= (this->mX + this->mW / this->mZoomFactor)) &&
5039  (y >= this->mY) && (y <= (this->mY + this->mH / this->mZoomFactor)))
5040  {
5041  std::vector<const CLGraphicalObject*>::const_iterator it = this->mDrawables.begin(), endit = this->mDrawables.end();
5042  const CLReactionGlyph* pRG = NULL;
5043  const CLMetabReferenceGlyph* pSRG = NULL;
5044  const CLBoundingBox* pBB = NULL;
5045  const CLCurve* pCurve = NULL;
5046  const CLLineSegment* pLS = NULL;
5047  double bbx, bby, bbwidth, bbheight;
5048  double toleranceRadius = 5.0 / this->mZoomFactor;
5049 
5050  while (it != endit)
5051  {
5052  pSRG = dynamic_cast<const CLMetabReferenceGlyph*>(*it);
5053  pRG = dynamic_cast<const CLReactionGlyph*>(*it);
5054 
5055  if (pSRG != NULL && pSRG->getCurve().getNumCurveSegments() != 0)
5056  {
5057  pCurve = &pSRG->getCurve();
5058  assert(pCurve != NULL);
5059  size_t i, iMax = pCurve->getNumCurveSegments();
5060 
5061  for (i = 0; i < iMax; ++i)
5062  {
5063  pLS = pCurve->getSegmentAt(i);
5064  assert(pLS != NULL);
5065 
5066  if (isSegmentHit(pLS, x, y, toleranceRadius))
5067  {
5068  hits.insert(const_cast<CLGraphicalObject*>(*it));
5069  break;
5070  }
5071  }
5072  }
5073  else if (pRG != NULL && pRG->getCurve().getNumCurveSegments())
5074  {
5075  pCurve = &pRG->getCurve();
5076  assert(pCurve != NULL);
5077  size_t i, iMax = pCurve->getNumCurveSegments();
5078 
5079  for (i = 0; i < iMax; ++i)
5080  {
5081  pLS = pCurve->getSegmentAt(i);
5082  assert(pLS != NULL);
5083 
5084  if (isSegmentHit(pLS, x, y, toleranceRadius))
5085  {
5086  hits.insert(const_cast<CLGraphicalObject*>(*it));
5087  break;
5088  }
5089  }
5090  }
5091  else
5092  {
5093  pBB = &(*it)->getBoundingBox();
5094  bbx = pBB->getPosition().getX();
5095  bby = pBB->getPosition().getY();
5096  bbwidth = pBB->getDimensions().getWidth();
5097  bbheight = pBB->getDimensions().getHeight();
5098 
5099  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5100  {
5101  hits.insert(const_cast<CLGraphicalObject*>(*it));
5102  }
5103  }
5104 
5105  ++it;
5106  }
5107  }
5108  else
5109  {
5110  if (this->mpLayout)
5111  {
5112  CLGraphicalObject* pGO = NULL;
5113  CLReactionGlyph* pRG = NULL;
5114  CLMetabReferenceGlyph* pSRG = NULL;
5115  const CLCurve* pCurve = NULL;
5116  const CLLineSegment* pLS = NULL;
5117  const CLBoundingBox* pBB = NULL;
5118  size_t k, kMax;
5119  size_t j, jMax;
5120  double bbx, bby, bbwidth, bbheight;
5121  double toleranceRadius = 5.0 / this->mZoomFactor;
5122  size_t i, iMax = this->mpLayout->getListOfCompartmentGlyphs().size();
5123 
5124  for (i = 0; i < iMax; ++i)
5125  {
5126  pGO = this->mpLayout->getListOfCompartmentGlyphs()[i];
5127  pBB = &pGO->getBoundingBox();
5128  bbx = pBB->getPosition().getX();
5129  bby = pBB->getPosition().getY();
5130  bbwidth = pBB->getDimensions().getWidth();
5131  bbheight = pBB->getDimensions().getHeight();
5132 
5133  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5134  {
5135  hits.insert(pGO);
5136  }
5137  }
5138 
5139  iMax = this->mpLayout->getListOfMetaboliteGlyphs().size();
5140 
5141  for (i = 0; i < iMax; ++i)
5142  {
5143  pGO = this->mpLayout->getListOfMetaboliteGlyphs()[i];
5144  pBB = &pGO->getBoundingBox();
5145  bbx = pBB->getPosition().getX();
5146  bby = pBB->getPosition().getY();
5147  bbwidth = pBB->getDimensions().getWidth();
5148  bbheight = pBB->getDimensions().getHeight();
5149 
5150  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5151  {
5152  hits.insert(pGO);
5153  }
5154  }
5155 
5156  iMax = this->mpLayout->getListOfReactionGlyphs().size();
5157 
5158  for (i = 0; i < iMax; ++i)
5159  {
5160  pRG = this->mpLayout->getListOfReactionGlyphs()[i];
5161  assert(pRG != NULL);
5162 
5163  // we have to look at all points of the curve if there is one
5164  if (pRG->getCurve().getNumCurveSegments() != 0)
5165  {
5166  pCurve = &pRG->getCurve();
5167  assert(pCurve != NULL);
5168  kMax = pCurve->getNumCurveSegments();
5169 
5170  for (k = 0; k < kMax; ++k)
5171  {
5172  pLS = pCurve->getSegmentAt(k);
5173  assert(pLS != NULL);
5174 
5175  if (isSegmentHit(pLS, x, y, toleranceRadius))
5176  {
5177  hits.insert(pRG);
5178  break;
5179  }
5180  }
5181  }
5182  else
5183  {
5184  pBB = &pRG->getBoundingBox();
5185  bbx = pBB->getPosition().getX();
5186  bby = pBB->getPosition().getY();
5187  bbwidth = pBB->getDimensions().getWidth();
5188  bbheight = pBB->getDimensions().getHeight();
5189 
5190  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5191  {
5192  hits.insert(pRG);
5193  }
5194  }
5195 
5196  jMax = pRG->getListOfMetabReferenceGlyphs().size();
5197 
5198  for (j = 0; j < jMax; ++j)
5199  {
5200  pSRG = pRG->getListOfMetabReferenceGlyphs()[j];
5201  assert(pSRG != NULL);
5202 
5203  // we have to look at all points of the curve if there is one
5204  if (pSRG->getCurve().getNumCurveSegments() != 0)
5205  {
5206  pCurve = &pSRG->getCurve();
5207  assert(pCurve != NULL);
5208  kMax = pCurve->getNumCurveSegments();
5209  bool drawn = false;
5210 
5211  for (k = 0; k < kMax && !drawn; ++k)
5212  {
5213  pLS = pCurve->getSegmentAt(k);
5214 
5215  if (isSegmentHit(pLS, x, y, toleranceRadius))
5216  {
5217  hits.insert(pSRG);
5218  break;
5219  }
5220  }
5221  }
5222  else
5223  {
5224  pBB = &pSRG->getBoundingBox();
5225  bbx = pBB->getPosition().getX();
5226  bby = pBB->getPosition().getY();
5227  bbwidth = pBB->getDimensions().getWidth();
5228  bbheight = pBB->getDimensions().getHeight();
5229 
5230  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5231  {
5232  hits.insert(pSRG);
5233  }
5234  }
5235  }
5236  }
5237 
5238  iMax = this->mpLayout->getListOfTextGlyphs().size();
5239 
5240  for (i = 0; i < iMax; ++i)
5241  {
5242  pGO = this->mpLayout->getListOfTextGlyphs()[i];
5243  pBB = &pGO->getBoundingBox();
5244  bbx = pBB->getPosition().getX();
5245  bby = pBB->getPosition().getY();
5246  bbwidth = pBB->getDimensions().getWidth();
5247  bbheight = pBB->getDimensions().getHeight();
5248 
5249  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5250  {
5251  hits.insert(pGO);
5252  }
5253  }
5254 
5255  iMax = this->mpLayout->getListOfGeneralGlyphs().size();
5256 
5257  for (i = 0; i < iMax; ++i)
5258  {
5259  CLGeneralGlyph* pGG = this->mpLayout->getListOfGeneralGlyphs()[i];
5260  assert(pGG != NULL);
5261 
5262  // we have to look at all points of the curve if there is one
5263  if (pGG->getCurve().getNumCurveSegments() != 0)
5264  {
5265  pCurve = &pGG->getCurve();
5266  assert(pCurve != NULL);
5267  kMax = pCurve->getNumCurveSegments();
5268 
5269  for (k = 0; k < kMax; ++k)
5270  {
5271  pLS = pCurve->getSegmentAt(k);
5272  assert(pLS != NULL);
5273 
5274  if (isSegmentHit(pLS, x, y, toleranceRadius))
5275  {
5276  hits.insert(pGG);
5277  break;
5278  }
5279  }
5280  }
5281  else
5282  {
5283  pBB = &pGG->getBoundingBox();
5284  bbx = pBB->getPosition().getX();
5285  bby = pBB->getPosition().getY();
5286  bbwidth = pBB->getDimensions().getWidth();
5287  bbheight = pBB->getDimensions().getHeight();
5288 
5289  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5290  {
5291  hits.insert(pRG);
5292  }
5293  }
5294 
5295  jMax = pGG->getListOfReferenceGlyphs().size();
5296 
5297  for (j = 0; j < jMax; ++j)
5298  {
5299  CLReferenceGlyph* pRefG = pGG->getListOfReferenceGlyphs()[j];
5300  assert(pRefG != NULL);
5301 
5302  // we have to look at all points of the curve if there is one
5303  if (pRefG->getCurve().getNumCurveSegments() != 0)
5304  {
5305  pCurve = &pRefG->getCurve();
5306  assert(pCurve != NULL);
5307  kMax = pCurve->getNumCurveSegments();
5308  bool drawn = false;
5309 
5310  for (k = 0; k < kMax && !drawn; ++k)
5311  {
5312  pLS = pCurve->getSegmentAt(k);
5313 
5314  if (isSegmentHit(pLS, x, y, toleranceRadius))
5315  {
5316  hits.insert(pSRG);
5317  break;
5318  }
5319  }
5320  }
5321  else
5322  {
5323  pBB = &pRefG->getBoundingBox();
5324  bbx = pBB->getPosition().getX();
5325  bby = pBB->getPosition().getY();
5326  bbwidth = pBB->getDimensions().getWidth();
5327  bbheight = pBB->getDimensions().getHeight();
5328 
5329  if (x >= bbx && y >= bby && x <= (bbx + bbwidth) && y <= (bby + bbheight))
5330  {
5331  hits.insert(pRefG);
5332  }
5333  }
5334  }
5335  }
5336  }
5337  }
5338 
5339  return hits;
5340 }
5341 
5342 /**
5343  * This method returns all objects at the given 2D screen coordinates.
5344  * The depth value is ignored.
5345  */
5346 std::multiset<CLGraphicalObject*, compareGraphicalObjectsBySize> CLLayoutRenderer::getObjectsAtViewportPosition(unsigned int x, unsigned int y)
5347 {
5348  // first we convert the viewport position to model coordinates
5349  std::pair<double, double> coords = this->convert_to_model_space(x, y);
5350  return this->getObjectsAt(coords.first, coords.second);
5351 }
5352 
5353 /**
5354  * Calculates if the point given as x,y is close enough to the given line segment
5355  * to count as a hit.
5356  */
5357 bool CLLayoutRenderer::isSegmentHit(const CLLineSegment* pLS, double x, double y, double toleranceRadius)
5358 {
5359  bool result = false;
5360 
5361  if (pLS->isBezier())
5362  {
5363  // check if the point is within an extended bounding box
5364  double x1, x2, x3, x4, y1, y2, y3, y4;
5365  x1 = pLS->getStart().getX();
5366  x2 = pLS->getBase1().getX();
5367  x3 = pLS->getBase2().getX();
5368  x4 = pLS->getEnd().getX();
5369  y1 = pLS->getStart().getY();
5370  y2 = pLS->getBase1().getY();
5371  y3 = pLS->getBase2().getY();
5372  y4 = pLS->getEnd().getY();
5373  double maxX = (x1 > x2) ? x1 : x2;
5374  maxX = (maxX > x3) ? maxX : x3;
5375  maxX = (maxX > x4) ? maxX : x4;
5376  double maxY = (y1 > y2) ? y1 : y2;
5377  maxY = (maxY > y3) ? maxY : y3;
5378  maxY = (maxY > y4) ? maxY : y4;
5379  double minX = (x1 < x2) ? x1 : x2;
5380  minX = (minX < x3) ? minX : x3;
5381  minX = (minX < x4) ? minX : x4;
5382  double minY = (y1 < y2) ? y1 : y2;
5383  minY = (minY < y3) ? minY : y3;
5384  minY = (minY < y4) ? minY : y4;
5385  minX -= toleranceRadius;
5386  minY -= toleranceRadius;
5387  maxX += toleranceRadius;
5388  maxY += toleranceRadius;
5389 
5390  if (x >= minX && y >= minY && x <= maxX && y <= maxY)
5391  {
5392  // calculate points to get line segments and check each one for a hit
5393  GLdouble* pData = new GLdouble[3 * NUM_BEZIER_POINTS];
5394  CLLayoutRenderer::calculate_cubicbezier(x1, y1, 0.0, x2, y2, 0.0, x3, y3, 0.0, x4, y4, 0.0, NUM_BEZIER_POINTS, pData);
5395  size_t i;
5396  double distance = 0.0;
5397 
5398  for (i = 1; i < NUM_BEZIER_POINTS && !result; ++i)
5399  {
5400  distance = distancePointLineSegment(x, y, pData[(i - 1) * 3], pData[(i - 1) * 3 + 1], pData[i * 3], pData[i * 3 + 1]);
5401  result = (distance == distance && distance <= toleranceRadius);
5402  }
5403  }
5404  else
5405  {
5406  result = false;
5407  }
5408  }
5409  else
5410  {
5411  double distance = distancePointLineSegment(x, y, pLS->getStart().getX(), pLS->getStart().getY(), pLS->getEnd().getX(), pLS->getEnd().getY());
5412  result = (distance == distance && distance <= toleranceRadius);
5413  }
5414 
5415  return result;
5416 }
5417 
5418 double CLLayoutRenderer::distancePointLineSegment(double x, double y, double lx1, double ly1, double lx2, double ly2)
5419 {
5420  double dx = lx2 - lx1;
5421  double dy = ly2 - ly1;
5422  double distance = std::numeric_limits<double>::quiet_NaN();
5423 
5424  if (fabs(dx) <= 1e-9)
5425  {
5426  if (fabs(dy) > 1e-9)
5427  {
5428  if (y >= ((ly1 < ly2) ? ly1 : ly2) && y <= ((ly1 > ly2) ? ly1 : ly2))
5429  {
5430  distance = fabs(lx1 - x);
5431  }
5432  }
5433  }
5434  else if (fabs(dy) <= 1e-9)
5435  {
5436  if (x >= ((lx1 < lx2) ? lx1 : lx2) && x <= ((lx1 > lx2) ? lx1 : lx2))
5437  {
5438  distance = fabs(ly1 - y);
5439  }
5440  }
5441  else
5442  {
5443  double slope = dy / dx;
5444  double invslope = -1.0 * dx / dy;
5445  double xCross = (-invslope * x + slope * lx1 + y - ly1) / (slope - invslope);
5446 
5447  if ((xCross >= ((lx1 < lx2) ? lx1 : lx2)) && (xCross <= (((lx1 > lx2) ? lx1 : lx2))))
5448  {
5449  double yCross = slope * (xCross - lx1) + ly1;
5450 
5451  if ((yCross >= ((ly1 < ly2) ? ly1 : ly2)) && (yCross <= (((ly1 > ly2) ? ly1 : ly2))))
5452  {
5453  distance = sqrt(pow(x - xCross, 2) + pow(y - yCross, 2));
5454  }
5455  else
5456  {
5457  if (fabs(lx1 - xCross) < fabs(lx2 - xCross))
5458  {
5459  // the distance to lx2.ly2 is smaller
5460  distance = sqrt(pow(x - lx2, 2) + pow(y - ly2, 2));
5461  }
5462  else
5463  {
5464  distance = sqrt(pow(x - lx1, 2) + pow(y - ly1, 2));
5465  }
5466  }
5467  }
5468  else
5469  {
5470  if (fabs(lx1 - xCross) < fabs(lx2 - xCross))
5471  {
5472  // the distance to lx2.ly2 is smaller
5473  distance = sqrt(pow(x - lx2, 2) + pow(y - ly2, 2));
5474  }
5475  else
5476  {
5477  distance = sqrt(pow(x - lx1, 2) + pow(y - ly1, 2));
5478  }
5479  }
5480  }
5481 
5482  return distance;
5483 }
5484 
5485 /**
5486  * Calculates the bounding box of the curve by looking at all the basepoints.
5487  * The returned boundingbox object has to be deleted by the caller.
5488  */
5490 {
5491 #ifdef _WIN32
5492 // windows seems to declare a macro called max which clashes with the
5493 // call to mac from numeric limits
5494 #undef max
5495 #endif // _WIN32
5496  double xMin = std::numeric_limits<double>::max();
5497  double yMin = xMin;
5498  double xMax = -xMin;
5499  double yMax = xMax;
5500  size_t i, iMax = pCurve->getNumCurveSegments();
5501  const CLLineSegment* pLS = NULL;
5502 
5503  for (i = 0; i < iMax; ++i)
5504  {
5505  pLS = pCurve->getSegmentAt(i);
5506  xMin = (xMin <= pLS->getStart().getX()) ? xMin : pLS->getStart().getX();
5507  yMin = (yMin <= pLS->getStart().getY()) ? yMin : pLS->getStart().getY();
5508  xMax = (xMax >= pLS->getStart().getX()) ? xMax : pLS->getStart().getX();
5509  yMax = (yMax >= pLS->getStart().getY()) ? yMax : pLS->getStart().getY();
5510  xMin = (xMin <= pLS->getEnd().getX()) ? xMin : pLS->getEnd().getX();
5511  yMin = (yMin <= pLS->getEnd().getY()) ? yMin : pLS->getEnd().getY();
5512  xMax = (xMax >= pLS->getEnd().getX()) ? xMax : pLS->getEnd().getX();
5513  yMax = (yMax >= pLS->getEnd().getY()) ? yMax : pLS->getEnd().getY();
5514 
5515  if (pLS->isBezier())
5516  {
5517  xMin = (xMin <= pLS->getBase1().getX()) ? xMin : pLS->getBase1().getX();
5518  yMin = (yMin <= pLS->getBase1().getY()) ? yMin : pLS->getBase1().getY();
5519  xMax = (xMax >= pLS->getBase1().getX()) ? xMax : pLS->getBase1().getX();
5520  yMax = (yMax >= pLS->getBase1().getY()) ? yMax : pLS->getBase1().getY();
5521  xMin = (xMin <= pLS->getBase2().getX()) ? xMin : pLS->getBase2().getX();
5522  yMin = (yMin <= pLS->getBase2().getY()) ? yMin : pLS->getBase2().getY();
5523  xMax = (xMax >= pLS->getBase2().getX()) ? xMax : pLS->getBase2().getX();
5524  yMax = (yMax >= pLS->getBase2().getY()) ? yMax : pLS->getBase2().getY();
5525  }
5526  }
5527 
5528  return new CLBoundingBox(CLPoint(xMin, yMin), CLDimensions(xMax - xMin, yMax - yMin));
5529 }
5530 
5531 /**
5532  * Draw the selection frame around an graphical objects bounding object box.
5533  * If drawHandles is true, resize handles are drawn as well,
5534  * else only the box is drawn.
5535  * If the curve is given, the basepoints of the curve are also drawn
5536  * as small circles.
5537  */
5538 void CLLayoutRenderer::drawSelectionBox(double x, double y, double width, double height, bool /*drawHandles*/)
5539 {
5540  // first we draw the actual box
5541  // and we use a stippled line of width 2
5542  // if the OpenGL implementation does not support different line width,
5543  // we don't care, it will probably just look better if it does.
5544  glPushMatrix();
5545  glTranslated(x - SELECTION_FRAME_WIDTH, y - SELECTION_FRAME_WIDTH, 0.0);
5546  glEnable(GL_LINE_STIPPLE);
5547  glLineStipple(10, 0x5555);
5548  glLineWidth(2.0);
5549  // first we draw a box with a black line and line stippling
5550  // we have to extend the box a bit to have a small space between the object and the frame
5551  glColor3f(0.0, 0.0, 0.0);
5552  glBegin(GL_LINE_LOOP);
5553  glVertex3d(0.0, 0.0, 0.0);
5554  glVertex3d(width + 2 * SELECTION_FRAME_WIDTH, 0.0, 0.0);
5555  glVertex3d(width + 2 * SELECTION_FRAME_WIDTH, height + 2 * SELECTION_FRAME_WIDTH, 0.0);
5556  glVertex3d(0.0, height + 2 * SELECTION_FRAME_WIDTH, 0.0);
5557  glEnd();
5558  // now we draw the same box again, but in white and with reversed line stippling
5559  glColor3f(1.0, 1.0, 1.0);
5560  glLineStipple(10, ~0x5555);
5561  glBegin(GL_LINE_LOOP);
5562  glVertex3d(0.0, 0.0, 0.0);
5563  glVertex3d(width + 2 * SELECTION_FRAME_WIDTH, 0.0, 0.0);
5564  glVertex3d(width + 2 * SELECTION_FRAME_WIDTH, height + 2 * SELECTION_FRAME_WIDTH, 0.0);
5565  glVertex3d(0.0, height + 2 * SELECTION_FRAME_WIDTH, 0.0);
5566  glEnd();
5567  glDisable(GL_LINE_STIPPLE);
5568  /*
5569  * For now we disable handle drawing until we make the handles functional.
5570  if(drawHandles == true)
5571  {
5572  // draw the handles
5573  // we draw 8 handles, one on each corner
5574  // and one on each side
5575  GLdouble* pArrowData=new GLdouble[3*7];
5576  // point 1
5577  pArrowData[0]=8.0;
5578  pArrowData[1]=3.0;
5579  pArrowData[2]=0.0;
5580 
5581  // point 2
5582  pArrowData[3]=0.0;
5583  pArrowData[4]=3.0;
5584  pArrowData[5]=0.0;
5585  // point 3
5586  pArrowData[6]=0.0;
5587  pArrowData[7]=-3.0;
5588  pArrowData[8]=0.0;
5589 
5590  // point 4
5591  pArrowData[9]=8.0;
5592  pArrowData[10]=-3.0;
5593  pArrowData[11]=0.0;
5594 
5595  // point 5
5596  pArrowData[12]=6.0;
5597  pArrowData[13]=-6.0;
5598  pArrowData[14]=0.0;
5599 
5600  // point 6
5601  pArrowData[15]=14.0;
5602  pArrowData[16]=0.0;
5603  pArrowData[17]=0.0;
5604 
5605  // point 7
5606  pArrowData[18]=6.0;
5607  pArrowData[19]=6.0;
5608  pArrowData[20]=0.0;
5609 
5610  size_t i;
5611  // right
5612  glPushMatrix();
5613  glTranslatef(width + 2 * SELECTION_FRAME_WIDTH + 3, (height + 2 * SELECTION_FRAME_WIDTH)/ 2.0 ,0.0);
5614  // white polygon
5615  glColor3f(1.0,1.0,1.0);
5616  glBegin(GL_POLYGON);
5617  for(i=0;i<4;++i)
5618  {
5619  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5620  }
5621  glEnd();
5622  glBegin(GL_POLYGON);
5623  for(i=4;i<7;++i)
5624  {
5625  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5626  }
5627  glVertex3f(pArrowData[0],pArrowData[1],pArrowData[2]);
5628  glEnd();
5629  // with black border
5630  glColor3f(0.0,0.0,0.0);
5631  glBegin(GL_LINE_LOOP);
5632  for(i=0;i<7;++i)
5633  {
5634  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5635  }
5636  glEnd();
5637  glPopMatrix();
5638  //
5639  // bottom
5640  glPushMatrix();
5641  glTranslatef((width + 2 * SELECTION_FRAME_WIDTH ) / 2.0 , height + 2 * SELECTION_FRAME_WIDTH + 3.0,0.0);
5642  glRotatef(90,0.0,0.0,1.0);
5643  // white polygon
5644  glColor3f(1.0,1.0,1.0);
5645  glBegin(GL_POLYGON);
5646  for(i=0;i<4;++i)
5647  {
5648  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5649  }
5650  glEnd();
5651  glBegin(GL_POLYGON);
5652  for(i=4;i<7;++i)
5653  {
5654  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5655  }
5656  glVertex3f(pArrowData[0],pArrowData[1],pArrowData[2]);
5657  glEnd();
5658  // with black border
5659  glColor3f(0.0,0.0,0.0);
5660  glBegin(GL_LINE_LOOP);
5661  for(i=0;i<7;++i)
5662  {
5663  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5664  }
5665  glEnd();
5666  glPopMatrix();
5667  //
5668  // left
5669  glPushMatrix();
5670  glTranslatef(- 3, (height + 2 * SELECTION_FRAME_WIDTH)/ 2.0 ,0.0);
5671  glRotatef(180,0.0,0.0,1.0);
5672  // white polygon
5673  glColor3f(1.0,1.0,1.0);
5674  glBegin(GL_POLYGON);
5675  for(i=0;i<4;++i)
5676  {
5677  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5678  }
5679  glEnd();
5680  glBegin(GL_POLYGON);
5681  for(i=4;i<7;++i)
5682  {
5683  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5684  }
5685  glVertex3f(pArrowData[0],pArrowData[1],pArrowData[2]);
5686  glEnd();
5687  // with black border
5688  glColor3f(0.0,0.0,0.0);
5689  glBegin(GL_LINE_LOOP);
5690  for(i=0;i<7;++i)
5691  {
5692  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5693  }
5694  glEnd();
5695  glPopMatrix();
5696  // top
5697  glPushMatrix();
5698  glTranslatef((width + 2 * SELECTION_FRAME_WIDTH) / 2.0 , -3.0,0.0);
5699  glRotatef(-90,0.0,0.0,1.0);
5700  // white polygon
5701  glColor3f(1.0,1.0,1.0);
5702  glBegin(GL_POLYGON);
5703  for(i=0;i<4;++i)
5704  {
5705  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5706  }
5707  glEnd();
5708  glBegin(GL_POLYGON);
5709  for(i=4;i<7;++i)
5710  {
5711  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5712  }
5713  glVertex3f(pArrowData[0],pArrowData[1],pArrowData[2]);
5714  glEnd();
5715  // with black border
5716  glColor3f(0.0,0.0,0.0);
5717  glBegin(GL_LINE_LOOP);
5718  for(i=0;i<7;++i)
5719  {
5720  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5721  }
5722  glEnd();
5723  glPopMatrix();
5724 
5725  //
5726  // left top corner
5727  glPushMatrix();
5728  glTranslatef(-3.0, -3.0,0.0);
5729  glRotatef(225,0.0,0.0,1.0);
5730  // white polygon
5731  glColor3f(1.0,1.0,1.0);
5732  glBegin(GL_POLYGON);
5733  for(i=0;i<4;++i)
5734  {
5735  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5736  }
5737  glEnd();
5738  glBegin(GL_POLYGON);
5739  for(i=4;i<7;++i)
5740  {
5741  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5742  }
5743  glVertex3f(pArrowData[0],pArrowData[1],pArrowData[2]);
5744  glEnd();
5745  // with black border
5746  glColor3f(0.0,0.0,0.0);
5747  glBegin(GL_LINE_LOOP);
5748  for(i=0;i<7;++i)
5749  {
5750  glVertex3f(pArrowData[i*3],pArrowData[i*3+1],pArrowData[i*3+2]);
5751  }
5752  glEnd();
5753  glPopMatrix();
5754  //
5755  // right top corner
5756  glPushMatrix();
5757  glTranslatef((width + 2 * SELECTION_FRAME_WIDTH) + 3.0, - 3.0,0.0);
5758  glRotatef(315,0.0,0.0,1.0);
5759  // white polygon
5760  glColor3f(1.0,1.0,1.0);
5761  glBegin(GL_POLYGON);
5762  for(i=0;i<4;++i)
5763  {
5764</