1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 
6 #include <string.h>
7 #include "drawContext.h"
8 #include "GmshDefines.h"
9 #include "Numeric.h"
10 #include "StringUtils.h"
11 #include "Context.h"
12 #include "gl2ps.h"
13 #include "SVector3.h"
14 #include "GModel.h"
15 
drawString(const std::string & s,double x,double y,double z,const std::string & font_name,int font_enum,int font_size,int align,int line_num)16 void drawContext::drawString(const std::string &s, double x, double y, double z,
17                              const std::string &font_name, int font_enum,
18                              int font_size, int align, int line_num)
19 {
20   if(s.empty()) return;
21   if(CTX::instance()->printing && !CTX::instance()->print.text) return;
22 
23   if(s.size() > 8 && s.substr(0, 7) == "file://") {
24     drawImage(s.substr(7), x, y, z, align);
25     return;
26   }
27 
28   glRasterPos3d(x, y, z);
29   GLboolean valid;
30   glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
31   if(valid == GL_FALSE) return; // the primitive is culled
32 
33   if(align > 0 || line_num) {
34     GLdouble pos[4];
35     glGetDoublev(GL_CURRENT_RASTER_POSITION, pos);
36     double x[3], w[3] = {pos[0], pos[1], pos[2]};
37     drawContext::global()->setFont(font_enum, font_size);
38     double width = drawContext::global()->getStringWidth(s.c_str());
39     double height = drawContext::global()->getStringHeight();
40     // width and height must here be computed in true pixel coordinates, because
41     // viewport2world uses the actual, pixel-sized (not FLTK-sized) viewport
42     width *= highResolutionPixelFactor();
43     height *= highResolutionPixelFactor();
44     // alignment for TeX is handled directly by gl2ps
45     if(!CTX::instance()->printing ||
46        CTX::instance()->print.fileFormat != FORMAT_TEX) {
47       switch(align) {
48       case 1:
49         w[0] -= width / 2.;
50         break; // bottom center
51       case 2:
52         w[0] -= width;
53         break; // bottom right
54       case 3:
55         w[1] -= height;
56         break; // top left
57       case 4:
58         w[0] -= width / 2.;
59         w[1] -= height;
60         break; // top center
61       case 5:
62         w[0] -= width;
63         w[1] -= height;
64         break; // top right
65       case 6:
66         w[1] -= height / 2.;
67         break; // center left
68       case 7:
69         w[0] -= width / 2.;
70         w[1] -= height / 2.;
71         break; // center center
72       case 8:
73         w[0] -= width;
74         w[1] -= height / 2.;
75         break; // center right
76       default: break;
77       }
78     }
79     // treat line number also for TeX
80     if(line_num) w[1] -= line_num * (1.1 * height);
81     viewport2World(w, x);
82     glRasterPos3d(x[0], x[1], x[2]);
83   }
84 
85   if(!CTX::instance()->printing) {
86     drawContext::global()->setFont(font_enum, font_size);
87     drawContext::global()->drawString(s.c_str());
88   }
89   else {
90     if(CTX::instance()->print.fileFormat == FORMAT_TEX) {
91       std::string tmp =
92         SanitizeTeXString(s.c_str(), CTX::instance()->print.texAsEquation);
93       int opt;
94       switch(align) {
95       case 1:
96         opt = GL2PS_TEXT_B;
97         break; // bottom center
98       case 2:
99         opt = GL2PS_TEXT_BR;
100         break; // bottom right
101       case 3:
102         opt = GL2PS_TEXT_TL;
103         break; // top left
104       case 4:
105         opt = GL2PS_TEXT_T;
106         break; // top center
107       case 5:
108         opt = GL2PS_TEXT_TR;
109         break; // top right
110       case 6:
111         opt = GL2PS_TEXT_CL;
112         break; // center left
113       case 7:
114         opt = GL2PS_TEXT_C;
115         break; // center center
116       case 8:
117         opt = GL2PS_TEXT_CR;
118         break; // center right
119       default:
120         opt = GL2PS_TEXT_BL;
121         break; // bottom left
122       }
123       gl2psTextOpt(tmp.c_str(), font_name.c_str(), font_size, opt, 0.);
124     }
125     else if(CTX::instance()->print.epsQuality &&
126             (CTX::instance()->print.fileFormat == FORMAT_PS ||
127              CTX::instance()->print.fileFormat == FORMAT_EPS ||
128              CTX::instance()->print.fileFormat == FORMAT_PDF ||
129              CTX::instance()->print.fileFormat == FORMAT_SVG ||
130              CTX::instance()->print.fileFormat == FORMAT_TIKZ)) {
131       gl2psText(s.c_str(), font_name.c_str(), font_size);
132     }
133     else {
134       drawContext::global()->setFont(font_enum, font_size);
135       drawContext::global()->drawString(s.c_str());
136     }
137   }
138 }
139 
drawString(const std::string & s,double x,double y,double z,int line_num)140 void drawContext::drawString(const std::string &s, double x, double y, double z,
141                              int line_num)
142 {
143   drawString(s, x, y, z, CTX::instance()->glFont, CTX::instance()->glFontEnum,
144              CTX::instance()->glFontSize, 0, line_num);
145 }
146 
drawStringCenter(const std::string & s,double x,double y,double z,int line_num)147 void drawContext::drawStringCenter(const std::string &s, double x, double y,
148                                    double z, int line_num)
149 {
150   drawString(s, x, y, z, CTX::instance()->glFont, CTX::instance()->glFontEnum,
151              CTX::instance()->glFontSize, 1, line_num);
152 }
153 
drawStringRight(const std::string & s,double x,double y,double z,int line_num)154 void drawContext::drawStringRight(const std::string &s, double x, double y,
155                                   double z, int line_num)
156 {
157   drawString(s, x, y, z, CTX::instance()->glFont, CTX::instance()->glFontEnum,
158              CTX::instance()->glFontSize, 2, line_num);
159 }
160 
drawString(const std::string & s,double x,double y,double z,double style,int line_num)161 void drawContext::drawString(const std::string &s, double x, double y, double z,
162                              double style, int line_num)
163 {
164   unsigned int bits = (unsigned int)style;
165 
166   if(!bits) { // use defaults
167     drawString(s, x, y, z, line_num);
168   }
169   else {
170     int size = (bits & 0xff);
171     int font = (bits >> 8 & 0xff);
172     int align = (bits >> 16 & 0xff);
173     int font_enum = drawContext::global()->getFontEnum(font);
174     std::string font_name = drawContext::global()->getFontName(font);
175     if(!size) size = CTX::instance()->glFontSize;
176     drawString(s, x, y, z, font_name, font_enum, size, align, line_num);
177   }
178 }
179 
drawImage(const std::string & name,double x,double y,double z,int align)180 void drawContext::drawImage(const std::string &name, double x, double y,
181                             double z, int align)
182 {
183   // format can be "@wxh" or "@wxh,wx,wy,wz,hx,hy,hz", where w and h are the
184   // width and height (in model coordinates for T3 or in pixels for T2) of the
185   // image, wx,wy,wz is the direction of the bottom edge of the image and
186   // hx,hy,hz is the direction of the left edge of the image.
187   size_t p = name.find_last_of('@');
188   std::string file = name, format;
189   if(p != std::string::npos) {
190     format = name.substr(p + 1);
191     file = name.substr(0, p);
192   }
193   double w = 0., h = 0., wx = 1., wy = 0., wz = 0., hx = 0., hy = 1., hz = 0.;
194   bool billboard = false;
195   if(format.size()) {
196     bool ok;
197     if(format.find(',') != std::string::npos)
198       ok = (sscanf(format.c_str(), "%lfx%lf,%lf,%lf,%lf,%lf,%lf,%lf", &w, &h,
199                    &wx, &wy, &wz, &hx, &hy, &hz) == 8);
200     else
201       ok = (sscanf(format.c_str(), "%lfx%lf", &w, &h) == 2);
202     if(!ok)
203       Msg::Warning("Bad image format: use `@wxh' or `@wxh,wx,wy,wz,hx,hy,hz'");
204     if(format.find('#') != std::string::npos)
205       billboard = true; // texture will always face camera
206   }
207 
208   imgtex *img;
209   if(!_imageTextures.count(file)) {
210     img = &_imageTextures[file];
211     file = FixRelativePath(GModel::current()->getFileName(), file);
212     if(!generateTextureForImage(file, 1, img->tex, img->w, img->h)) {
213       return;
214     }
215   }
216   else {
217     img = &_imageTextures[file];
218   }
219   if(!img->tex) {
220     Msg::Debug("No texture for image - skipping image draw");
221     return;
222   }
223 
224   if(w == 0 && h == 0) {
225     w = img->w;
226     h = img->h;
227   }
228   else if(h == 0) {
229     h = w * img->h / img->w;
230   }
231   else if(w == 0) {
232     w = h * img->w / img->h;
233   }
234 
235   GLboolean valid = GL_TRUE;
236   GLint matrixMode = 0;
237   if(billboard) {
238     glRasterPos3d(x, y, z);
239     GLfloat pos[4];
240     glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
241     glGetIntegerv(GL_MATRIX_MODE, &matrixMode);
242     glMatrixMode(GL_PROJECTION);
243     glPushMatrix();
244     glLoadIdentity();
245     glMatrixMode(GL_MODELVIEW);
246     glPushMatrix();
247     glLoadIdentity();
248     double fact = highResolutionPixelFactor();
249     glOrtho((double)viewport[0], (double)viewport[2] * fact,
250             (double)viewport[1], (double)viewport[3] * fact, -1, 1);
251     x = pos[0];
252     y = pos[1];
253     z = 0;
254     w *= fact * s[0] / pixel_equiv_x;
255     h *= fact * s[1] / pixel_equiv_y;
256     glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
257   }
258   if(valid == GL_TRUE) {
259     switch(align) {
260     case 1:
261       x -= w / 2.;
262       break; // bottom center
263     case 2:
264       x -= w;
265       break; // bottom right
266     case 3:
267       y -= h;
268       break; // top left
269     case 4:
270       x -= w / 2.;
271       y -= h;
272       break; // top center
273     case 5:
274       x -= w;
275       y -= h;
276       break; // top right
277     case 6:
278       y -= h / 2.;
279       break; // center left
280     case 7:
281       x -= w / 2.;
282       y -= h / 2.;
283       break; // center center
284     case 8:
285       x -= w;
286       y -= h / 2.;
287       break; // center right
288     default: break;
289     }
290     glEnable(GL_BLEND);
291     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
292     glEnable(GL_TEXTURE_2D);
293     glBindTexture(GL_TEXTURE_2D, img->tex);
294     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
295     glBegin(GL_QUADS);
296     glTexCoord2f(1.0f, 1.0f);
297     glVertex3d(x + wx * w, y + wy * w, z + wz * w);
298     glTexCoord2f(1.0f, 0.0f);
299     glVertex3d(x + wx * w + hx * h, y + wy * w + hy * h, z + wz * w + hz * h);
300     glTexCoord2f(0.0f, 0.0f);
301     glVertex3d(x + hx * h, y + hy * h, z + hz * h);
302     glTexCoord2f(0.0f, 1.0f);
303     glVertex3d(x, y, z);
304     glEnd();
305     glDisable(GL_TEXTURE_2D);
306     glDisable(GL_BLEND);
307   }
308   if(billboard) {
309     glPopMatrix();
310     glMatrixMode(GL_PROJECTION);
311     glPopMatrix();
312     glMatrixMode(matrixMode);
313   }
314 }
315 
drawCube(double x,double y,double z,double val[9],int light)316 void drawContext::drawCube(double x, double y, double z, double val[9],
317                            int light)
318 {
319   double d0[3] = {val[0], val[1], val[2]};
320   double d1[3] = {val[3], val[4], val[5]};
321   double d2[3] = {val[6], val[7], val[8]};
322 
323   double x0[3] = {x + d1[0] + d1[0] + d2[0], x + d0[1] + d1[1] + d2[1],
324                   z + d0[2] + d1[2] + d2[2]};
325   double x1[3] = {x - d1[0] + d1[0] + d2[0], x - d0[1] + d1[1] + d2[1],
326                   z - d0[2] + d1[2] + d2[2]};
327   double x2[3] = {x - d1[0] - d1[0] + d2[0], x - d0[1] - d1[1] + d2[1],
328                   z - d0[2] - d1[2] + d2[2]};
329   double x3[3] = {x + d1[0] - d1[0] + d2[0], x + d0[1] - d1[1] + d2[1],
330                   z + d0[2] - d1[2] + d2[2]};
331 
332   double x4[3] = {x + d1[0] + d1[0] - d2[0], x + d0[1] + d1[1] - d2[1],
333                   z + d0[2] + d1[2] - d2[2]};
334   double x5[3] = {x - d1[0] + d1[0] - d2[0], x - d0[1] + d1[1] - d2[1],
335                   z - d0[2] + d1[2] - d2[2]};
336   double x6[3] = {x - d1[0] - d1[0] - d2[0], x - d0[1] - d1[1] - d2[1],
337                   z - d0[2] - d1[2] - d2[2]};
338   double x7[3] = {x + d1[0] - d1[0] - d2[0], x + d0[1] - d1[1] - d2[1],
339                   z + d0[2] - d1[2] - d2[2]};
340 
341   if(light) glEnable(GL_LIGHTING);
342   glPushMatrix();
343 
344   glBegin(GL_POLYGON);
345   glColor3f(x0[0], x0[1], x0[2]);
346   glColor3f(x1[0], x1[1], x1[2]);
347   glColor3f(x2[0], x2[1], x2[2]);
348   glColor3f(x3[0], x3[1], x3[2]);
349   glEnd();
350 
351   glBegin(GL_POLYGON);
352   glColor3f(x4[0], x4[1], x4[2]);
353   glColor3f(x7[0], x7[1], x7[2]);
354   glColor3f(x6[0], x6[1], x6[2]);
355   glColor3f(x5[0], x5[1], x5[2]);
356   glEnd();
357 
358   glBegin(GL_POLYGON);
359   glColor3f(x0[0], x0[1], x0[2]);
360   glColor3f(x3[0], x3[1], x3[2]);
361   glColor3f(x7[0], x7[1], x7[2]);
362   glColor3f(x4[0], x4[1], x4[2]);
363   glEnd();
364 
365   glBegin(GL_POLYGON);
366   glColor3f(x1[0], x1[1], x1[2]);
367   glColor3f(x5[0], x5[1], x5[2]);
368   glColor3f(x6[0], x6[1], x6[2]);
369   glColor3f(x2[0], x2[1], x2[2]);
370   glEnd();
371 
372   glBegin(GL_POLYGON);
373   glColor3f(x0[0], x0[1], x0[2]);
374   glColor3f(x4[0], x4[1], x4[2]);
375   glColor3f(x5[0], x5[1], x5[2]);
376   glColor3f(x1[0], x1[1], x1[2]);
377   glEnd();
378 
379   glBegin(GL_POLYGON);
380   glColor3f(x3[0], x3[1], x3[2]);
381   glColor3f(x2[0], x2[1], x2[2]);
382   glColor3f(x6[0], x6[1], x6[2]);
383   glColor3f(x7[0], x7[1], x7[2]);
384   glEnd();
385 
386   glPopMatrix();
387   glDisable(GL_LIGHTING);
388 }
389 
drawSphere(double R,double x,double y,double z,int n1,int n2,int light)390 void drawContext::drawSphere(double R, double x, double y, double z, int n1,
391                              int n2, int light)
392 {
393   if(light) glEnable(GL_LIGHTING);
394   glPushMatrix();
395   glTranslated(x, y, z);
396   gluSphere(_quadric, R, n1, n2);
397   glPopMatrix();
398   glDisable(GL_LIGHTING);
399 }
400 
drawEllipse(double x,double y,double z,float v0[3],float v1[3],int light)401 void drawContext::drawEllipse(double x, double y, double z, float v0[3],
402                               float v1[3], int light)
403 {
404   if(light) glEnable(GL_LIGHTING);
405   glPushMatrix();
406   GLfloat m[16] = {v0[0],
407                    v0[1],
408                    v0[2],
409                    .0f,
410                    v1[0],
411                    v1[1],
412                    v1[2],
413                    .0f,
414                    v0[1] * v1[2] - v0[2] * v1[1],
415                    v0[2] * v1[0] - v0[0] * v1[2],
416                    v0[0] * v1[1] - v0[1] * v1[0],
417                    .0f,
418                    (GLfloat)x,
419                    (GLfloat)y,
420                    (GLfloat)z,
421                    1.f};
422   glMultMatrixf(m);
423   glCallList(_displayLists + 2);
424   glPopMatrix();
425   glDisable(GL_LIGHTING);
426 }
427 
drawEllipsoid(double x,double y,double z,float v0[3],float v1[3],float v2[3],int light)428 void drawContext::drawEllipsoid(double x, double y, double z, float v0[3],
429                                 float v1[3], float v2[3], int light)
430 {
431   if(light) glEnable(GL_LIGHTING);
432   glPushMatrix();
433   GLfloat m[16] = {v0[0],      v0[1],      v0[2],      .0f,   v1[0], v1[1],
434                    v1[2],      .0f,        v2[0],      v2[1], v2[2], .0f,
435                    (GLfloat)x, (GLfloat)y, (GLfloat)z, 1.f};
436   glMultMatrixf(m);
437   glCallList(_displayLists + 0);
438   glPopMatrix();
439   glDisable(GL_LIGHTING);
440 }
441 
drawSphere(double size,double x,double y,double z,int light)442 void drawContext::drawSphere(double size, double x, double y, double z,
443                              int light)
444 {
445   double ss = size * pixel_equiv_x / s[0]; // size is in pixels
446   if(light) glEnable(GL_LIGHTING);
447   glPushMatrix();
448   glTranslated(x, y, z);
449   glScaled(ss, ss, ss);
450   glCallList(_displayLists + 0);
451   glPopMatrix();
452   glDisable(GL_LIGHTING);
453 }
454 
drawTaperedCylinder(double width,double val1,double val2,double ValMin,double ValMax,double * x,double * y,double * z,int light)455 void drawContext::drawTaperedCylinder(double width, double val1, double val2,
456                                       double ValMin, double ValMax, double *x,
457                                       double *y, double *z, int light)
458 {
459   if(light) glEnable(GL_LIGHTING);
460 
461   double dx = x[1] - x[0];
462   double dy = y[1] - y[0];
463   double dz = z[1] - z[0];
464   double const length = std::sqrt(dx * dx + dy * dy + dz * dz);
465   double fact = width * pixel_equiv_x / s[0] / (ValMax - ValMin);
466   double radius1 = (val1 - ValMin) * fact;
467   double radius2 = (val2 - ValMin) * fact;
468   double zdir[3] = {0., 0., 1.};
469   double vdir[3] = {dx / length, dy / length, dz / length};
470   double axis[3], phi;
471   prodve(zdir, vdir, axis);
472   double const cosphi = prosca(zdir, vdir);
473   if(!norme(axis)) {
474     axis[0] = 0.;
475     axis[1] = 1.;
476     axis[2] = 0.;
477   }
478   phi = 180. * myacos(cosphi) / M_PI;
479 
480   glPushMatrix();
481   glTranslated(x[0], y[0], z[0]);
482   glRotated(phi, axis[0], axis[1], axis[2]);
483   gluCylinder(_quadric, radius1, radius2, length,
484               CTX::instance()->quadricSubdivisions, 1);
485   glPopMatrix();
486 
487   glDisable(GL_LIGHTING);
488 }
489 
drawCylinder(double width,double * x,double * y,double * z,int light)490 void drawContext::drawCylinder(double width, double *x, double *y, double *z,
491                                int light)
492 {
493   if(light) glEnable(GL_LIGHTING);
494 
495   double dx = x[1] - x[0];
496   double dy = y[1] - y[0];
497   double dz = z[1] - z[0];
498   double const length = std::sqrt(dx * dx + dy * dy + dz * dz);
499   double radius = width * pixel_equiv_x / s[0];
500   double zdir[3] = {0., 0., 1.};
501   double vdir[3] = {dx / length, dy / length, dz / length};
502   double axis[3], phi;
503   prodve(zdir, vdir, axis);
504   double const cosphi = prosca(zdir, vdir);
505   if(!norme(axis)) {
506     axis[0] = 0.;
507     axis[1] = 1.;
508     axis[2] = 0.;
509   }
510   phi = 180. * myacos(cosphi) / M_PI;
511 
512   glPushMatrix();
513   glTranslated(x[0], y[0], z[0]);
514   glRotated(phi, axis[0], axis[1], axis[2]);
515   gluCylinder(_quadric, radius, radius, length,
516               CTX::instance()->quadricSubdivisions, 1);
517   glPopMatrix();
518 
519   glDisable(GL_LIGHTING);
520 }
521 
drawSimpleVector(int arrow,int fill,double x,double y,double z,double dx,double dy,double dz,double d,int light)522 static void drawSimpleVector(int arrow, int fill, double x, double y, double z,
523                              double dx, double dy, double dz, double d,
524                              int light)
525 {
526   double n[3], t[3], u[3];
527 
528   n[0] = dx / d;
529   n[1] = dy / d;
530   n[2] = dz / d;
531 
532   if((fabs(n[0]) >= fabs(n[1]) && fabs(n[0]) >= fabs(n[2])) ||
533      (fabs(n[1]) >= fabs(n[0]) && fabs(n[1]) >= fabs(n[2]))) {
534     t[0] = n[1];
535     t[1] = -n[0];
536     t[2] = 0.;
537   }
538   else {
539     t[0] = 0.;
540     t[1] = n[2];
541     t[2] = -n[1];
542   }
543 
544   double l = sqrt(t[0] * t[0] + t[1] * t[1] + t[2] * t[2]);
545   t[0] /= l;
546   t[1] /= l;
547   t[2] /= l;
548 
549   u[0] = n[1] * t[2] - n[2] * t[1];
550   u[1] = n[2] * t[0] - n[0] * t[2];
551   u[2] = n[0] * t[1] - n[1] * t[0];
552 
553   l = sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
554   u[0] /= l;
555   u[1] /= l;
556   u[2] /= l;
557 
558   double b = CTX::instance()->arrowRelHeadRadius * d;
559 
560   if(arrow) {
561     double f1 = CTX::instance()->arrowRelStemLength;
562     double f2 = (1 - 2. * CTX::instance()->arrowRelStemRadius) * f1; // hack :-)
563 
564     if(fill) {
565       glBegin(GL_LINES);
566       glVertex3d(x, y, z);
567       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
568       glEnd();
569 
570       if(light && fill) glEnable(GL_LIGHTING);
571       glBegin(GL_TRIANGLES);
572       if(light) glNormal3dv(u);
573       glVertex3d(x + dx, y + dy, z + dz);
574       glVertex3d(x + f2 * dx + b * (t[0]), y + f2 * dy + b * (t[1]),
575                  z + f2 * dz + b * (t[2]));
576       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
577 
578       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
579       glVertex3d(x + f2 * dx + b * (-t[0]), y + f2 * dy + b * (-t[1]),
580                  z + f2 * dz + b * (-t[2]));
581       glVertex3d(x + dx, y + dy, z + dz);
582 
583       if(light) glNormal3dv(t);
584       glVertex3d(x + dx, y + dy, z + dz);
585       glVertex3d(x + f2 * dx + b * (-u[0]), y + f2 * dy + b * (-u[1]),
586                  z + f2 * dz + b * (-u[2]));
587       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
588 
589       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
590       glVertex3d(x + f2 * dx + b * (u[0]), y + f2 * dy + b * (u[1]),
591                  z + f2 * dz + b * (u[2]));
592       glVertex3d(x + dx, y + dy, z + dz);
593       glEnd();
594       glDisable(GL_LIGHTING);
595     }
596     else {
597       glBegin(GL_LINE_STRIP);
598       glVertex3d(x, y, z);
599       glVertex3d(x + dx, y + dy, z + dz);
600       glVertex3d(x + f2 * dx + b * (t[0]), y + f2 * dy + b * (t[1]),
601                  z + f2 * dz + b * (t[2]));
602       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
603       glVertex3d(x + f2 * dx + b * (-t[0]), y + f2 * dy + b * (-t[1]),
604                  z + f2 * dz + b * (-t[2]));
605       glVertex3d(x + dx, y + dy, z + dz);
606       glVertex3d(x + f2 * dx + b * (-u[0]), y + f2 * dy + b * (-u[1]),
607                  z + f2 * dz + b * (-u[2]));
608       glVertex3d(x + f1 * dx, y + f1 * dy, z + f1 * dz);
609       glVertex3d(x + f2 * dx + b * (u[0]), y + f2 * dy + b * (u[1]),
610                  z + f2 * dz + b * (u[2]));
611       glVertex3d(x + dx, y + dy, z + dz);
612       glEnd();
613     }
614   }
615   else { // simple pyramid
616     if(fill) {
617       double top[3] = {x + dx, y + dy, z + dz};
618       double tp[3] = {x + b * t[0], y + b * t[1], z + b * t[2]};
619       double tm[3] = {x - b * t[0], y - b * t[1], z - b * t[2]};
620       double up[3] = {x + b * u[0], y + b * u[1], z + b * u[2]};
621       double um[3] = {x - b * u[0], y - b * u[1], z - b * u[2]};
622       double nn[3];
623 
624       if(light && fill) glEnable(GL_LIGHTING);
625       glBegin(GL_TRIANGLES);
626       if(light) {
627         normal3points(tm[0], tm[1], tm[2], um[0], um[1], um[2], top[0], top[1],
628                       top[2], nn);
629         glNormal3dv(nn);
630       }
631       glVertex3d(tm[0], tm[1], tm[2]);
632       glVertex3d(um[0], um[1], um[2]);
633       glVertex3d(top[0], top[1], top[2]);
634 
635       if(light) {
636         normal3points(um[0], um[1], um[2], tp[0], tp[1], tp[2], top[0], top[1],
637                       top[2], nn);
638         glNormal3dv(nn);
639       }
640       glVertex3d(um[0], um[1], um[2]);
641       glVertex3d(tp[0], tp[1], tp[2]);
642       glVertex3d(top[0], top[1], top[2]);
643 
644       if(light) {
645         normal3points(tp[0], tp[1], tp[2], up[0], up[1], up[2], top[0], top[1],
646                       top[2], nn);
647         glNormal3dv(nn);
648       }
649       glVertex3d(tp[0], tp[1], tp[2]);
650       glVertex3d(up[0], up[1], up[2]);
651       glVertex3d(top[0], top[1], top[2]);
652 
653       if(light) {
654         normal3points(up[0], up[1], up[2], tm[0], tm[1], tm[2], top[0], top[1],
655                       top[2], nn);
656         glNormal3dv(nn);
657       }
658       glVertex3d(up[0], up[1], up[2]);
659       glVertex3d(tm[0], tm[1], tm[2]);
660       glVertex3d(top[0], top[1], top[2]);
661       glEnd();
662       glDisable(GL_LIGHTING);
663     }
664     else {
665       glBegin(GL_LINE_LOOP);
666       glVertex3d(x + b * (t[0]), y + b * (t[1]), z + b * (t[2]));
667       glVertex3d(x + b * (-u[0]), y + b * (-u[1]), z + b * (-u[2]));
668       glVertex3d(x + b * (-t[0]), y + b * (-t[1]), z + b * (-t[2]));
669       glVertex3d(x + b * (u[0]), y + b * (u[1]), z + b * (u[2]));
670       glEnd();
671 
672       glBegin(GL_LINES);
673       glVertex3d(x + b * (t[0]), y + b * (t[1]), z + b * (t[2]));
674       glVertex3d(x + dx, y + dy, z + dz);
675 
676       glVertex3d(x + b * (-u[0]), y + b * (-u[1]), z + b * (-u[2]));
677       glVertex3d(x + dx, y + dy, z + dz);
678 
679       glVertex3d(x + b * (-t[0]), y + b * (-t[1]), z + b * (-t[2]));
680       glVertex3d(x + dx, y + dy, z + dz);
681 
682       glVertex3d(x + b * (u[0]), y + b * (u[1]), z + b * (u[2]));
683       glVertex3d(x + dx, y + dy, z + dz);
684       glEnd();
685     }
686   }
687 }
688 
drawArrow3d(double x,double y,double z,double dx,double dy,double dz,double length,int light)689 void drawContext::drawArrow3d(double x, double y, double z, double dx,
690                               double dy, double dz, double length, int light)
691 {
692   double zdir[3] = {0., 0., 1.};
693   double vdir[3] = {dx / length, dy / length, dz / length};
694   double axis[3];
695   prodve(zdir, vdir, axis);
696   double const cosphi = prosca(zdir, vdir);
697   if(!norme(axis)) {
698     axis[0] = 0.;
699     axis[1] = 1.;
700     axis[2] = 0.;
701   }
702   double phi = 180. * myacos(cosphi) / M_PI;
703 
704   if(light) glEnable(GL_LIGHTING);
705   glPushMatrix();
706   glTranslated(x, y, z);
707   glScaled(length, length, length);
708   glRotated(phi, axis[0], axis[1], axis[2]);
709   glCallList(_displayLists + 1);
710   glPopMatrix();
711   glDisable(GL_LIGHTING);
712 }
713 
drawVector(int Type,int Fill,double x,double y,double z,double dx,double dy,double dz,int light)714 void drawContext::drawVector(int Type, int Fill, double x, double y, double z,
715                              double dx, double dy, double dz, int light)
716 {
717   double length = sqrt(dx * dx + dy * dy + dz * dz);
718 
719   if(length == 0.0) return;
720 
721   switch(Type) {
722   case 1:
723     glBegin(GL_LINES);
724     glVertex3d(x, y, z);
725     glVertex3d(x + dx, y + dy, z + dz);
726     glEnd();
727     break;
728   case 6:
729     if(CTX::instance()->arrowRelHeadRadius) {
730       glBegin(GL_POINTS);
731       glVertex3d(x + dx, y + dy, z + dz);
732       glEnd();
733     }
734     glBegin(GL_LINES);
735     glVertex3d(x + dx, y + dy, z + dz);
736     // color gradient
737     glColor4ubv((GLubyte *)&CTX::instance()->color.bg);
738     glVertex3d(x, y, z);
739     glEnd();
740     break;
741   case 2: drawSimpleVector(1, Fill, x, y, z, dx, dy, dz, length, light); break;
742   case 3: drawSimpleVector(0, Fill, x, y, z, dx, dy, dz, length, light); break;
743   case 4:
744   default: drawArrow3d(x, y, z, dx, dy, dz, length, light); break;
745   }
746 }
747 
748 namespace {
749 class point {
750 public:
751   double x, y, z;
752   bool valid;
point()753   point() : x(0.), y(0.), z(0.), valid(false) {}
point(double xi,double yi,double zi)754   point(double xi, double yi, double zi) : x(xi), y(yi), z(zi), valid(true) {}
755 };
756 
757 class plane {
758 private:
759   double _a, _b, _c, _d;
760 
761 public:
plane(double a,double b,double c,double d)762   plane(double a, double b, double c, double d) : _a(a), _b(b), _c(c), _d(d) {}
val(point & p)763   double val(point &p) { return _a * p.x + _b * p.y + _c * p.z + _d; };
intersect(point & p1,point & p2)764   point intersect(point &p1, point &p2)
765   {
766     double v1 = val(p1), v2 = val(p2);
767     if(fabs(v1) < 1.e-12) {
768       if(fabs(v2) < 1.e-12)
769         return point();
770       else
771         return point(p1.x, p1.y, p1.z);
772     }
773     else if(fabs(v2) < 1.e-12) {
774       return point(p2.x, p2.y, p2.z);
775     }
776     else if(v1 * v2 < 0.) {
777       double coef = -v1 / (v2 - v1);
778       return point(coef * (p2.x - p1.x) + p1.x, coef * (p2.y - p1.y) + p1.y,
779                    coef * (p2.z - p1.z) + p1.z);
780     }
781     else
782       return point();
783   }
784 };
785 }
786 
drawBox(double xmin,double ymin,double zmin,double xmax,double ymax,double zmax,bool labels)787 void drawContext::drawBox(double xmin, double ymin, double zmin, double xmax,
788                           double ymax, double zmax, bool labels)
789 {
790   glBegin(GL_LINE_LOOP);
791   glVertex3d(xmin, ymin, zmin);
792   glVertex3d(xmax, ymin, zmin);
793   glVertex3d(xmax, ymax, zmin);
794   glVertex3d(xmin, ymax, zmin);
795   glEnd();
796   glBegin(GL_LINE_LOOP);
797   glVertex3d(xmin, ymin, zmax);
798   glVertex3d(xmax, ymin, zmax);
799   glVertex3d(xmax, ymax, zmax);
800   glVertex3d(xmin, ymax, zmax);
801   glEnd();
802   glBegin(GL_LINES);
803   glVertex3d(xmin, ymin, zmin);
804   glVertex3d(xmin, ymin, zmax);
805   glVertex3d(xmax, ymin, zmin);
806   glVertex3d(xmax, ymin, zmax);
807   glVertex3d(xmax, ymax, zmin);
808   glVertex3d(xmax, ymax, zmax);
809   glVertex3d(xmin, ymax, zmin);
810   glVertex3d(xmin, ymax, zmax);
811   glEnd();
812   if(labels) {
813     char label[256];
814     double offset = 0.3 * CTX::instance()->glFontSize * pixel_equiv_x;
815     sprintf(label, "(%g,%g,%g)", xmin, ymin, zmin);
816     drawString(label, xmin + offset / s[0], ymin + offset / s[1],
817                zmin + offset / s[2]);
818     sprintf(label, "(%g,%g,%g)", xmax, ymax, zmax);
819     drawString(label, xmax + offset / s[0], ymax + offset / s[1],
820                zmax + offset / s[2]);
821   }
822 }
823 
drawPlaneInBoundingBox(double xmin,double ymin,double zmin,double xmax,double ymax,double zmax,double a,double b,double c,double d,int shade)824 void drawContext::drawPlaneInBoundingBox(double xmin, double ymin, double zmin,
825                                          double xmax, double ymax, double zmax,
826                                          double a, double b, double c, double d,
827                                          int shade)
828 {
829   plane pl(a, b, c, d);
830   point p1(xmin, ymin, zmin), p2(xmax, ymin, zmin);
831   point p3(xmax, ymax, zmin), p4(xmin, ymax, zmin);
832   point p5(xmin, ymin, zmax), p6(xmax, ymin, zmax);
833   point p7(xmax, ymax, zmax), p8(xmin, ymax, zmax);
834 
835   point edge[12];
836   edge[0] = pl.intersect(p1, p2);
837   edge[1] = pl.intersect(p1, p4);
838   edge[2] = pl.intersect(p1, p5);
839   edge[3] = pl.intersect(p2, p3);
840   edge[4] = pl.intersect(p2, p6);
841   edge[5] = pl.intersect(p3, p4);
842   edge[6] = pl.intersect(p3, p7);
843   edge[7] = pl.intersect(p4, p8);
844   edge[8] = pl.intersect(p5, p6);
845   edge[9] = pl.intersect(p5, p8);
846   edge[10] = pl.intersect(p6, p7);
847   edge[11] = pl.intersect(p7, p8);
848 
849   int face[6][4] = {{0, 2, 4, 8},  {0, 1, 3, 5},  {1, 2, 7, 9},
850                     {3, 4, 6, 10}, {5, 6, 7, 11}, {8, 9, 10, 11}};
851 
852   double n[3] = {a, b, c}, ll = 50;
853   norme(n);
854   if(CTX::instance()->arrowRelStemRadius)
855     ll = CTX::instance()->lineWidth / CTX::instance()->arrowRelStemRadius;
856   n[0] *= ll * pixel_equiv_x / s[0];
857   n[1] *= ll * pixel_equiv_x / s[1];
858   n[2] *= ll * pixel_equiv_x / s[2];
859   double length = sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
860 
861   int n_shade = 0;
862   point p_shade[24];
863 
864   for(int i = 0; i < 6; i++) {
865     int nb = 0;
866     point p[4];
867     for(int j = 0; j < 4; j++) {
868       if(edge[face[i][j]].valid == true) p[nb++] = edge[face[i][j]];
869     }
870     if(nb > 1) {
871       for(int j = 1; j < nb; j++) {
872         double xx[2] = {p[j].x, p[j - 1].x};
873         double yy[2] = {p[j].y, p[j - 1].y};
874         double zz[2] = {p[j].z, p[j - 1].z};
875         drawCylinder(CTX::instance()->lineWidth, xx, yy, zz, 1);
876       }
877       for(int j = 0; j < nb; j++) {
878         drawArrow3d(p[j].x, p[j].y, p[j].z, n[0], n[1], n[2], length, 1);
879         if(shade) {
880           p_shade[n_shade].x = p[j].x;
881           p_shade[n_shade].y = p[j].y;
882           p_shade[n_shade].z = p[j].z;
883           n_shade++;
884         }
885       }
886     }
887   }
888 
889   if(shade) {
890     // disable two-side lighting beacuse polygon can overlap itself
891     GLboolean twoside;
892     glGetBooleanv(GL_LIGHT_MODEL_TWO_SIDE, &twoside);
893     glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
894     glEnable(GL_LIGHTING);
895     glBegin(GL_POLYGON);
896     glNormal3d(n[0], n[1], n[2]);
897     for(int j = 0; j < n_shade; j++) {
898       glVertex3d(p_shade[j].x, p_shade[j].y, p_shade[j].z);
899     }
900     glEnd();
901     glDisable(GL_LIGHTING);
902     glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, twoside);
903   }
904 }
905