1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2008-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <sstream>
31 
32 #if defined (HAVE_WINDOWS_H)
33 #  define WIN32_LEAN_AND_MEAN
34 #  include <windows.h>
35 #endif
36 
37 #include "lo-mappers.h"
38 #include "oct-locbuf.h"
39 #include "oct-refcount.h"
40 
41 #include "errwarn.h"
42 #include "gl-render.h"
43 #include "interpreter-private.h"
44 #include "oct-opengl.h"
45 #include "text-renderer.h"
46 
47 namespace octave
48 {
49 #if defined (HAVE_OPENGL)
50 
51   static int
next_power_of_2(int n)52   next_power_of_2 (int n)
53   {
54     int m = 1;
55 
56     while (m < n && m < std::numeric_limits<int>::max ())
57       m <<= 1;
58 
59     return m;
60   }
61 
62 #define LIGHT_MODE GL_FRONT_AND_BACK
63 
64   // Use symbolic names for axes
65   enum
66   {
67     X_AXIS,
68     Y_AXIS,
69     Z_AXIS
70   };
71 
72   // Use symbolic names for color mode
73   enum
74   {
75     UNIFORM,
76     FLAT,
77     INTERP,
78     TEXTURE
79   };
80 
81   // Use symbolic names for lighting
82   enum
83   {
84     NONE,
85     //FLAT,  // Already declared in anonymous enum for color mode
86     GOURAUD = 2
87   };
88 
89   // Win32 API requires the CALLBACK attributes for
90   // GLU callback functions.  Define it to empty on
91   // other platforms.
92 #if ! defined (CALLBACK)
93 #  define CALLBACK
94 #endif
95 
96   class
97   opengl_texture
98   {
99   protected:
100     class texture_rep
101     {
102     public:
texture_rep(opengl_functions & glfcns)103       texture_rep (opengl_functions& glfcns)
104         : m_glfcns (glfcns), id (), w (), h (), tw (), th (), tx (), ty (),
105           valid (false), count (1)
106       { }
107 
texture_rep(opengl_functions & glfcns,GLuint id_arg,int w_arg,int h_arg,int tw_arg,int th_arg)108       texture_rep (opengl_functions& glfcns, GLuint id_arg,
109                    int w_arg, int h_arg, int tw_arg, int th_arg)
110         : m_glfcns (glfcns), id (id_arg), w (w_arg), h (h_arg),
111           tw (tw_arg), th (th_arg), tx (double(w)/tw), ty (double(h)/th),
112           valid (true), count (1)
113       { }
114 
~texture_rep(void)115       ~texture_rep (void)
116       {
117         if (valid)
118           m_glfcns.glDeleteTextures (1, &id);
119       }
120 
bind(int mode) const121       void bind (int mode) const
122       { if (valid) m_glfcns.glBindTexture (mode, id); }
123 
tex_coord(double q,double r) const124       void tex_coord (double q, double r) const
125       { if (valid) m_glfcns.glTexCoord2d (q*tx, r*ty); }
126 
127       opengl_functions& m_glfcns;
128       GLuint id;
129       int w, h;
130       int tw, th;
131       double tx, ty;
132       bool valid;
133       refcount<octave_idx_type> count;
134     };
135 
136     texture_rep *rep;
137 
138   private:
opengl_texture(texture_rep * _rep)139     opengl_texture (texture_rep *_rep) : rep (_rep) { }
140 
141   public:
opengl_texture(opengl_functions & glfcns)142     opengl_texture (opengl_functions& glfcns)
143       : rep (new texture_rep (glfcns)) { }
144 
opengl_texture(const opengl_texture & tx)145     opengl_texture (const opengl_texture& tx)
146       : rep (tx.rep)
147     {
148       rep->count++;
149     }
150 
~opengl_texture(void)151     ~opengl_texture (void)
152     {
153       if (--rep->count == 0)
154         delete rep;
155     }
156 
operator =(const opengl_texture & tx)157     opengl_texture& operator = (const opengl_texture& tx)
158     {
159       if (&tx != this)
160         {
161           if (--rep->count == 0)
162             delete rep;
163 
164           rep = tx.rep;
165           rep->count++;
166         }
167 
168       return *this;
169     }
170 
171     static opengl_texture create (opengl_functions& glfcns,
172                                   const octave_value& data);
173 
bind(int mode=GL_TEXTURE_2D) const174     void bind (int mode = GL_TEXTURE_2D) const
175     { rep->bind (mode); }
176 
tex_coord(double q,double r) const177     void tex_coord (double q, double r) const
178     { rep->tex_coord (q, r); }
179 
is_valid(void) const180     bool is_valid (void) const
181     { return rep->valid; }
182   };
183 
184   opengl_texture
create(opengl_functions & glfcns,const octave_value & data)185   opengl_texture::create (opengl_functions& glfcns, const octave_value& data)
186   {
187     opengl_texture retval (glfcns);
188 
189     dim_vector dv (data.dims ());
190 
191     // Expect RGB data
192     if (dv.ndims () == 3 && dv(2) == 3)
193       {
194         // FIXME: dim_vectors hold octave_idx_type values.
195         //        Should we check for dimensions larger than intmax?
196         int h, w, tw, th;
197         h = dv(0), w = dv(1);
198 
199         // Return early if the image data are larger than the texture
200         // can hold
201         int max_size;
202         glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size);
203         static bool warned = false;
204         if (h > max_size || w > max_size)
205           {
206             if (! warned)
207               {
208                 warning ("opengl_texture::create: the opengl library in use "
209                          "doesn't support images with either dimension larger "
210                          "than %d. Not rendering.", max_size);
211                 warned = true;
212               }
213 
214             return opengl_texture (glfcns);
215           }
216 
217         GLuint id;
218         bool ok = true;
219 
220         tw = next_power_of_2 (w);
221         th = next_power_of_2 (h);
222 
223         glfcns.glGenTextures (1, &id);
224         glfcns.glBindTexture (GL_TEXTURE_2D, id);
225 
226         if (data.is_double_type ())
227           {
228             const NDArray xdata = data.array_value ();
229 
230             OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th));
231 
232             for (int i = 0; i < h; i++)
233               {
234                 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
235                   {
236                     a[idx]   = xdata(i,j,0);
237                     a[idx+1] = xdata(i,j,1);
238                     a[idx+2] = xdata(i,j,2);
239                   }
240               }
241 
242             glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB,
243                                  GL_FLOAT, a);
244           }
245 
246         else if (data.is_single_type ())
247           {
248             const FloatNDArray xdata = data.float_array_value ();
249 
250             OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th));
251 
252             for (int i = 0; i < h; i++)
253               {
254                 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
255                   {
256                     a[idx]   = xdata(i,j,0);
257                     a[idx+1] = xdata(i,j,1);
258                     a[idx+2] = xdata(i,j,2);
259                   }
260               }
261 
262             glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB,
263                                  GL_FLOAT, a);
264           }
265         else if (data.is_uint16_type ())
266           {
267             const uint16NDArray xdata = data.uint16_array_value ();
268 
269             OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th));
270 
271             for (int i = 0; i < h; i++)
272               {
273                 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
274                   {
275                     a[idx]   = xdata(i,j,0);
276                     a[idx+1] = xdata(i,j,1);
277                     a[idx+2] = xdata(i,j,2);
278                   }
279               }
280 
281             glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
282                                  GL_RGB, GL_UNSIGNED_SHORT, a);
283           }
284         else if (data.is_uint8_type ())
285           {
286             const uint8NDArray xdata = data.uint8_array_value ();
287 
288             OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th));
289 
290             for (int i = 0; i < h; i++)
291               {
292                 for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
293                   {
294                     a[idx]   = xdata(i,j,0);
295                     a[idx+1] = xdata(i,j,1);
296                     a[idx+2] = xdata(i,j,2);
297                   }
298               }
299 
300             glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
301                                  GL_RGB, GL_UNSIGNED_BYTE, a);
302           }
303         else
304           {
305             ok = false;
306             warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16");
307           }
308 
309         if (ok)
310           {
311             glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
312             glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
313 
314             if (glfcns.glGetError () != GL_NO_ERROR)
315               warning ("opengl_texture::create: OpenGL error while generating texture data");
316             else
317               retval = opengl_texture (new texture_rep (glfcns, id, w, h, tw, th));
318           }
319       }
320     else
321       warning ("opengl_texture::create: invalid texture data size");
322 
323     return retval;
324   }
325 
326   class
327   opengl_tessellator
328   {
329   public:
330 #if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS)
331     typedef GLvoid (CALLBACK *fcn) (...);
332 #else
333     typedef void (CALLBACK *fcn) (void);
334 #endif
335 
336   public:
337 
opengl_tessellator(void)338     opengl_tessellator (void) : glu_tess (nullptr), fill () { init (); }
339 
340     // No copying!
341 
342     opengl_tessellator (const opengl_tessellator&) = delete;
343 
344     opengl_tessellator operator = (const opengl_tessellator&) = delete;
345 
~opengl_tessellator(void)346     virtual ~opengl_tessellator (void)
347     { if (glu_tess) gluDeleteTess (glu_tess); }
348 
begin_polygon(bool filled=true)349     void begin_polygon (bool filled = true)
350     {
351       gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY,
352                        (filled ? GL_FALSE : GL_TRUE));
353       fill = filled;
354       gluTessBeginPolygon (glu_tess, this);
355     }
356 
end_polygon(void) const357     void end_polygon (void) const
358     { gluTessEndPolygon (glu_tess); }
359 
begin_contour(void) const360     void begin_contour (void) const
361     { gluTessBeginContour (glu_tess); }
362 
end_contour(void) const363     void end_contour (void) const
364     { gluTessEndContour (glu_tess); }
365 
add_vertex(double * loc,void * data) const366     void add_vertex (double *loc, void *data) const
367     { gluTessVertex (glu_tess, loc, data); }
368 
369   protected:
begin(GLenum)370     virtual void begin (GLenum /*type*/) { }
371 
end(void)372     virtual void end (void) { }
373 
vertex(void *)374     virtual void vertex (void * /*data*/) { }
375 
combine(GLdouble[3],void * [4],GLfloat[4],void **)376     virtual void combine (GLdouble [3] /*c*/, void * [4] /*data*/,
377                           GLfloat  [4] /*w*/, void ** /*out_data*/) { }
378 
edge_flag(GLboolean)379     virtual void edge_flag (GLboolean /*flag*/) { }
380 
error(GLenum err)381     virtual void error (GLenum err)
382     { ::error ("OpenGL tessellation error (%d)", err); }
383 
init(void)384     virtual void init (void)
385     {
386       glu_tess = gluNewTess ();
387 
388       gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA,
389                        reinterpret_cast<fcn> (tess_begin));
390       gluTessCallback (glu_tess, GLU_TESS_END_DATA,
391                        reinterpret_cast<fcn> (tess_end));
392       gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA,
393                        reinterpret_cast<fcn> (tess_vertex));
394       gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA,
395                        reinterpret_cast<fcn> (tess_combine));
396       gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA,
397                        reinterpret_cast<fcn> (tess_edge_flag));
398       gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA,
399                        reinterpret_cast<fcn> (tess_error));
400     }
401 
is_filled(void) const402     bool is_filled (void) const { return fill; }
403 
404   private:
tess_begin(GLenum type,void * t)405     static void CALLBACK tess_begin (GLenum type, void *t)
406     { reinterpret_cast<opengl_tessellator *> (t)->begin (type); }
407 
tess_end(void * t)408     static void CALLBACK tess_end (void *t)
409     { reinterpret_cast<opengl_tessellator *> (t)->end (); }
410 
tess_vertex(void * v,void * t)411     static void CALLBACK tess_vertex (void *v, void *t)
412     { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); }
413 
tess_combine(GLdouble c[3],void * v[4],GLfloat w[4],void ** out,void * t)414     static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4],
415                                        void **out,  void *t)
416     { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); }
417 
tess_edge_flag(GLboolean flag,void * t)418     static void CALLBACK tess_edge_flag (GLboolean flag, void *t)
419     { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); }
420 
tess_error(GLenum err,void * t)421     static void CALLBACK tess_error (GLenum err, void *t)
422     { reinterpret_cast<opengl_tessellator *> (t)->error (err); }
423 
424   private:
425 
426     GLUtesselator *glu_tess;
427     bool fill;
428   };
429 
430   class
431   vertex_data
432   {
433   public:
434     class vertex_data_rep
435     {
436     public:
437       Matrix coords;
438       Matrix color;
439       Matrix vertex_normal;
440       Matrix face_normal;
441       double alpha;
442       float ambient;
443       float diffuse;
444       float specular;
445       float specular_exp;
446       float specular_color_refl;
447 
448       // reference counter
449       refcount<octave_idx_type> count;
450 
vertex_data_rep(void)451       vertex_data_rep (void)
452         : coords (), color (), vertex_normal (), face_normal (), alpha (),
453           ambient (), diffuse (), specular (), specular_exp (),
454           specular_color_refl (), count (1) { }
455 
vertex_data_rep(const Matrix & c,const Matrix & col,const Matrix & vn,const Matrix & fn,double a,float as,float ds,float ss,float se,float scr)456       vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& vn,
457                        const Matrix& fn, double a, float as, float ds, float ss,
458                        float se, float scr)
459         : coords (c), color (col), vertex_normal (vn), face_normal (fn),
460           alpha (a), ambient (as), diffuse (ds), specular (ss),
461           specular_exp (se), specular_color_refl (scr), count (1) { }
462     };
463 
464   private:
465     vertex_data_rep *rep;
466 
nil_rep(void) const467     vertex_data_rep * nil_rep (void) const
468     {
469       static vertex_data_rep *nr = new vertex_data_rep ();
470 
471       return nr;
472     }
473 
474   public:
vertex_data(void)475     vertex_data (void) : rep (nil_rep ())
476     { rep->count++; }
477 
vertex_data(const vertex_data & v)478     vertex_data (const vertex_data& v) : rep (v.rep)
479     { rep->count++; }
480 
vertex_data(const Matrix & c,const Matrix & col,const Matrix & vn,const Matrix & fn,double a,float as,float ds,float ss,float se,float scr)481     vertex_data (const Matrix& c, const Matrix& col, const Matrix& vn,
482                  const Matrix& fn, double a, float as, float ds, float ss,
483                  float se, float scr)
484       : rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr))
485     { }
486 
vertex_data(vertex_data_rep * new_rep)487     vertex_data (vertex_data_rep *new_rep)
488       : rep (new_rep) { }
489 
~vertex_data(void)490     ~vertex_data (void)
491     {
492       if (--rep->count == 0)
493         delete rep;
494     }
495 
operator =(const vertex_data & v)496     vertex_data& operator = (const vertex_data& v)
497     {
498       if (&v != this)
499         {
500           if (--rep->count == 0)
501             delete rep;
502 
503           rep = v.rep;
504           rep->count++;
505         }
506 
507       return *this;
508     }
509 
get_rep(void) const510     vertex_data_rep * get_rep (void) const { return rep; }
511   };
512 
513   class
514   opengl_renderer::patch_tessellator : public opengl_tessellator
515   {
516   public:
patch_tessellator(opengl_renderer * r,int cmode,int lmode,bool fl,float idx=0.0)517     patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl,
518                        float idx = 0.0)
519       : opengl_tessellator (), renderer (r),
520         color_mode (cmode), light_mode (lmode), face_lighting (fl), index (idx),
521         first (true), tmp_vdata ()
522     { }
523 
524   protected:
begin(GLenum type)525     void begin (GLenum type)
526     {
527       opengl_functions& glfcns = renderer->get_opengl_functions ();
528 
529       //printf ("patch_tessellator::begin (%d)\n", type);
530       first = true;
531 
532       if (color_mode == INTERP || light_mode == GOURAUD)
533         glfcns.glShadeModel (GL_SMOOTH);
534       else
535         glfcns.glShadeModel (GL_FLAT);
536 
537       if (is_filled ())
538         renderer->set_polygon_offset (true, index);
539 
540       glfcns.glBegin (type);
541     }
542 
end(void)543     void end (void)
544     {
545       opengl_functions& glfcns = renderer->get_opengl_functions ();
546 
547       //printf ("patch_tessellator::end\n");
548       glfcns.glEnd ();
549       renderer->set_polygon_offset (false);
550     }
551 
vertex(void * data)552     void vertex (void *data)
553     {
554       opengl_functions& glfcns = renderer->get_opengl_functions ();
555 
556       vertex_data::vertex_data_rep *v
557         = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
558       //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->coords(2));
559 
560       // NOTE: OpenGL can re-order vertices.  For "flat" coloring of FaceColor
561       // the first vertex must be identified in the draw_patch routine.
562 
563       if (color_mode == INTERP || (color_mode == FLAT && ! is_filled ()))
564         {
565           Matrix col = v->color;
566 
567           if (col.numel () == 3)
568             {
569               glfcns.glColor4d (col(0), col(1), col(2), v->alpha);
570               if (light_mode > 0)
571                 {
572                   // edge lighting only uses ambient light
573                   float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };;
574 
575                   if (face_lighting)
576                     for (int k = 0; k < 3; k++)
577                       buf[k] = v->specular * (v->specular_color_refl +
578                                               (1 - v->specular_color_refl) * col(k));
579                   glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
580 
581                   if (face_lighting)
582                     for (int k = 0; k < 3; k++)
583                       buf[k] = (v->diffuse * col(k));
584                   glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf);
585 
586                   for (int k = 0; k < 3; k++)
587                     buf[k] = (v->ambient * col(k));
588                   glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
589                 }
590             }
591         }
592 
593       if (light_mode == FLAT && first)
594         glfcns.glNormal3dv (v->face_normal.data ());
595       else if (light_mode == GOURAUD)
596         glfcns.glNormal3dv (v->vertex_normal.data ());
597 
598       glfcns.glVertex3dv (v->coords.data ());
599 
600       first = false;
601     }
602 
combine(GLdouble xyz[3],void * data[4],GLfloat w[4],void ** out_data)603     void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data)
604     {
605       //printf ("patch_tessellator::combine\n");
606 
607       vertex_data::vertex_data_rep *v[4];
608       int vmax = 4;
609 
610       for (int i = 0; i < 4; i++)
611         {
612           v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]);
613 
614           if (vmax == 4 && ! v[i])
615             vmax = i;
616         }
617 
618       Matrix vv (1, 3, 0.0);
619       Matrix cc;
620       Matrix vnn (1, 3, 0.0);
621       Matrix fnn (1, 3, 0.0);
622       double aa = 0.0;
623 
624       vv(0) = xyz[0];
625       vv(1) = xyz[1];
626       vv(2) = xyz[2];
627 
628       if (v[0]->color.numel ())
629         {
630           cc.resize (1, 3, 0.0);
631           for (int ic = 0; ic < 3; ic++)
632             for (int iv = 0; iv < vmax; iv++)
633               cc(ic) += (w[iv] * v[iv]->color (ic));
634         }
635 
636       if (v[0]->vertex_normal.numel () > 0)
637         {
638           for (int in = 0; in < 3; in++)
639             for (int iv = 0; iv < vmax; iv++)
640               vnn(in) += (w[iv] * v[iv]->vertex_normal (in));
641         }
642 
643       if (v[0]->face_normal.numel () > 0)
644         {
645           for (int in = 0; in < 3; in++)
646             for (int iv = 0; iv < vmax; iv++)
647               fnn(in) += (w[iv] * v[iv]->face_normal (in));
648         }
649 
650       for (int iv = 0; iv < vmax; iv++)
651         aa += (w[iv] * v[iv]->alpha);
652 
653       vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->ambient, v[0]->diffuse,
654                          v[0]->specular, v[0]->specular_exp,
655                          v[0]->specular_color_refl);
656       tmp_vdata.push_back (new_v);
657 
658       *out_data = new_v.get_rep ();
659     }
660 
661   private:
662 
663     // No copying!
664 
665     patch_tessellator (const patch_tessellator&) = delete;
666 
667     patch_tessellator& operator = (const patch_tessellator&) = delete;
668 
669     opengl_renderer *renderer;
670     int color_mode;
671     int light_mode;
672     bool face_lighting;
673     int index;
674     bool first;
675     std::list<vertex_data> tmp_vdata;
676   };
677 
678 #else
679 
680   class
681   opengl_renderer::patch_tessellator
682   {
683     // Dummy class.
684   };
685 
686 #endif
687 
opengl_renderer(opengl_functions & glfcns)688   opengl_renderer::opengl_renderer (opengl_functions& glfcns)
689     : m_glfcns (glfcns), xmin (), xmax (), ymin (), ymax (), zmin (), zmax (),
690       m_devpixratio (1.0), xform (), toolkit (), xZ1 (), xZ2 (), marker_id (),
691       filled_marker_id (), camera_pos (), camera_dir (), view_vector (),
692       interpreter ("none"), txt_renderer (), m_current_light (0),
693       m_max_lights (0), selecting (false), m_printing (false)
694   {
695     // This constructor will fail if we don't have OpenGL or if the data
696     // types we assumed in our public interface aren't compatible with the
697     // OpenGL types.
698 
699 #if defined (HAVE_OPENGL)
700 
701     // Ensure that we can't request an image larger than OpenGL can handle.
702     // FIXME: should we check signed vs. unsigned?
703 
704     static bool ok = (sizeof (int) <= sizeof (GLsizei));
705 
706     if (! ok)
707       error ("the size of GLsizei is smaller than the size of int");
708 
709 #else
710 
711     err_disabled_feature ("opengl_renderer", "OpenGL");
712 
713 #endif
714   }
715 
716   void
draw(const graphics_object & go,bool toplevel)717   opengl_renderer::draw (const graphics_object& go, bool toplevel)
718   {
719     if (! go.valid_object ())
720       return;
721 
722     const base_properties& props = go.get_properties ();
723 
724     if (! toolkit)
725       toolkit = props.get_toolkit ();
726 
727     if (go.isa ("figure"))
728       draw_figure (dynamic_cast<const figure::properties&> (props));
729     else if (go.isa ("axes"))
730       draw_axes (dynamic_cast<const axes::properties&> (props));
731     else if (go.isa ("line"))
732       draw_line (dynamic_cast<const line::properties&> (props));
733     else if (go.isa ("surface"))
734       draw_surface (dynamic_cast<const surface::properties&> (props));
735     else if (go.isa ("patch"))
736       draw_patch (dynamic_cast<const patch::properties&> (props));
737     else if (go.isa ("light"))
738       draw_light (dynamic_cast<const light::properties&> (props));
739     else if (go.isa ("hggroup"))
740       draw_hggroup (dynamic_cast<const hggroup::properties&> (props));
741     else if (go.isa ("text"))
742       draw_text (dynamic_cast<const text::properties&> (props));
743     else if (go.isa ("image"))
744       draw_image (dynamic_cast<const image::properties&> (props));
745     else if (go.isa ("uimenu") || go.isa ("uicontrol")
746              || go.isa ("uicontextmenu") || go.isa ("uitoolbar")
747              || go.isa ("uipushtool") || go.isa ("uitoggletool")
748              || go.isa ("uitable"))
749       ; // SKIP
750     else if (go.isa ("uipanel"))
751       {
752         if (toplevel)
753           draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go);
754       }
755     else if (go.isa ("uibuttongroup"))
756       {
757         if (toplevel)
758           draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go);
759       }
760     else
761       {
762         warning ("opengl_renderer: cannot render object of type '%s'",
763                  props.graphics_object_name ().c_str ());
764       }
765 
766 #if defined (HAVE_OPENGL)
767 
768     GLenum gl_error = m_glfcns.glGetError ();
769     if (gl_error)
770       warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object",
771                gluErrorString (gl_error), gl_error, props.graphics_object_name ().c_str ());
772 
773 #endif
774   }
775 
776   void
draw_figure(const figure::properties & props)777   opengl_renderer::draw_figure (const figure::properties& props)
778   {
779     m_printing = props.is___printing__ ();
780 
781     // Initialize OpenGL context
782     init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ());
783 
784 #if defined (HAVE_OPENGL)
785 
786     props.set___gl_extensions__ (get_string (GL_EXTENSIONS));
787     props.set___gl_renderer__ (get_string (GL_RENDERER));
788     props.set___gl_vendor__ (get_string (GL_VENDOR));
789     props.set___gl_version__ (get_string (GL_VERSION));
790 
791 #endif
792 
793     // Draw children
794 
795     draw (props.get_all_children (), false);
796   }
797 
798   void
draw_uipanel(const uipanel::properties & props,const graphics_object & go)799   opengl_renderer::draw_uipanel (const uipanel::properties& props,
800                                  const graphics_object& go)
801   {
802     graphics_object fig = go.get_ancestor ("figure");
803     const figure::properties& figProps
804       = dynamic_cast<const figure::properties&> (fig.get_properties ());
805 
806     // Initialize OpenGL context
807 
808     init_gl_context (figProps.is_graphicssmoothing (),
809                      props.get_backgroundcolor_rgb ());
810 
811     // Draw children
812 
813     draw (props.get_all_children (), false);
814   }
815 
816   void
draw_uibuttongroup(const uibuttongroup::properties & props,const graphics_object & go)817   opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props,
818                                        const graphics_object& go)
819   {
820     graphics_object fig = go.get_ancestor ("figure");
821     const figure::properties& figProps
822       = dynamic_cast<const figure::properties&> (fig.get_properties ());
823 
824     // Initialize OpenGL context
825 
826     init_gl_context (figProps.is_graphicssmoothing (),
827                      props.get_backgroundcolor_rgb ());
828 
829     // Draw children
830 
831     draw (props.get_all_children (), false);
832   }
833 
834   void
init_gl_context(bool enhanced,const Matrix & c)835   opengl_renderer::init_gl_context (bool enhanced, const Matrix& c)
836   {
837 #if defined (HAVE_OPENGL)
838 
839     // Initialize OpenGL context
840 
841     m_glfcns.glEnable (GL_DEPTH_TEST);
842     m_glfcns.glDepthFunc (GL_LEQUAL);
843     m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
844     m_glfcns.glAlphaFunc (GL_GREATER, 0.0f);
845     m_glfcns.glEnable (GL_NORMALIZE);
846     m_glfcns.glEnable (GL_BLEND);
847 
848     if (enhanced)
849       {
850         m_glfcns.glEnable (GL_MULTISAMPLE);
851         bool has_multisample = false;
852         if (! m_glfcns.glGetError ())
853           {
854             GLint iMultiSample, iNumSamples;
855             m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample);
856             m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples);
857             if (iMultiSample == GL_TRUE && iNumSamples > 0)
858               has_multisample = true;
859           }
860 
861         if (! has_multisample)
862           {
863             // MultiSample not implemented.  Use old-style anti-aliasing
864             m_glfcns.glDisable (GL_MULTISAMPLE);
865             // Disabling GL_MULTISAMPLE will raise a gl error if it is not
866             // implemented.  Thus, call glGetError to reset the error state.
867             m_glfcns.glGetError ();
868 
869             m_glfcns.glEnable (GL_LINE_SMOOTH);
870             m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
871           }
872       }
873     else
874       {
875         m_glfcns.glDisable (GL_LINE_SMOOTH);
876       }
877 
878     // Clear background
879 
880     if (c.numel () >= 3)
881       {
882         m_glfcns.glClearColor (c(0), c(1), c(2), 1);
883         m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
884       }
885 
886     GLenum gl_error = m_glfcns.glGetError ();
887     if (gl_error)
888       warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context",
889                gluErrorString (gl_error), gl_error);
890 
891 #else
892 
893     octave_unused_parameter (enhanced);
894     octave_unused_parameter (c);
895 
896     // This shouldn't happen because construction of opengl_renderer
897     // objects is supposed to be impossible if OpenGL is not available.
898 
899     panic_impossible ();
900 
901 #endif
902   }
903 
904   void
render_grid(const double linewidth,const std::string & gridstyle,const Matrix & gridcolor,const double gridalpha,const Matrix & ticks,double lim1,double lim2,double p1,double p1N,double p2,double p2N,int xyz,bool is_3D)905   opengl_renderer::render_grid (const double linewidth,
906                                 const std::string& gridstyle,
907                                 const Matrix& gridcolor, const double gridalpha,
908                                 const Matrix& ticks, double lim1, double lim2,
909                                 double p1, double p1N, double p2, double p2N,
910                                 int xyz, bool is_3D)
911   {
912 #if defined (HAVE_OPENGL)
913 
914     m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha);
915     set_linestyle (gridstyle, true, linewidth);
916     m_glfcns.glBegin (GL_LINES);
917     for (int i = 0; i < ticks.numel (); i++)
918       {
919         double val = ticks(i);
920         if (lim1 <= val && val <= lim2)
921           {
922             if (xyz == X_AXIS)
923               {
924                 m_glfcns.glVertex3d (val, p1N, p2);
925                 m_glfcns.glVertex3d (val, p1, p2);
926                 if (is_3D)
927                   {
928                     m_glfcns.glVertex3d (val, p1, p2N);
929                     m_glfcns.glVertex3d (val, p1, p2);
930                   }
931               }
932             else if (xyz == Y_AXIS)
933               {
934                 m_glfcns.glVertex3d (p1N, val, p2);
935                 m_glfcns.glVertex3d (p1, val, p2);
936                 if (is_3D)
937                   {
938                     m_glfcns.glVertex3d (p1, val, p2N);
939                     m_glfcns.glVertex3d (p1, val, p2);
940                   }
941               }
942             else if (xyz == Z_AXIS)
943               {
944                 m_glfcns.glVertex3d (p1N, p2, val);
945                 m_glfcns.glVertex3d (p1, p2, val);
946                 m_glfcns.glVertex3d (p1, p2N, val);
947                 m_glfcns.glVertex3d (p1, p2, val);
948               }
949           }
950       }
951     m_glfcns.glEnd ();
952     set_linestyle ("-");  // Disable LineStipple
953     double black[3] = {0, 0, 0};
954     m_glfcns.glColor3dv (black);
955 
956 #else
957 
958     octave_unused_parameter (linewidth);
959     octave_unused_parameter (gridstyle);
960     octave_unused_parameter (gridcolor);
961     octave_unused_parameter (gridalpha);
962     octave_unused_parameter (ticks);
963     octave_unused_parameter (lim1);
964     octave_unused_parameter (lim2);
965     octave_unused_parameter (p1);
966     octave_unused_parameter (p1N);
967     octave_unused_parameter (p2);
968     octave_unused_parameter (p2N);
969     octave_unused_parameter (xyz);
970     octave_unused_parameter (is_3D);
971 
972     // This shouldn't happen because construction of opengl_renderer
973     // objects is supposed to be impossible if OpenGL is not available.
974 
975     panic_impossible ();
976 
977 #endif
978   }
979 
980   void
render_tickmarks(const Matrix & ticks,double lim1,double lim2,double p1,double p1N,double p2,double p2N,double dx,double dy,double dz,int xyz,bool mirror)981   opengl_renderer::render_tickmarks (const Matrix& ticks,
982                                      double lim1, double lim2,
983                                      double p1, double p1N,
984                                      double p2, double p2N,
985                                      double dx, double dy, double dz,
986                                      int xyz, bool mirror)
987   {
988 #if defined (HAVE_OPENGL)
989 
990     m_glfcns.glBegin (GL_LINES);
991 
992     for (int i = 0; i < ticks.numel (); i++)
993       {
994         double val = ticks(i);
995 
996         if (lim1 <= val && val <= lim2)
997           {
998             if (xyz == X_AXIS)
999               {
1000                 m_glfcns.glVertex3d (val, p1, p2);
1001                 m_glfcns.glVertex3d (val, p1+dy, p2+dz);
1002                 if (mirror)
1003                   {
1004                     m_glfcns.glVertex3d (val, p1N, p2N);
1005                     m_glfcns.glVertex3d (val, p1N-dy, p2N-dz);
1006                   }
1007               }
1008             else if (xyz == Y_AXIS)
1009               {
1010                 m_glfcns.glVertex3d (p1, val, p2);
1011                 m_glfcns.glVertex3d (p1+dx, val, p2+dz);
1012                 if (mirror)
1013                   {
1014                     m_glfcns.glVertex3d (p1N, val, p2N);
1015                     m_glfcns.glVertex3d (p1N-dx, val, p2N-dz);
1016                   }
1017               }
1018             else if (xyz == Z_AXIS)
1019               {
1020                 m_glfcns.glVertex3d (p1, p2, val);
1021                 m_glfcns.glVertex3d (p1+dx, p2+dy, val);
1022                 if (mirror)
1023                   {
1024                     m_glfcns.glVertex3d (p1N, p2N, val);
1025                     m_glfcns.glVertex3d (p1N-dx, p2N-dy, val);
1026                   }
1027               }
1028           }
1029       }
1030 
1031     m_glfcns.glEnd ();
1032 
1033 #else
1034 
1035     octave_unused_parameter (ticks);
1036     octave_unused_parameter (lim1);
1037     octave_unused_parameter (lim2);
1038     octave_unused_parameter (p1);
1039     octave_unused_parameter (p1N);
1040     octave_unused_parameter (p2);
1041     octave_unused_parameter (p2N);
1042     octave_unused_parameter (dx);
1043     octave_unused_parameter (dy);
1044     octave_unused_parameter (dz);
1045     octave_unused_parameter (xyz);
1046     octave_unused_parameter (mirror);
1047 
1048     // This shouldn't happen because construction of opengl_renderer
1049     // objects is supposed to be impossible if OpenGL is not available.
1050 
1051     panic_impossible ();
1052 
1053 #endif
1054   }
1055 
1056   void
render_ticktexts(const Matrix & ticks,const string_vector & ticklabels,double lim1,double lim2,double p1,double p2,int xyz,int ha,int va,int & wmax,int & hmax)1057   opengl_renderer::render_ticktexts (const Matrix& ticks,
1058                                      const string_vector& ticklabels,
1059                                      double lim1, double lim2,
1060                                      double p1, double p2,
1061                                      int xyz, int ha, int va,
1062                                      int& wmax, int& hmax)
1063   {
1064 #if defined (HAVE_OPENGL)
1065 
1066     int nticks  = ticks.numel ();
1067     int nlabels = ticklabels.numel ();
1068 
1069     if (nlabels == 0)
1070       return;
1071 
1072     for (int i = 0; i < nticks; i++)
1073       {
1074         double val = ticks(i);
1075 
1076         if (lim1 <= val && val <= lim2)
1077           {
1078             Matrix b;
1079 
1080             std::string label (ticklabels(i % nlabels));
1081             label.erase (0, label.find_first_not_of (' '));
1082             label = label.substr (0, label.find_last_not_of (' ')+1);
1083 
1084             // FIXME: As tick text is transparent, shouldn't it be
1085             //        drawn after axes object, for correct rendering?
1086             if (xyz == X_AXIS)
1087               {
1088                 b = render_text (label, val, p1, p2, ha, va);
1089               }
1090             else if (xyz == Y_AXIS)
1091               {
1092                 b = render_text (label, p1, val, p2, ha, va);
1093               }
1094             else if (xyz == Z_AXIS)
1095               {
1096                 b = render_text (label, p1, p2, val, ha, va);
1097               }
1098 
1099             wmax = std::max (wmax, static_cast<int> (b(2)));
1100             hmax = std::max (hmax, static_cast<int> (b(3)));
1101           }
1102       }
1103 
1104 #else
1105 
1106     octave_unused_parameter (ticks);
1107     octave_unused_parameter (ticklabels);
1108     octave_unused_parameter (lim1);
1109     octave_unused_parameter (lim2);
1110     octave_unused_parameter (p1);
1111     octave_unused_parameter (p2);
1112     octave_unused_parameter (xyz);
1113     octave_unused_parameter (ha);
1114     octave_unused_parameter (va);
1115     octave_unused_parameter (wmax);
1116     octave_unused_parameter (hmax);
1117 
1118     // This shouldn't happen because construction of opengl_renderer
1119     // objects is supposed to be impossible if OpenGL is not available.
1120 
1121     panic_impossible ();
1122 
1123 #endif
1124   }
1125 
1126   void
draw_zoom_rect(int x1,int y1,int x2,int y2)1127   opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2)
1128   {
1129 #if defined (HAVE_OPENGL)
1130 
1131     m_glfcns.glVertex2d (x1, y1);
1132     m_glfcns.glVertex2d (x2, y1);
1133     m_glfcns.glVertex2d (x2, y2);
1134     m_glfcns.glVertex2d (x1, y2);
1135     m_glfcns.glVertex2d (x1, y1);
1136 
1137 #else
1138 
1139     octave_unused_parameter (x1);
1140     octave_unused_parameter (x2);
1141     octave_unused_parameter (y1);
1142     octave_unused_parameter (y2);
1143 
1144     // This shouldn't happen because construction of opengl_renderer
1145     // objects is supposed to be impossible if OpenGL is not available.
1146 
1147     panic_impossible ();
1148 
1149 #endif
1150   }
1151 
1152   void
draw_zoom_box(int width,int height,int x1,int y1,int x2,int y2,const Matrix & overlaycolor,double overlayalpha,const Matrix & bordercolor,double borderalpha,double borderwidth)1153   opengl_renderer::draw_zoom_box (int width, int height,
1154                                   int x1, int y1, int x2, int y2,
1155                                   const Matrix& overlaycolor,
1156                                   double overlayalpha,
1157                                   const Matrix& bordercolor,
1158                                   double borderalpha, double borderwidth)
1159   {
1160 #if defined (HAVE_OPENGL)
1161 
1162     m_glfcns.glMatrixMode (GL_MODELVIEW);
1163     m_glfcns.glPushMatrix ();
1164     m_glfcns.glLoadIdentity ();
1165 
1166     m_glfcns.glMatrixMode (GL_PROJECTION);
1167     m_glfcns.glPushMatrix ();
1168     m_glfcns.glLoadIdentity ();
1169     m_glfcns.glOrtho (0, width, height, 0, 1, -1);
1170 
1171     m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
1172     m_glfcns.glDisable (GL_DEPTH_TEST);
1173 
1174     m_glfcns.glBegin (GL_POLYGON);
1175     m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2),
1176                         overlayalpha);
1177     draw_zoom_rect (x1, y1, x2, y2);
1178     m_glfcns.glEnd ();
1179 
1180     m_glfcns.glLineWidth (borderwidth);
1181     m_glfcns.glBegin (GL_LINE_STRIP);
1182     m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2),
1183                         borderalpha);
1184     draw_zoom_rect (x1, y1, x2, y2);
1185     m_glfcns.glEnd ();
1186 
1187     m_glfcns.glPopAttrib ();
1188 
1189     m_glfcns.glMatrixMode (GL_MODELVIEW);
1190     m_glfcns.glPopMatrix ();
1191 
1192     m_glfcns.glMatrixMode (GL_PROJECTION);
1193     m_glfcns.glPopMatrix ();
1194 
1195 #else
1196 
1197     octave_unused_parameter (width);
1198     octave_unused_parameter (height);
1199     octave_unused_parameter (x1);
1200     octave_unused_parameter (x2);
1201     octave_unused_parameter (y1);
1202     octave_unused_parameter (y2);
1203     octave_unused_parameter (overlaycolor);
1204     octave_unused_parameter (overlayalpha);
1205     octave_unused_parameter (bordercolor);
1206     octave_unused_parameter (borderalpha);
1207     octave_unused_parameter (borderwidth);
1208 
1209     // This shouldn't happen because construction of opengl_renderer
1210     // objects is supposed to be impossible if OpenGL is not available.
1211 
1212     panic_impossible ();
1213 
1214 #endif
1215   }
1216 
1217   uint8NDArray
get_pixels(int width,int height)1218   opengl_renderer::get_pixels (int width, int height)
1219   {
1220 #if defined (HAVE_OPENGL)
1221 
1222     m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1);
1223     uint8NDArray pix(dim_vector (3, width, height), 0);
1224 
1225     m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
1226                           pix.fortran_vec ());
1227 
1228     // Permute and flip data
1229     Array<octave_idx_type> perm (dim_vector (3, 1));
1230     perm(0) = 2;
1231     perm(1) = 1;
1232     perm(2) = 0;
1233 
1234     Array<idx_vector> idx (dim_vector (3, 1));
1235     idx(0) = idx_vector::make_range (height - 1, -1, height);
1236     idx(1) = idx_vector::colon;
1237     idx(2) = idx_vector::colon;
1238 
1239     return pix.permute (perm).index (idx);
1240 
1241 #else
1242 
1243     // This shouldn't happen because construction of opengl_renderer
1244     // objects is supposed to be impossible if OpenGL is not available.
1245 
1246     octave_unused_parameter (width);
1247     octave_unused_parameter (height);
1248 
1249     panic_impossible ();
1250 
1251 #endif
1252   }
1253 
1254   void
finish(void)1255   opengl_renderer::finish (void)
1256   {
1257 #if defined (HAVE_OPENGL)
1258 
1259     m_glfcns.glFinish ();
1260 
1261 #else
1262 
1263     // This shouldn't happen because construction of opengl_renderer
1264     // objects is supposed to be impossible if OpenGL is not available.
1265 
1266     panic_impossible ();
1267 
1268 #endif
1269   }
1270 
1271   void
setup_opengl_transformation(const axes::properties & props)1272   opengl_renderer::setup_opengl_transformation (const axes::properties& props)
1273   {
1274 #if defined (HAVE_OPENGL)
1275 
1276     // setup OpenGL transformation
1277 
1278     Matrix x_zlim = props.get_transform_zlim ();
1279 
1280     xZ1 = std::max (-1e6, x_zlim(0)-(x_zlim(1)-x_zlim(0))*100.0);
1281     xZ2 = std::min (1e6, x_zlim(1)+(x_zlim(1)-x_zlim(0))*100.0);
1282 
1283     Matrix x_mat1 = props.get_opengl_matrix_1 ();
1284     Matrix x_mat2 = props.get_opengl_matrix_2 ();
1285 
1286     m_glfcns.glMatrixMode (GL_MODELVIEW);
1287     m_glfcns.glLoadIdentity ();
1288     m_glfcns.glScaled (1, 1, -1);
1289     m_glfcns.glMultMatrixd (x_mat1.data ());
1290     m_glfcns.glMatrixMode (GL_PROJECTION);
1291     m_glfcns.glLoadIdentity ();
1292 
1293     Matrix vp = get_viewport_scaled ();
1294     m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
1295     m_glfcns.glMultMatrixd (x_mat2.data ());
1296     m_glfcns.glMatrixMode (GL_MODELVIEW);
1297 
1298     m_glfcns.glClear (GL_DEPTH_BUFFER_BIT);
1299 
1300     // store axes transformation data
1301 
1302     xform = props.get_transform ();
1303 
1304 #else
1305 
1306     octave_unused_parameter (props);
1307 
1308     // This shouldn't happen because construction of opengl_renderer
1309     // objects is supposed to be impossible if OpenGL is not available.
1310 
1311     panic_impossible ();
1312 
1313 #endif
1314   }
1315 
1316   void
draw_axes_planes(const axes::properties & props)1317   opengl_renderer::draw_axes_planes (const axes::properties& props)
1318   {
1319 #if defined (HAVE_OPENGL)
1320 
1321     Matrix axe_color = props.get_color_rgb ();
1322     if (axe_color.isempty () || ! props.is_visible ())
1323       return;
1324 
1325     double xPlane = props.get_xPlane ();
1326     double yPlane = props.get_yPlane ();
1327     double zPlane = props.get_zPlane ();
1328     double xPlaneN = props.get_xPlaneN ();
1329     double yPlaneN = props.get_yPlaneN ();
1330     double zPlaneN = props.get_zPlaneN ();
1331     bool is2D = props.get_is2D ();
1332 
1333     // Axes planes
1334     set_color (axe_color);
1335     set_polygon_offset (true, 9.0);
1336 
1337     m_glfcns.glBegin (GL_QUADS);
1338 
1339     if (! is2D)
1340       {
1341         // X plane
1342         m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
1343         m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
1344         m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
1345         m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
1346 
1347         // Y plane
1348         m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
1349         m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
1350         m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
1351         m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
1352       }
1353 
1354     // Z plane
1355     m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
1356     m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
1357     m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
1358     m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
1359 
1360     m_glfcns.glEnd ();
1361 
1362     set_polygon_offset (false);
1363 
1364 #else
1365 
1366     octave_unused_parameter (props);
1367 
1368     // This shouldn't happen because construction of opengl_renderer
1369     // objects is supposed to be impossible if OpenGL is not available.
1370 
1371     panic_impossible ();
1372 
1373 #endif
1374   }
1375 
1376   void
draw_axes_boxes(const axes::properties & props)1377   opengl_renderer::draw_axes_boxes (const axes::properties& props)
1378   {
1379 #if defined (HAVE_OPENGL)
1380 
1381     if (! props.is_visible ())
1382       return;
1383 
1384     bool xySym = props.get_xySym ();
1385     bool layer2Dtop = props.get_layer2Dtop ();
1386     bool is2D = props.get_is2D ();
1387     bool isXOrigin = props.xaxislocation_is ("origin")
1388                      && ! props.yscale_is ("log");
1389     bool isYOrigin = props.yaxislocation_is ("origin")
1390                      && ! props.xscale_is ("log");
1391     bool boxFull = (props.get_boxstyle () == "full");
1392     double linewidth = props.get_linewidth ();
1393     double xPlane = props.get_xPlane ();
1394     double yPlane = props.get_yPlane ();
1395     double zPlane = props.get_zPlane ();
1396     double xPlaneN = props.get_xPlaneN ();
1397     double yPlaneN = props.get_yPlaneN ();
1398     double zPlaneN = props.get_zPlaneN ();
1399     double xpTick = props.get_xpTick ();
1400     double ypTick = props.get_ypTick ();
1401     double zpTick = props.get_zpTick ();
1402     double xpTickN = props.get_xpTickN ();
1403     double ypTickN = props.get_ypTickN ();
1404     double zpTickN = props.get_zpTickN ();
1405 
1406     bool plotyy = (props.has_property ("__plotyy_axes__"));
1407 
1408     // Axes box
1409 
1410     set_linecap ("square");
1411     set_linestyle ("-", true, linewidth);
1412 
1413     m_glfcns.glBegin (GL_LINES);
1414 
1415     if (layer2Dtop)
1416       std::swap (zpTick, zpTickN);
1417 
1418     // X box
1419     Matrix color = props.get_xcolor_rgb ();
1420 
1421     if (! color.isempty ())
1422       {
1423         set_color (color);
1424 
1425         if (! isXOrigin || props.is_box() || ! is2D)
1426           {
1427             m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick);
1428             m_glfcns.glVertex3d (xPlane, ypTick, zpTick);
1429           }
1430 
1431         if (props.is_box ())
1432           {
1433             m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick);
1434             m_glfcns.glVertex3d (xPlane, ypTickN, zpTick);
1435             if (! is2D)
1436               {
1437                 m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN);
1438                 m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN);
1439                 if (boxFull)
1440                   {
1441                     m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN);
1442                     m_glfcns.glVertex3d (xPlane, ypTick, zpTickN);
1443                   }
1444               }
1445           }
1446       }
1447 
1448     // Y box
1449     color = props.get_ycolor_rgb ();
1450 
1451     if (! color.isempty ())
1452       {
1453         set_color (color);
1454         if (! isYOrigin || props.is_box() || ! is2D)
1455           {
1456             m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick);
1457             m_glfcns.glVertex3d (xpTick, yPlane, zpTick);
1458           }
1459 
1460         if (props.is_box () && ! plotyy)
1461           {
1462             m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick);
1463             m_glfcns.glVertex3d (xpTickN, yPlane, zpTick);
1464 
1465             if (! is2D)
1466               {
1467                 m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN);
1468                 m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN);
1469                 if (boxFull)
1470                   {
1471                     m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN);
1472                     m_glfcns.glVertex3d (xpTick, yPlane, zpTickN);
1473                   }
1474               }
1475           }
1476       }
1477 
1478     // Z box
1479     color = props.get_zcolor_rgb ();
1480 
1481     if (! color.isempty () && ! is2D)
1482       {
1483         set_color (color);
1484 
1485         if (xySym)
1486           {
1487             m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
1488             m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
1489           }
1490         else
1491           {
1492             m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
1493             m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
1494           }
1495 
1496         if (props.is_box ())
1497           {
1498             m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
1499             m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
1500 
1501             if (xySym)
1502               {
1503                 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
1504                 m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
1505               }
1506             else
1507               {
1508                 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
1509                 m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
1510               }
1511 
1512             if (boxFull)
1513               {
1514                 m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN);
1515                 m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
1516               }
1517           }
1518       }
1519 
1520     m_glfcns.glEnd ();
1521 
1522     set_linestyle ("-");  // Disable LineStipple
1523 
1524 #else
1525 
1526     octave_unused_parameter (props);
1527 
1528     // This shouldn't happen because construction of opengl_renderer
1529     // objects is supposed to be impossible if OpenGL is not available.
1530 
1531     panic_impossible ();
1532 
1533 #endif
1534   }
1535 
1536   void
draw_axes_x_grid(const axes::properties & props)1537   opengl_renderer::draw_axes_x_grid (const axes::properties& props)
1538   {
1539 #if defined (HAVE_OPENGL)
1540 
1541     gh_manager& gh_mgr
1542       = __get_gh_manager__ ("opengl_renderer::draw_axes_x_grid");
1543 
1544     int xstate = props.get_xstate ();
1545 
1546     if (xstate != AXE_DEPTH_DIR
1547         && (props.is_visible ()
1548             || (selecting && props.pickableparts_is ("all"))))
1549       {
1550         int zstate = props.get_zstate ();
1551         bool x2Dtop = props.get_x2Dtop ();
1552         bool layer2Dtop = props.get_layer2Dtop ();
1553         bool xyzSym = props.get_xyzSym ();
1554         bool nearhoriz = props.get_nearhoriz ();
1555         double xticklen = props.get_xticklen ();
1556         double xtickoffset = props.get_xtickoffset ();
1557         double fy = props.get_fy ();
1558         double fz = props.get_fz ();
1559         double x_min = props.get_x_min ();
1560         double x_max = props.get_x_max ();
1561         double y_min = props.get_y_min ();
1562         double y_max = props.get_y_max ();
1563         double yPlane = props.get_yPlane ();
1564         double yPlaneN = props.get_yPlaneN ();
1565         double ypTick = props.get_ypTick ();
1566         double ypTickN = props.get_ypTickN ();
1567         double zPlane = props.get_zPlane ();
1568         double zPlaneN = props.get_zPlaneN ();
1569         double zpTick = props.get_zpTick ();
1570         double zpTickN = props.get_zpTickN ();
1571 
1572         // X ticks and grid properties
1573         Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ());
1574         Matrix xmticks = xform.xscale (props.get_xminortickvalues ().matrix_value ());
1575         bool do_xminortick = props.is_xminortick () && ! xticks.isempty ();
1576         string_vector xticklabels = props.get_xticklabel ().string_vector_value ();
1577         int wmax = 0;
1578         int hmax = 0;
1579         bool tick_along_z = nearhoriz || math::isinf (fy);
1580         double linewidth = props.get_linewidth ();
1581         std::string gridstyle = props.get_gridlinestyle ();
1582         std::string minorgridstyle = props.get_minorgridlinestyle ();
1583         Matrix gridcolor = props.get_gridcolor_rgb ();
1584         Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
1585         double gridalpha = props.get_gridalpha ();
1586         double minorgridalpha = props.get_minorgridalpha ();
1587         bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
1588         bool do_xminorgrid = (props.is_xminorgrid ()
1589                               && (minorgridstyle != "none")
1590                               && ! xticks.isempty ());
1591         bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D ()
1592                          && ! props.yscale_is ("log");
1593         bool is_origin_low = is_origin && (y_min + y_max) < 0;
1594         bool mirror = props.is_box () && xstate != AXE_ANY_DIR;
1595 
1596         // X grid
1597 
1598         // possibly use axis color for gridcolor & minorgridcolor
1599         if (props.gridcolormode_is ("auto"))
1600           if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none"))
1601             gridcolor = props.get_xcolor_rgb ();
1602 
1603         if (props.minorgridcolormode_is ("auto"))
1604           if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none"))
1605             minorgridcolor = props.get_xcolor_rgb ();
1606 
1607         if (gridcolor.isempty ())
1608           do_xgrid = false;
1609 
1610         if (minorgridcolor.isempty ())
1611           do_xminorgrid = false;
1612 
1613         // set styles when drawing only minor grid
1614         if (do_xminorgrid && ! do_xgrid)
1615           {
1616             gridstyle = minorgridstyle;
1617             gridcolor = minorgridcolor;
1618             gridalpha = minorgridalpha;
1619             do_xgrid = true;
1620           }
1621 
1622         // minor grid lines
1623         if (do_xminorgrid)
1624           render_grid (linewidth,
1625                        minorgridstyle, minorgridcolor, minorgridalpha,
1626                        xmticks, x_min, x_max,
1627                        yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
1628                        0, (zstate != AXE_DEPTH_DIR));
1629 
1630         // grid lines
1631         if (do_xgrid)
1632           render_grid (linewidth,
1633                        gridstyle, gridcolor, gridalpha,
1634                        xticks, x_min, x_max,
1635                        yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
1636                        0, (zstate != AXE_DEPTH_DIR));
1637 
1638         // Skip drawing axis, ticks, and ticklabels when color is "none"
1639         if (props.xcolor_is ("none"))
1640           return;
1641 
1642         set_color (props.get_xcolor_rgb ());
1643 
1644         // axis line
1645         double y_axis_pos = 0.;
1646         if (is_origin)
1647           {
1648             y_axis_pos = math::max (math::min (0., y_max), y_min);
1649             m_glfcns.glBegin (GL_LINES);
1650             set_color (props.get_xcolor_rgb ());
1651             m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick);
1652             m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick);
1653             m_glfcns.glEnd ();
1654           }
1655 
1656         // minor tick marks
1657         if (do_xminortick)
1658           {
1659             if (tick_along_z)
1660               render_tickmarks (xmticks, x_min, x_max,
1661                                 is_origin ? y_axis_pos : ypTick, ypTick,
1662                                 zpTick, zpTickN, 0., 0.,
1663                                 (is_origin_low ? -1. : 1.) *
1664                                 math::signum (zpTick-zpTickN)*fz*xticklen/2,
1665                                 0, ! is_origin && mirror);
1666             else
1667               render_tickmarks (xmticks, x_min, x_max,
1668                                 is_origin ? y_axis_pos : ypTick, ypTickN,
1669                                 zpTick, zpTick, 0.,
1670                                 (is_origin_low ? -1. : 1.) *
1671                                 math::signum (ypTick-ypTickN)*fy*xticklen/2,
1672                                 0., 0, ! is_origin && mirror);
1673           }
1674 
1675         // tick marks
1676         if (tick_along_z)
1677           render_tickmarks (xticks, x_min, x_max,
1678                             is_origin ? y_axis_pos : ypTick, ypTick,
1679                             zpTick, zpTickN, 0., 0.,
1680                             (is_origin_low ? -1. : 1.) *
1681                             math::signum (zpTick-zpTickN)*fz*xticklen,
1682                             0, ! is_origin && mirror);
1683         else
1684           render_tickmarks (xticks, x_min, x_max,
1685                             is_origin ? y_axis_pos : ypTick, ypTickN,
1686                             zpTick, zpTick, 0.,
1687                             (is_origin_low ? -1. : 1.) *
1688                             math::signum (ypTick-ypTickN)*fy*xticklen,
1689                             0., 0, ! is_origin && mirror);
1690 
1691         // tick texts
1692         if (xticklabels.numel () > 0)
1693           {
1694             int halign = (xstate == AXE_HORZ_DIR
1695                           ? 1
1696                           : (xyzSym || is_origin_low ? 0 : 2));
1697             int valign = (xstate == AXE_VERT_DIR
1698                           ? 1
1699                           : (x2Dtop || is_origin_low ? 0 : 2));
1700 
1701             if (tick_along_z)
1702               render_ticktexts (xticks, xticklabels, x_min, x_max,
1703                                 is_origin ? y_axis_pos : ypTick,
1704                                 zpTick +
1705                                 (is_origin_low ? -1. : 1.) *
1706                                 math::signum (zpTick-zpTickN)*fz*xtickoffset,
1707                                 0, halign, valign, wmax, hmax);
1708             else
1709               render_ticktexts (xticks, xticklabels, x_min, x_max,
1710                                 (is_origin ? y_axis_pos : ypTick) +
1711                                 (is_origin_low ?  -1. : 1.) *
1712                                 math::signum (ypTick-ypTickN)*fy*xtickoffset,
1713                                 zpTick, 0, halign, valign, wmax, hmax);
1714           }
1715 
1716         gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on");
1717       }
1718     else
1719       gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off");
1720 
1721 #else
1722 
1723     octave_unused_parameter (props);
1724 
1725     // This shouldn't happen because construction of opengl_renderer
1726     // objects is supposed to be impossible if OpenGL is not available.
1727 
1728     panic_impossible ();
1729 
1730 #endif
1731   }
1732 
1733   void
draw_axes_y_grid(const axes::properties & props)1734   opengl_renderer::draw_axes_y_grid (const axes::properties& props)
1735   {
1736 #if defined (HAVE_OPENGL)
1737 
1738     gh_manager& gh_mgr
1739       = __get_gh_manager__ ("opengl_renderer::draw_axes_y_grid");
1740 
1741     int ystate = props.get_ystate ();
1742 
1743     if (ystate != AXE_DEPTH_DIR && props.is_visible ()
1744         && (props.is_visible ()
1745             || (selecting && props.pickableparts_is ("all"))))
1746       {
1747         int zstate = props.get_zstate ();
1748         bool y2Dright = props.get_y2Dright ();
1749         bool layer2Dtop = props.get_layer2Dtop ();
1750         bool xyzSym = props.get_xyzSym ();
1751         bool nearhoriz = props.get_nearhoriz ();
1752         double yticklen = props.get_yticklen ();
1753         double ytickoffset = props.get_ytickoffset ();
1754         double fx = props.get_fx ();
1755         double fz = props.get_fz ();
1756         double xPlane = props.get_xPlane ();
1757         double xPlaneN = props.get_xPlaneN ();
1758         double xpTick = props.get_xpTick ();
1759         double xpTickN = props.get_xpTickN ();
1760         double y_min = props.get_y_min ();
1761         double y_max = props.get_y_max ();
1762         double x_min = props.get_x_min ();
1763         double x_max = props.get_x_max ();
1764         double zPlane = props.get_zPlane ();
1765         double zPlaneN = props.get_zPlaneN ();
1766         double zpTick = props.get_zpTick ();
1767         double zpTickN = props.get_zpTickN ();
1768 
1769         // Y ticks and grid properties
1770         Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ());
1771         Matrix ymticks = xform.yscale (props.get_yminortickvalues ().matrix_value ());
1772         bool do_yminortick = props.is_yminortick () && ! yticks.isempty ();
1773         string_vector yticklabels = props.get_yticklabel ().string_vector_value ();
1774         int wmax = 0;
1775         int hmax = 0;
1776         bool tick_along_z = nearhoriz || math::isinf (fx);
1777         double linewidth = props.get_linewidth ();
1778         std::string gridstyle = props.get_gridlinestyle ();
1779         std::string minorgridstyle = props.get_minorgridlinestyle ();
1780         Matrix gridcolor = props.get_gridcolor_rgb ();
1781         Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
1782         double gridalpha = props.get_gridalpha ();
1783         double minorgridalpha = props.get_minorgridalpha ();
1784         bool do_ygrid = (props.is_ygrid () && (gridstyle != "none"));
1785         bool do_yminorgrid = (props.is_yminorgrid ()
1786                               && (minorgridstyle != "none")
1787                               && ! yticks.isempty ());
1788         bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D ()
1789                          && ! props.xscale_is ("log");
1790         bool is_origin_low = is_origin && (x_min + x_max) < 0;
1791         bool mirror = props.is_box () && ystate != AXE_ANY_DIR
1792                       && (! props.has_property ("__plotyy_axes__"));
1793 
1794         // Y grid
1795 
1796         // possibly use axis color for gridcolor & minorgridcolor
1797         if (props.gridcolormode_is ("auto"))
1798           if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none"))
1799             gridcolor = props.get_ycolor_rgb ();
1800 
1801         if (props.minorgridcolormode_is ("auto"))
1802           if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none"))
1803             minorgridcolor = props.get_ycolor_rgb ();
1804 
1805         if (gridcolor.isempty ())
1806           do_ygrid = false;
1807 
1808         if (minorgridcolor.isempty ())
1809           do_yminorgrid = false;
1810 
1811         // set styles when drawing only minor grid
1812         if (do_yminorgrid && ! do_ygrid)
1813           {
1814             gridstyle = minorgridstyle;
1815             gridcolor = minorgridcolor;
1816             gridalpha = minorgridalpha;
1817             do_ygrid = true;
1818           }
1819 
1820         // minor grid lines
1821         if (do_yminorgrid)
1822           render_grid (linewidth,
1823                        minorgridstyle, minorgridcolor, minorgridalpha,
1824                        ymticks, y_min, y_max,
1825                        xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
1826                        1, (zstate != AXE_DEPTH_DIR));
1827 
1828         // grid lines
1829         if (do_ygrid)
1830           render_grid (linewidth,
1831                        gridstyle, gridcolor, gridalpha,
1832                        yticks, y_min, y_max,
1833                        xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
1834                        1, (zstate != AXE_DEPTH_DIR));
1835 
1836         // Skip drawing axis, ticks, and ticklabels when color is "none"
1837         if (props.ycolor_is ("none"))
1838           return;
1839 
1840         set_color (props.get_ycolor_rgb ());
1841 
1842         // axis line
1843         double x_axis_pos = 0.;
1844         if (is_origin)
1845           {
1846             x_axis_pos = math::max (math::min (0., x_max), x_min);
1847             m_glfcns.glBegin (GL_LINES);
1848             set_color (props.get_ycolor_rgb ());
1849             m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick);
1850             m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick);
1851             m_glfcns.glEnd ();
1852           }
1853 
1854         // minor tick marks
1855         if (do_yminortick)
1856           {
1857             if (tick_along_z)
1858               render_tickmarks (ymticks, y_min, y_max,
1859                                 is_origin ? x_axis_pos : xpTick, xpTick,
1860                                 zpTick, zpTickN, 0., 0.,
1861                                 (is_origin_low ? -1. : 1.) *
1862                                 math::signum (zpTick-zpTickN)*fz*yticklen/2,
1863                                 1, ! is_origin && mirror);
1864             else
1865               render_tickmarks (ymticks, y_min, y_max,
1866                                 is_origin ? x_axis_pos : xpTick, xpTickN,
1867                                 zpTick, zpTick,
1868                                 (is_origin_low ? -1. : 1.) *
1869                                 math::signum (xpTick-xpTickN)*fx*yticklen/2,
1870                                 0., 0., 1, ! is_origin && mirror);
1871           }
1872 
1873         // tick marks
1874         if (tick_along_z)
1875           render_tickmarks (yticks, y_min, y_max,
1876                             is_origin ? x_axis_pos : xpTick, xpTick,
1877                             zpTick, zpTickN, 0., 0.,
1878                             (is_origin_low ? -1. : 1.) *
1879                             math::signum (zpTick-zpTickN)*fz*yticklen,
1880                             1, ! is_origin && mirror);
1881         else
1882           render_tickmarks (yticks, y_min, y_max,
1883                             is_origin ? x_axis_pos : xpTick, xpTickN,
1884                             zpTick, zpTick,
1885                             (is_origin_low ? -1. : 1.) *
1886                             math::signum (xPlaneN-xPlane)*fx*yticklen,
1887                             0., 0., 1, ! is_origin && mirror);
1888 
1889         // tick texts
1890         if (yticklabels.numel () > 0)
1891           {
1892             int halign = (ystate == AXE_HORZ_DIR
1893                           ? 1
1894                           : (! xyzSym || y2Dright || is_origin_low ? 0 : 2));
1895             int valign = (ystate == AXE_VERT_DIR
1896                           ? 1
1897                           : (is_origin_low ? 0 : 2));
1898 
1899             if (tick_along_z)
1900               render_ticktexts (yticks, yticklabels, y_min, y_max,
1901                                 is_origin ? x_axis_pos : xpTick,
1902                                 zpTick +
1903                                 (is_origin_low ? -1. : 1.) *
1904                                 math::signum (zpTick-zpTickN)*fz*ytickoffset,
1905                                 1, halign, valign, wmax, hmax);
1906             else
1907               render_ticktexts (yticks, yticklabels, y_min, y_max,
1908                                 (is_origin ? x_axis_pos : xpTick) +
1909                                 (is_origin_low ?  -1. : 1.) *
1910                                 math::signum (xpTick-xpTickN)*fx*ytickoffset,
1911                                 zpTick, 1, halign, valign, wmax, hmax);
1912           }
1913 
1914         gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on");
1915       }
1916     else
1917       gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off");
1918 
1919 #else
1920 
1921     octave_unused_parameter (props);
1922 
1923     // This shouldn't happen because construction of opengl_renderer
1924     // objects is supposed to be impossible if OpenGL is not available.
1925 
1926     panic_impossible ();
1927 
1928 #endif
1929   }
1930 
1931   void
draw_axes_z_grid(const axes::properties & props)1932   opengl_renderer::draw_axes_z_grid (const axes::properties& props)
1933   {
1934     gh_manager& gh_mgr
1935       = __get_gh_manager__ ("opengl_renderer::draw_axes_z_grid");
1936 
1937     int zstate = props.get_zstate ();
1938 
1939     if (zstate != AXE_DEPTH_DIR && props.is_visible ()
1940         && (props.is_visible ()
1941             || (selecting && props.pickableparts_is ("all"))))
1942       {
1943         bool xySym = props.get_xySym ();
1944         bool zSign = props.get_zSign ();
1945         double zticklen = props.get_zticklen ();
1946         double ztickoffset = props.get_ztickoffset ();
1947         double fx = props.get_fx ();
1948         double fy = props.get_fy ();
1949         double xPlane = props.get_xPlane ();
1950         double xPlaneN = props.get_xPlaneN ();
1951         double yPlane = props.get_yPlane ();
1952         double yPlaneN = props.get_yPlaneN ();
1953         double z_min = props.get_z_min ();
1954         double z_max = props.get_z_max ();
1955 
1956         // Z ticks and grid properties
1957         Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ());
1958         Matrix zmticks = xform.zscale (props.get_zminortickvalues ().matrix_value ());
1959         bool do_zminortick = props.is_zminortick () && ! zticks.isempty ();
1960         string_vector zticklabels = props.get_zticklabel ().string_vector_value ();
1961         int wmax = 0;
1962         int hmax = 0;
1963         double linewidth = props.get_linewidth ();
1964         std::string gridstyle = props.get_gridlinestyle ();
1965         std::string minorgridstyle = props.get_minorgridlinestyle ();
1966         Matrix gridcolor = props.get_gridcolor_rgb ();
1967         Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
1968         double gridalpha = props.get_gridalpha ();
1969         double minorgridalpha = props.get_minorgridalpha ();
1970         bool do_zgrid = (props.is_zgrid () && (gridstyle != "none"));
1971         bool do_zminorgrid = (props.is_zminorgrid ()
1972                               && (minorgridstyle != "none")
1973                               && ! zticks.isempty ());
1974         bool mirror = props.is_box () && zstate != AXE_ANY_DIR;
1975 
1976         // Z grid
1977 
1978         // possibly use axis color for gridcolor & minorgridcolor
1979         if (props.gridcolormode_is ("auto"))
1980           if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none"))
1981             gridcolor = props.get_zcolor_rgb ();
1982 
1983         if (props.minorgridcolormode_is ("auto"))
1984           if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none"))
1985             minorgridcolor = props.get_zcolor_rgb ();
1986 
1987         if (gridcolor.isempty ())
1988           do_zgrid = false;
1989 
1990         if (minorgridcolor.isempty ())
1991           do_zminorgrid = false;
1992 
1993         // set styles when drawing only minor grid
1994         if (do_zminorgrid && ! do_zgrid)
1995           {
1996             gridstyle = minorgridstyle;
1997             gridcolor = minorgridcolor;
1998             gridalpha = minorgridalpha;
1999             do_zgrid = true;
2000           }
2001 
2002         // minor grid lines
2003         if (do_zminorgrid)
2004           render_grid (linewidth,
2005                        minorgridstyle, minorgridcolor, minorgridalpha,
2006                        zmticks, z_min, z_max,
2007                        xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
2008 
2009         // grid lines
2010         if (do_zgrid)
2011           render_grid (linewidth,
2012                        gridstyle, gridcolor, gridalpha,
2013                        zticks, z_min, z_max,
2014                        xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
2015 
2016         // Skip drawing axis, ticks, and ticklabels when color is "none"
2017         if (props.zcolor_is ("none"))
2018           return;
2019 
2020         set_color (props.get_zcolor_rgb ());
2021 
2022         // minor tick marks
2023         if (do_zminortick)
2024           {
2025             if (xySym)
2026               {
2027                 if (math::isinf (fy))
2028                   render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane,
2029                                     yPlane, yPlane,
2030                                     math::signum (xPlaneN-xPlane)*fx*zticklen/2,
2031                                     0., 0., 2, mirror);
2032                 else
2033                   render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN,
2034                                     yPlane, yPlane, 0.,
2035                                     math::signum (yPlane-yPlaneN)*fy*zticklen/2,
2036                                     0., 2, false);
2037               }
2038             else
2039               {
2040                 if (math::isinf (fx))
2041                   render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
2042                                     yPlaneN, yPlane, 0.,
2043                                     math::signum (yPlaneN-yPlane)*fy*zticklen/2,
2044                                     0., 2, mirror);
2045                 else
2046                   render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
2047                                     yPlaneN, yPlaneN,
2048                                     math::signum (xPlane-xPlaneN)*fx*zticklen/2,
2049                                     0., 0., 2, false);
2050               }
2051           }
2052 
2053         // tick marks
2054         if (xySym)
2055           {
2056             if (math::isinf (fy))
2057               render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
2058                                 yPlane, yPlane,
2059                                 math::signum (xPlaneN-xPlane)*fx*zticklen,
2060                                 0., 0., 2, mirror);
2061             else
2062               render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN,
2063                                 yPlane, yPlane, 0.,
2064                                 math::signum (yPlane-yPlaneN)*fy*zticklen,
2065                                 0., 2, false);
2066           }
2067         else
2068           {
2069             if (math::isinf (fx))
2070               render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
2071                                 yPlaneN, yPlane, 0.,
2072                                 math::signum (yPlaneN-yPlane)*fy*zticklen,
2073                                 0., 2, mirror);
2074             else
2075               render_tickmarks (zticks, z_min, z_max, xPlane, xPlane,
2076                                 yPlaneN, yPlane,
2077                                 math::signum (xPlane-xPlaneN)*fx*zticklen,
2078                                 0., 0., 2, false);
2079           }
2080 
2081         // tick texts
2082         if (zticklabels.numel () > 0)
2083           {
2084             int halign = 2;
2085             int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2));
2086 
2087             if (xySym)
2088               {
2089                 if (math::isinf (fy))
2090                   render_ticktexts (zticks, zticklabels, z_min, z_max,
2091                                     xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset,
2092                                     yPlane, 2, halign, valign, wmax, hmax);
2093                 else
2094                   render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN,
2095                                     yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset,
2096                                     2, halign, valign, wmax, hmax);
2097               }
2098             else
2099               {
2100                 if (math::isinf (fx))
2101                   render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane,
2102                                     yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset,
2103                                     2, halign, valign, wmax, hmax);
2104                 else
2105                   render_ticktexts (zticks, zticklabels, z_min, z_max,
2106                                     xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset,
2107                                     yPlaneN, 2, halign, valign, wmax, hmax);
2108               }
2109           }
2110 
2111         gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on");
2112       }
2113     else
2114       gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off");
2115   }
2116 
2117   void
draw_axes_grids(const axes::properties & props)2118   opengl_renderer::draw_axes_grids (const axes::properties& props)
2119   {
2120 #if defined (HAVE_OPENGL)
2121     // Disable line smoothing for axes
2122     GLboolean antialias;
2123 
2124     m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias);
2125 
2126     if (antialias == GL_TRUE)
2127       m_glfcns.glDisable (GL_LINE_SMOOTH);
2128 
2129     set_linecap ("butt");
2130     set_linewidth (props.get_linewidth ());
2131     set_font (props);
2132     set_interpreter (props.get_ticklabelinterpreter ());
2133 
2134     draw_axes_x_grid (props);
2135     draw_axes_y_grid (props);
2136     draw_axes_z_grid (props);
2137 
2138     if (antialias == GL_TRUE)
2139       m_glfcns.glEnable (GL_LINE_SMOOTH);
2140 #else
2141 
2142     octave_unused_parameter (props);
2143 
2144     // This shouldn't happen because construction of opengl_renderer
2145     // objects is supposed to be impossible if OpenGL is not available.
2146 
2147     panic_impossible ();
2148 
2149 #endif
2150   }
2151 
2152   void
draw_all_lights(const base_properties & props,std::list<graphics_object> & obj_list)2153   opengl_renderer::draw_all_lights (const base_properties& props,
2154                                     std::list<graphics_object>& obj_list)
2155   {
2156 #if defined (HAVE_OPENGL)
2157     gh_manager& gh_mgr
2158       = __get_gh_manager__ ("opengl_renderer::draw_axes_all_lights");
2159 
2160     Matrix children = props.get_all_children ();
2161 
2162     for (octave_idx_type i = children.numel () - 1; i >= 0; i--)
2163       {
2164         graphics_object go = gh_mgr.get_object (children(i));
2165 
2166         base_properties p = go.get_properties ();
2167 
2168         if (p.is_visible ()
2169             || (selecting && p.pickableparts_is ("all")))
2170           {
2171             if (go.isa ("light") && ! selecting)
2172               {
2173                 if (m_current_light-GL_LIGHT0 < m_max_lights)
2174                   {
2175                     set_clipping (p.is_clipping ());
2176                     draw (go);
2177                     m_current_light++;
2178                   }
2179               }
2180             else if (go.isa ("hggroup")
2181                      && ! (selecting && p.pickableparts_is ("none")))
2182               draw_all_lights (go.get_properties (), obj_list);
2183             else if (! (selecting && p.pickableparts_is ("none")))
2184               obj_list.push_back (go);
2185           }
2186       }
2187 #else
2188 
2189     octave_unused_parameter (props);
2190     octave_unused_parameter (obj_list);
2191 
2192     // This shouldn't happen because construction of opengl_renderer
2193     // objects is supposed to be impossible if OpenGL is not available.
2194 
2195     panic_impossible ();
2196 
2197 #endif
2198   }
2199 
2200   void
draw_axes_children(const axes::properties & props)2201   opengl_renderer::draw_axes_children (const axes::properties& props)
2202   {
2203 #if defined (HAVE_OPENGL)
2204     // list for non-light child objects
2205     std::list<graphics_object> obj_list;
2206     std::list<graphics_object>::iterator it;
2207 
2208     // 1st pass: draw light objects
2209 
2210     // FIXME: max_lights only needs to be set once.
2211     // It would be better if this could be in the constructor for gl_renderer
2212     // but this seems to lead to calls of OpenGL functions before the context
2213     // is actually initialized.  See bug #48669.
2214     // Check actual maximum number of lights possible
2215     init_maxlights ();
2216 
2217     // Start with the last element of the array of child objects to
2218     // display them in the order they were added to the array.
2219 
2220     if (props.get_num_lights () > m_max_lights)
2221       warning_with_id ("Octave:max-lights-exceeded",
2222                        "light: Maximum number of lights (%d) in these axes is "
2223                        "exceeded.", m_max_lights);
2224 
2225     m_current_light = GL_LIGHT0;
2226     draw_all_lights (props, obj_list);
2227 
2228     // disable other OpenGL lights
2229     for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++)
2230       m_glfcns.glDisable (GL_LIGHT0 + i);
2231 
2232     // save camera position and set ambient light color before drawing
2233     // other objects
2234     view_vector = props.get_cameraposition ().matrix_value ();
2235 
2236     float cb[4] = { 1.0, 1.0, 1.0, 1.0 };
2237     ColumnVector ambient_color = props.get_ambientlightcolor_rgb ();
2238     for (int i = 0; i < 3; i++)
2239       cb[i] = ambient_color(i);
2240     m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb);
2241 
2242     // 2nd pass: draw other objects (with units set to "data")
2243 
2244     it = obj_list.begin ();
2245     while (it != obj_list.end ())
2246       {
2247         graphics_object go = (*it);
2248 
2249         // FIXME: check whether object has "units" property and it is set
2250         // to "data"
2251         if (! go.isa ("text") || go.get ("units").string_value () == "data")
2252           {
2253             set_clipping (go.get_properties ().is_clipping ());
2254             draw (go);
2255 
2256             it = obj_list.erase (it);
2257           }
2258         else
2259           it++;
2260       }
2261 
2262     // 3rd pass: draw remaining objects
2263 
2264     m_glfcns.glDisable (GL_DEPTH_TEST);
2265 
2266     for (it = obj_list.begin (); it != obj_list.end (); it++)
2267       {
2268         graphics_object go = (*it);
2269 
2270         set_clipping (go.get_properties ().is_clipping ());
2271         draw (go);
2272       }
2273 
2274     set_clipping (false);
2275 
2276     // FIXME: finalize rendering (transparency processing)
2277     // FIXME: draw zoom box, if needed
2278 
2279 #else
2280 
2281     octave_unused_parameter (props);
2282 
2283     // This shouldn't happen because construction of opengl_renderer
2284     // objects is supposed to be impossible if OpenGL is not available.
2285 
2286     panic_impossible ();
2287 
2288 #endif
2289   }
2290 
2291   void
draw_axes(const axes::properties & props)2292   opengl_renderer::draw_axes (const axes::properties& props)
2293   {
2294 #if defined (HAVE_OPENGL)
2295 
2296     // Legends are not drawn when "visible" is "off".
2297     if (! props.is_visible () && props.get_tag () == "legend")
2298       return;
2299 
2300     // Don't draw the axes and its children if we are in selection and
2301     // pickable parts is "none".
2302     if (selecting && props.pickableparts_is ("none"))
2303       return;
2304 
2305     static double floatmax = std::numeric_limits<float>::max ();
2306 
2307     double x_min = props.get_x_min ();
2308     double x_max = props.get_x_max ();
2309     double y_min = props.get_y_min ();
2310     double y_max = props.get_y_max ();
2311     double z_min = props.get_z_min ();
2312     double z_max = props.get_z_max ();
2313 
2314     if (x_max > floatmax || y_max > floatmax || z_max > floatmax
2315         || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax)
2316       {
2317         warning ("opengl_renderer: data values greater than float capacity.  (1) Scale data, or (2) Use gnuplot");
2318         return;
2319       }
2320 
2321     setup_opengl_transformation (props);
2322 
2323     // For 2D axes with only 2D primitives, draw from back to front without
2324     // depth sorting
2325     bool is2D = props.get_is2D (true);
2326     if (is2D)
2327       m_glfcns.glDisable (GL_DEPTH_TEST);
2328     else
2329       m_glfcns.glEnable (GL_DEPTH_TEST);
2330 
2331     draw_axes_planes (props);
2332 
2333     if (! is2D || props.layer_is ("bottom"))
2334       {
2335         draw_axes_grids (props);
2336         if (props.get_tag () != "legend" || props.get_box () != "off")
2337           draw_axes_boxes (props);
2338       }
2339 
2340     set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max);
2341 
2342     draw_axes_children (props);
2343 
2344     if (is2D && props.layer_is ("top"))
2345       {
2346         draw_axes_grids (props);
2347         if (props.get_tag () != "legend" || props.get_box () != "off")
2348           draw_axes_boxes (props);
2349       }
2350 
2351 #else
2352 
2353     octave_unused_parameter (props);
2354 
2355     // This shouldn't happen because construction of opengl_renderer
2356     // objects is supposed to be impossible if OpenGL is not available.
2357 
2358     panic_impossible ();
2359 
2360 #endif
2361   }
2362 
2363   void
draw_line(const line::properties & props)2364   opengl_renderer::draw_line (const line::properties& props)
2365   {
2366 #if defined (HAVE_OPENGL)
2367 
2368     bool draw_all = selecting && props.pickableparts_is ("all");
2369 
2370     Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
2371     Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
2372     Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
2373 
2374     bool has_z = (z.numel () > 0);
2375     int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()),
2376                                         (has_z ? z.numel ()
2377                                          : std::numeric_limits<int>::max ())));
2378     uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
2379     uint8_t clip_ok = 0x40;
2380 
2381     std::vector<uint8_t> clip (n);
2382 
2383     if (has_z)
2384       for (int i = 0; i < n; i++)
2385         clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask);
2386     else
2387       {
2388         double z_mid = (zmin+zmax)/2;
2389 
2390         for (int i = 0; i < n; i++)
2391           clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask);
2392       }
2393 
2394     if (! props.linestyle_is ("none") && ! props.color_is ("none"))
2395       {
2396         set_color (props.get_color_rgb ());
2397         set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
2398         set_linewidth (props.get_linewidth ());
2399         set_linecap ("butt");
2400         set_linejoin (props.get_linejoin ());
2401 
2402         if (has_z)
2403           {
2404             bool flag = false;
2405 
2406             for (int i = 1; i < n; i++)
2407               {
2408                 if ((clip[i-1] & clip[i]) == clip_ok)
2409                   {
2410                     if (! flag)
2411                       {
2412                         flag = true;
2413                         m_glfcns.glBegin (GL_LINE_STRIP);
2414                         m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1));
2415                       }
2416                     m_glfcns.glVertex3d (x(i), y(i), z(i));
2417                   }
2418                 else if (flag)
2419                   {
2420                     flag = false;
2421                     m_glfcns.glEnd ();
2422                   }
2423               }
2424 
2425             if (flag)
2426               m_glfcns.glEnd ();
2427           }
2428         else
2429           {
2430             bool flag = false;
2431 
2432             for (int i = 1; i < n; i++)
2433               {
2434                 if ((clip[i-1] & clip[i]) == clip_ok)
2435                   {
2436                     if (! flag)
2437                       {
2438                         flag = true;
2439                         m_glfcns.glBegin (GL_LINE_STRIP);
2440                         m_glfcns.glVertex2d (x(i-1), y(i-1));
2441                       }
2442                     m_glfcns.glVertex2d (x(i), y(i));
2443                   }
2444                 else if (flag)
2445                   {
2446                     flag = false;
2447                     m_glfcns.glEnd ();
2448                   }
2449               }
2450 
2451             if (flag)
2452               m_glfcns.glEnd ();
2453           }
2454 
2455         set_linewidth (0.5f);
2456         set_linestyle ("-");
2457       }
2458 
2459     set_clipping (false);
2460 
2461     if (! props.marker_is ("none")
2462         && ! (props.markeredgecolor_is ("none")
2463               && props.markerfacecolor_is ("none")))
2464       {
2465         Matrix lc, fc;
2466 
2467         if (draw_all)
2468           lc = Matrix (1, 3, 0.0);
2469         else if (props.markeredgecolor_is ("auto"))
2470           lc = props.get_color_rgb ();
2471         else if (! props.markeredgecolor_is ("none"))
2472           lc = props.get_markeredgecolor_rgb ();
2473 
2474         if (draw_all)
2475           fc = Matrix (1, 3, 0.0);
2476         if (props.markerfacecolor_is ("auto"))
2477           fc = props.get_color_rgb ();
2478         else if (! props.markerfacecolor_is ("none"))
2479           fc = props.get_markerfacecolor_rgb ();
2480 
2481         init_marker (props.get_marker (), props.get_markersize (),
2482                      props.get_linewidth ());
2483 
2484         for (int i = 0; i < n; i++)
2485           {
2486             if (clip[i] == clip_ok)
2487               draw_marker (x(i), y(i),
2488                            has_z ? z(i) : 0.0,
2489                            lc, fc);
2490           }
2491 
2492         end_marker ();
2493       }
2494 
2495     set_clipping (props.is_clipping ());
2496 
2497 #else
2498 
2499     octave_unused_parameter (props);
2500 
2501     // This shouldn't happen because construction of opengl_renderer
2502     // objects is supposed to be impossible if OpenGL is not available.
2503 
2504     panic_impossible ();
2505 
2506 #endif
2507   }
2508 
2509   void
draw_surface(const surface::properties & props)2510   opengl_renderer::draw_surface (const surface::properties& props)
2511   {
2512 #if defined (HAVE_OPENGL)
2513 
2514     bool draw_all = selecting && props.pickableparts_is ("all");
2515 
2516     const Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
2517     const Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
2518     const Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
2519 
2520     int zr = z.rows ();
2521     int zc = z.columns ();
2522 
2523     NDArray c;
2524     const NDArray vn = props.get_vertexnormals ().array_value ();
2525     dim_vector vn_dims = vn.dims ();
2526     bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc
2527                                && vn_dims(2) == 3);
2528     const NDArray fn = props.get_facenormals ().array_value ();
2529     dim_vector fn_dims = fn.dims ();
2530     bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1
2531                              && fn_dims(2) == 3);
2532 
2533     // FIXME: handle transparency
2534     Matrix a;
2535 
2536     int fc_mode = (props.facecolor_is_rgb () ? 0 :
2537                    (props.facecolor_is ("flat") ? 1 :
2538                     (props.facecolor_is ("interp") ? 2 :
2539                      (props.facecolor_is ("texturemap") ? 3 : -1))));
2540     int fl_mode = (props.facelighting_is ("none") ? 0 :
2541                    (props.facelighting_is ("flat") ?
2542                     (has_face_normals ? 1 : 0) :
2543                     (has_vertex_normals ? 2 : 0)));
2544     int fa_mode = (props.facealpha_is_double () ? 0 :
2545                    (props.facealpha_is ("flat") ? 1 : 2));
2546     int ec_mode = (props.edgecolor_is_rgb () ? 0 :
2547                    (props.edgecolor_is ("flat") ? 1 :
2548                     (props.edgecolor_is ("interp") ? 2 : -1)));
2549     int el_mode = (props.edgelighting_is ("none") ? 0 :
2550                    (props.edgelighting_is ("flat") ?
2551                     (has_face_normals ? 1 : 0) :
2552                     (has_vertex_normals ? 2 : 0)));
2553     int ea_mode = (props.edgealpha_is_double () ? 0 :
2554                    (props.edgealpha_is ("flat") ? 1 : 2));
2555     int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
2556                     (props.backfacelighting_is ("reverselit") ? 1 : 2));
2557     bool do_lighting = props.get_do_lighting ();
2558 
2559     Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0)
2560                                         : props.get_facecolor_rgb ());
2561     Matrix ecolor = props.get_edgecolor_rgb ();
2562     double fa = 1.0;
2563 
2564     float as = props.get_ambientstrength ();
2565     float ds = props.get_diffusestrength ();
2566     float ss = props.get_specularstrength ();
2567     float se = props.get_specularexponent () * 5; // to fit Matlab
2568     float scr = props.get_specularcolorreflectance ();
2569     float cb[4] = { 0.0, 0.0, 0.0, 1.0 };
2570 
2571     opengl_texture tex (m_glfcns);
2572 
2573     int i1, i2, j1, j2;
2574     bool x_mat = (x.rows () == z.rows ());
2575     bool y_mat = (y.columns () == z.columns ());
2576 
2577     i1 = i2 = j1 = j2 = 0;
2578 
2579     if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0)
2580       c = props.get_color_data ().array_value ();
2581 
2582     boolMatrix clip (z.dims (), false);
2583 
2584     for (int i = 0; i < zr; i++)
2585       {
2586         if (x_mat)
2587           i1 = i;
2588 
2589         for (int j = 0; j < zc; j++)
2590           {
2591             if (y_mat)
2592               j1 = j;
2593 
2594             clip(i,j) = is_nan_or_inf (x(i1,j), y(i,j1), z(i,j));
2595           }
2596       }
2597 
2598     if (fa_mode > 0 || ea_mode > 0)
2599       {
2600         // FIXME: implement alphadata conversion
2601         //a = props.get_alpha_data ();
2602       }
2603 
2604     if (fl_mode > 0 || el_mode > 0)
2605       m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
2606 
2607     // FIXME: good candidate for caching,
2608     //        transferring pixel data to OpenGL is time consuming.
2609     if (fc_mode == TEXTURE)
2610       tex = opengl_texture::create (m_glfcns, props.get_color_data ());
2611 
2612     if (draw_all || ! props.facecolor_is ("none"))
2613       {
2614         if (fa_mode == 0)
2615           {
2616             fa = props.get_facealpha_double ();
2617             cb[3] = fa;
2618             if (fc_mode == UNIFORM || fc_mode == TEXTURE)
2619               {
2620                 m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
2621                 if (fl_mode > 0)
2622                   {
2623                     for (int i = 0; i < 3; i++)
2624                       cb[i] = as * fcolor(i);
2625                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2626 
2627                     for (int i = 0; i < 3; i++)
2628                       cb[i] = ds * fcolor(i);
2629                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2630 
2631                     for (int i = 0; i < 3; i++)
2632                       cb[i] = ss * (scr + (1-scr) * fcolor(i));
2633                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2634                   }
2635               }
2636 
2637             if ((fl_mode > 0) && do_lighting)
2638               m_glfcns.glEnable (GL_LIGHTING);
2639             m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD)
2640                                    ? GL_SMOOTH : GL_FLAT);
2641             set_polygon_offset (true, 1.0);
2642             if (fc_mode == TEXTURE)
2643               m_glfcns.glEnable (GL_TEXTURE_2D);
2644 
2645             for (int i = 1; i < zc; i++)
2646               {
2647                 if (y_mat)
2648                   {
2649                     i1 = i-1;
2650                     i2 = i;
2651                   }
2652 
2653                 for (int j = 1; j < zr; j++)
2654                   {
2655 
2656                     if (clip(j-1, i-1) || clip(j, i-1)
2657                         || clip(j-1, i) || clip(j, i))
2658                       continue;
2659 
2660                     if (fc_mode == FLAT)
2661                       {
2662                         // "flat" only needs color at lower-left vertex
2663                         if (! math::isfinite (c(j-1,i-1)))
2664                           continue;
2665                       }
2666                     else if (fc_mode == INTERP)
2667                       {
2668                         // "interp" needs valid color at all 4 vertices
2669                         if (! (math::isfinite (c(j-1, i-1)) && math::isfinite (c(j, i-1))
2670                                && math::isfinite (c(j-1, i)) && math::isfinite (c(j, i))))
2671                           continue;
2672                       }
2673 
2674                     if (x_mat)
2675                       {
2676                         j1 = j-1;
2677                         j2 = j;
2678                       }
2679 
2680                     m_glfcns.glBegin (GL_QUADS);
2681 
2682                     // Vertex 1
2683                     if (fc_mode == TEXTURE)
2684                       tex.tex_coord (double (i-1) / (zc-1),
2685                                      double (j-1) / (zr-1));
2686                     else if (fc_mode > 0)
2687                       {
2688                         // FIXME: is there a smarter way to do this?
2689                         for (int k = 0; k < 3; k++)
2690                           cb[k] = c(j-1, i-1, k);
2691                         m_glfcns.glColor4fv (cb);
2692 
2693                         if (fl_mode > 0)
2694                           {
2695                             for (int k = 0; k < 3; k++)
2696                               cb[k] *= as;
2697                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2698 
2699                             for (int k = 0; k < 3; k++)
2700                               cb[k] = ds * c(j-1, i-1, k);
2701                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2702 
2703                             for (int k = 0; k < 3; k++)
2704                               cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k));
2705                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2706                           }
2707                       }
2708                     if (fl_mode > 0)
2709                       set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn),
2710                                   j-1, i-1);
2711 
2712                     m_glfcns.glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
2713 
2714                     // Vertex 2
2715                     if (fc_mode == TEXTURE)
2716                       tex.tex_coord (double (i) / (zc-1), double (j-1) / (zr-1));
2717                     else if (fc_mode == INTERP)
2718                       {
2719                         for (int k = 0; k < 3; k++)
2720                           cb[k] = c(j-1, i, k);
2721                         m_glfcns.glColor4fv (cb);
2722 
2723                         if (fl_mode > 0)
2724                           {
2725                             for (int k = 0; k < 3; k++)
2726                               cb[k] *= as;
2727                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2728 
2729                             for (int k = 0; k < 3; k++)
2730                               cb[k] = ds * c(j-1, i, k);
2731                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2732 
2733                             for (int k = 0; k < 3; k++)
2734                               cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
2735                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2736                           }
2737                       }
2738 
2739                     if (fl_mode == GOURAUD)
2740                       set_normal (bfl_mode, vn, j-1, i);
2741 
2742                     m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
2743 
2744                     // Vertex 3
2745                     if (fc_mode == TEXTURE)
2746                       tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1));
2747                     else if (fc_mode == INTERP)
2748                       {
2749                         for (int k = 0; k < 3; k++)
2750                           cb[k] = c(j, i, k);
2751                         m_glfcns.glColor4fv (cb);
2752 
2753                         if (fl_mode > 0)
2754                           {
2755                             for (int k = 0; k < 3; k++)
2756                               cb[k] *= as;
2757                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2758 
2759                             for (int k = 0; k < 3; k++)
2760                               cb[k] = ds * c(j, i, k);
2761                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2762 
2763                             for (int k = 0; k < 3; k++)
2764                               cb[k] = ss * (scr + (1-scr) * c(j, i, k));
2765                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2766                           }
2767                       }
2768                     if (fl_mode == GOURAUD)
2769                       set_normal (bfl_mode, vn, j, i);
2770 
2771                     m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
2772 
2773                     // Vertex 4
2774                     if (fc_mode == TEXTURE)
2775                       tex.tex_coord (double (i-1) / (zc-1), double (j) / (zr-1));
2776                     else if (fc_mode == INTERP)
2777                       {
2778                         for (int k = 0; k < 3; k++)
2779                           cb[k] = c(j, i-1, k);
2780                         m_glfcns.glColor4fv (cb);
2781 
2782                         if (fl_mode > 0)
2783                           {
2784                             for (int k = 0; k < 3; k++)
2785                               cb[k] *= as;
2786                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2787 
2788                             for (int k = 0; k < 3; k++)
2789                               cb[k] = ds * c(j, i-1, k);
2790                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2791 
2792                             for (int k = 0; k < 3; k++)
2793                               cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
2794                             m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2795                           }
2796                       }
2797                     if (fl_mode == GOURAUD)
2798                       set_normal (bfl_mode, vn, j, i-1);
2799 
2800                     m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
2801 
2802                     m_glfcns.glEnd ();
2803                   }
2804               }
2805 
2806             set_polygon_offset (false);
2807             if (fc_mode == TEXTURE)
2808               m_glfcns.glDisable (GL_TEXTURE_2D);
2809 
2810             if ((fl_mode > 0) && do_lighting)
2811               m_glfcns.glDisable (GL_LIGHTING);
2812           }
2813         else
2814           {
2815             // FIXME: implement flat, interp and texturemap transparency
2816           }
2817       }
2818 
2819     if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))
2820       {
2821         if (props.get_edgealpha_double () == 1)
2822           {
2823             cb[3] = 1.0; // edgealpha isn't implemented yet
2824             if (ec_mode == UNIFORM)
2825               {
2826                 m_glfcns.glColor3dv (ecolor.data ());
2827                 if (el_mode > 0)
2828                   {
2829                     for (int i = 0; i < 3; i++)
2830                       cb[i] = as * ecolor(i);
2831                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2832 
2833                     for (int i = 0; i < 3; i++)
2834                       cb[i] = ds * ecolor(i);
2835                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2836 
2837                     for (int i = 0; i < 3; i++)
2838                       cb[i] = ss * (scr + (1-scr) * ecolor(i));
2839                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2840                   }
2841               }
2842 
2843             if ((el_mode > 0) && do_lighting)
2844               m_glfcns.glEnable (GL_LIGHTING);
2845             m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
2846                                    ? GL_SMOOTH : GL_FLAT);
2847 
2848             set_linestyle (props.get_linestyle (), false,
2849                            props.get_linewidth ());
2850             set_linewidth (props.get_linewidth ());
2851             set_linecap ("butt");
2852             set_linejoin ("miter");
2853 
2854             // Mesh along Y-axis
2855 
2856             if (props.meshstyle_is ("both") || props.meshstyle_is ("column"))
2857               {
2858                 for (int i = 0; i < zc; i++)
2859                   {
2860                     if (y_mat)
2861                       {
2862                         i1 = i-1;
2863                         i2 = i;
2864                       }
2865 
2866                     for (int j = 1; j < zr; j++)
2867                       {
2868                         if (clip(j-1,i) || clip(j,i))
2869                           continue;
2870 
2871                         if (ec_mode == FLAT)
2872                           {
2873                             // "flat" only needs color at lower-left vertex
2874                             if (! math::isfinite (c(j-1,i)))
2875                               continue;
2876                           }
2877                         else if (ec_mode == INTERP)
2878                           {
2879                             // "interp" needs valid color at both vertices
2880                             if (! (math::isfinite (c(j-1, i)) && math::isfinite (c(j, i))))
2881                               continue;
2882                           }
2883 
2884                         if (x_mat)
2885                           {
2886                             j1 = j-1;
2887                             j2 = j;
2888                           }
2889 
2890                         m_glfcns.glBegin (GL_LINES);
2891 
2892                         // Vertex 1
2893                         if (ec_mode > 0)
2894                           {
2895                             for (int k = 0; k < 3; k++)
2896                               cb[k] = c(j-1, i, k);
2897                             m_glfcns.glColor3fv (cb);
2898 
2899                             if (el_mode > 0)
2900                               {
2901                                 for (int k = 0; k < 3; k++)
2902                                   cb[k] *= as;
2903                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2904 
2905                                 for (int k = 0; k < 3; k++)
2906                                   cb[k] = ds * c(j-1, i, k);
2907                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2908 
2909                                 for (int k = 0; k < 3; k++)
2910                                   cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
2911                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2912                               }
2913                           }
2914                         if (el_mode > 0)
2915                           {
2916                             if (el_mode == GOURAUD)
2917                               set_normal (bfl_mode, vn, j-1, i);
2918                             else
2919                               set_normal (bfl_mode, fn, j-1, std::min (i, zc-2));
2920                           }
2921 
2922                         m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
2923 
2924                         // Vertex 2
2925                         if (ec_mode == INTERP)
2926                           {
2927                             for (int k = 0; k < 3; k++)
2928                               cb[k] = c(j, i, k);
2929                             m_glfcns.glColor3fv (cb);
2930 
2931                             if (el_mode > 0)
2932                               {
2933                                 for (int k = 0; k < 3; k++)
2934                                   cb[k] *= as;
2935                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2936 
2937                                 for (int k = 0; k < 3; k++)
2938                                   cb[k] = ds * c(j, i, k);
2939                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2940 
2941                                 for (int k = 0; k < 3; k++)
2942                                   cb[k] = ss * (scr + (1-scr) * c(j, i, k));
2943                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
2944                               }
2945                           }
2946                         if (el_mode == GOURAUD)
2947                           set_normal (bfl_mode, vn, j, i);
2948 
2949                         m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
2950 
2951                         m_glfcns.glEnd ();
2952                       }
2953                   }
2954               }
2955 
2956             // Mesh along X-axis
2957 
2958             if (props.meshstyle_is ("both") || props.meshstyle_is ("row"))
2959               {
2960                 for (int j = 0; j < zr; j++)
2961                   {
2962                     if (x_mat)
2963                       {
2964                         j1 = j-1;
2965                         j2 = j;
2966                       }
2967 
2968                     for (int i = 1; i < zc; i++)
2969                       {
2970                         if (clip(j,i-1) || clip(j,i))
2971                           continue;
2972 
2973                         if (ec_mode == FLAT)
2974                           {
2975                             // "flat" only needs color at lower-left vertex
2976                             if (! math::isfinite (c(j,i-1)))
2977                               continue;
2978                           }
2979                         else if (ec_mode == INTERP)
2980                           {
2981                             // "interp" needs valid color at both vertices
2982                             if (! (math::isfinite (c(j, i-1)) && math::isfinite (c(j, i))))
2983                               continue;
2984                           }
2985 
2986                         if (y_mat)
2987                           {
2988                             i1 = i-1;
2989                             i2 = i;
2990                           }
2991 
2992                         m_glfcns.glBegin (GL_LINES);
2993 
2994                         // Vertex 1
2995                         if (ec_mode > 0)
2996                           {
2997                             for (int k = 0; k < 3; k++)
2998                               cb[k] = c(j, i-1, k);
2999                             m_glfcns.glColor3fv (cb);
3000 
3001                             if (el_mode > 0)
3002                               {
3003                                 for (int k = 0; k < 3; k++)
3004                                   cb[k] *= as;
3005                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
3006 
3007                                 for (int k = 0; k < 3; k++)
3008                                   cb[k] = ds * c(j, i-1, k);
3009                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
3010 
3011                                 for (int k = 0; k < 3; k++)
3012                                   cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
3013                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
3014                               }
3015                           }
3016                         if (el_mode > 0)
3017                           {
3018                             if (el_mode == GOURAUD)
3019                               set_normal (bfl_mode, vn, j, i-1);
3020                             else
3021                               set_normal (bfl_mode, fn, std::min (j, zr-2), i-1);
3022                           }
3023 
3024                         m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
3025 
3026                         // Vertex 2
3027                         if (ec_mode == INTERP)
3028                           {
3029                             for (int k = 0; k < 3; k++)
3030                               cb[k] = c(j, i, k);
3031                             m_glfcns.glColor3fv (cb);
3032 
3033                             if (el_mode > 0)
3034                               {
3035                                 for (int k = 0; k < 3; k++)
3036                                   cb[k] *= as;
3037                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
3038 
3039                                 for (int k = 0; k < 3; k++)
3040                                   cb[k] = ds * c(j, i, k);
3041                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
3042 
3043                                 for (int k = 0; k < 3; k++)
3044                                   cb[k] = ss * (scr + (1-scr) * c(j, i, k));
3045                                 m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
3046                               }
3047                           }
3048                         if (el_mode == GOURAUD)
3049                           set_normal (bfl_mode, vn, j, i);
3050 
3051                         m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
3052 
3053                         m_glfcns.glEnd ();
3054                       }
3055                   }
3056               }
3057 
3058             set_linestyle ("-");  // Disable LineStipple
3059             set_linewidth (0.5f);
3060 
3061             if ((el_mode > 0) && do_lighting)
3062               m_glfcns.glDisable (GL_LIGHTING);
3063           }
3064         else
3065           {
3066             // FIXME: implement transparency
3067           }
3068       }
3069 
3070     if (! props.marker_is ("none")
3071         && ! (props.markeredgecolor_is ("none")
3072               && props.markerfacecolor_is ("none")))
3073       {
3074         // FIXME: check how transparency should be handled in markers
3075         // FIXME: check what to do with marker facecolor set to auto
3076         //        and facecolor set to none.
3077 
3078         bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
3079         bool do_face = draw_all || ! props.markerfacecolor_is ("none");
3080 
3081         Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
3082                           props.get_markeredgecolor_rgb ());
3083         Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
3084                           props.get_markerfacecolor_rgb ());
3085         Matrix cc (1, 3, 0.0);
3086 
3087         if (mecolor.isempty () && props.markeredgecolor_is ("auto"))
3088           {
3089             mecolor = props.get_edgecolor_rgb ();
3090             do_edge = ! props.edgecolor_is ("none");
3091           }
3092 
3093         if (mfcolor.isempty () && props.markerfacecolor_is ("auto"))
3094           {
3095             mfcolor = props.get_facecolor_rgb ();
3096             do_face = ! props.facecolor_is ("none");
3097           }
3098 
3099         if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ())
3100           c = props.get_color_data ().array_value ();
3101 
3102         init_marker (props.get_marker (), props.get_markersize (),
3103                      props.get_linewidth ());
3104 
3105         uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
3106         uint8_t clip_ok = 0x40;
3107 
3108         for (int i = 0; i < zc; i++)
3109           {
3110             if (y_mat)
3111               i1 = i;
3112 
3113             for (int j = 0; j < zr; j++)
3114               {
3115                 if (x_mat)
3116                   j1 = j;
3117 
3118                 if ((clip_code (x(j1,i), y(j,i1), z(j,i)) & clip_mask)
3119                     != clip_ok)
3120                   continue;
3121 
3122                 if ((do_edge && mecolor.isempty ())
3123                     || (do_face && mfcolor.isempty ()))
3124                   {
3125                     if (! math::isfinite (c(j,i)))
3126                       continue;  // Skip NaNs in color data
3127 
3128                     for (int k = 0; k < 3; k++)
3129                       cc(k) = c(j,i,k);
3130                   }
3131 
3132                 Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor)
3133                                      : Matrix ());
3134                 Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor)
3135                                      : Matrix ());
3136 
3137                 draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc);
3138               }
3139           }
3140 
3141         end_marker ();
3142       }
3143 
3144 #else
3145 
3146     octave_unused_parameter (props);
3147 
3148     // This shouldn't happen because construction of opengl_renderer
3149     // objects is supposed to be impossible if OpenGL is not available.
3150 
3151     panic_impossible ();
3152 
3153 #endif
3154   }
3155 
3156   // FIXME: global optimization (rendering, data structures...),
3157   // there is probably a smarter/faster/less-memory-consuming way to do this.
3158   void
draw_patch(const patch::properties & props)3159   opengl_renderer::draw_patch (const patch::properties& props)
3160   {
3161 #if defined (HAVE_OPENGL)
3162 
3163     // Do not render if the patch has incoherent data
3164     std::string msg;
3165     if (props.has_bad_data (msg))
3166       {
3167         warning ("opengl_renderer: %s.  Not rendering.", msg.c_str ());
3168         return;
3169       }
3170 
3171     bool draw_all = selecting && props.pickableparts_is ("all");
3172     const Matrix f = props.get_faces ().matrix_value ();
3173     const Matrix v = xform.scale (props.get_vertices ().matrix_value ());
3174     Matrix c;
3175     Matrix a;
3176     double fa = 1.0;
3177 
3178     int nv = v.rows ();
3179     int nf = f.rows ();
3180     int fcmax = f.columns ();
3181 
3182     bool has_z = (v.columns () > 2);
3183     bool has_facecolor = false;
3184     bool has_facealpha = false;
3185 
3186     int fc_mode = ((props.facecolor_is ("none")
3187                     || props.facecolor_is_rgb () || draw_all) ? 0 :
3188                    (props.facecolor_is ("flat") ? 1 : 2));
3189     int fl_mode = (props.facelighting_is ("none") ? 0 :
3190                    (props.facelighting_is ("flat") ? 1 : 2));
3191     int fa_mode = (props.facealpha_is_double () ? 0 :
3192                    (props.facealpha_is ("flat") ? 1 : 2));
3193     int ec_mode = ((props.edgecolor_is ("none")
3194                     || props.edgecolor_is_rgb ()) ? 0 :
3195                    (props.edgecolor_is ("flat") ? 1 : 2));
3196     int el_mode = (props.edgelighting_is ("none") ? 0 :
3197                    (props.edgelighting_is ("flat") ? 1 : 2));
3198     int ea_mode = (props.edgealpha_is_double () ? 0 :
3199                    (props.edgealpha_is ("flat") ? 1 : 2));
3200     int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
3201                     (props.backfacelighting_is ("reverselit") ? 1 : 2));
3202     bool do_lighting = props.get_do_lighting ();
3203 
3204     Matrix fcolor = props.get_facecolor_rgb ();
3205     Matrix ecolor = props.get_edgecolor_rgb ();
3206 
3207     float as = props.get_ambientstrength ();
3208     float ds = props.get_diffusestrength ();
3209     float ss = props.get_specularstrength ();
3210     float se = props.get_specularexponent () * 5; // to fit Matlab
3211     float scr = props.get_specularcolorreflectance ();
3212 
3213     const Matrix vn = props.get_vertexnormals ().matrix_value ();
3214     bool has_vertex_normals = (vn.rows () == nv);
3215     const Matrix fn = props.get_facenormals ().matrix_value ();
3216     bool has_face_normals = (fn.rows () == nf);
3217 
3218     boolMatrix clip (1, nv, false);
3219 
3220     if (has_z)
3221       for (int i = 0; i < nv; i++)
3222         clip(i) = is_nan_or_inf (v(i,0), v(i,1), v(i,2));
3223     else
3224       for (int i = 0; i < nv; i++)
3225         clip(i) = is_nan_or_inf (v(i,0), v(i,1), 0);
3226 
3227     boolMatrix clip_f (1, nf, false);
3228     Array<int> count_f (dim_vector (nf, 1), 0);
3229 
3230     for (int i = 0; i < nf; i++)
3231       {
3232         bool fclip = false;
3233         int count = 0;
3234 
3235         for (int j = 0; j < fcmax && ! math::isnan (f(i,j)); j++, count++)
3236           fclip = (fclip || clip(int (f(i,j) - 1)));
3237 
3238         clip_f(i) = fclip;
3239         count_f(i) = count;
3240       }
3241 
3242     if (draw_all || fc_mode > 0 || ec_mode > 0)
3243       {
3244         if (draw_all)
3245           c = Matrix (1, 3, 0.0);
3246         else
3247           c = props.get_color_data ().matrix_value ();
3248 
3249         if (c.rows () == 1)
3250           {
3251             // Single color specifications, we can simplify a little bit
3252 
3253             if (draw_all || fc_mode > 0)
3254               {
3255                 fcolor = c;
3256                 fc_mode = UNIFORM;
3257               }
3258 
3259             if (draw_all || ec_mode > 0)
3260               {
3261                 ecolor = c;
3262                 ec_mode = UNIFORM;
3263               }
3264 
3265             c = Matrix ();
3266           }
3267         else
3268           has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ()));
3269       }
3270 
3271     if (fa_mode > 0 || ea_mode > 0)
3272       {
3273         // FIXME: retrieve alpha data from patch object
3274         //a = props.get_alpha_data ();
3275         has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ()));
3276       }
3277 
3278     if (fa_mode == 0)
3279       fa = props.get_facealpha_double ();
3280 
3281     octave_idx_type fr = f.rows ();
3282     std::vector<vertex_data> vdata (f.numel ());
3283 
3284     for (int i = 0; i < nf; i++)
3285       for (int j = 0; j < count_f(i); j++)
3286         {
3287           int idx = int (f(i,j) - 1);
3288 
3289           Matrix vv (1, 3, 0.0);
3290           Matrix cc;
3291           Matrix vnn (1, 3, 0.0);
3292           Matrix fnn (1, 3, 0.0);
3293           double aa = 1.0;
3294 
3295           vv(0) = v(idx,0); vv(1) = v(idx,1);
3296           if (has_z)
3297             vv(2) = v(idx,2);
3298           if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals)
3299             {
3300               double dir = 1.0;
3301               if (bfl_mode > 0)
3302                 dir = ((fn(i,0) * view_vector(0)
3303                         + fn(i,1) * view_vector(1)
3304                         + fn(i,2) * view_vector(2) < 0)
3305                        ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
3306               fnn(0) = dir * fn(i,0);
3307               fnn(1) = dir * fn(i,1);
3308               fnn(2) = dir * fn(i,2);
3309             }
3310           if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals)
3311             {
3312               double dir = 1.0;
3313               if (bfl_mode > 0)
3314                 dir = ((vn(idx,0) * view_vector(0)
3315                         + vn(idx,1) * view_vector(1)
3316                         + vn(idx,2) * view_vector(2) < 0)
3317                        ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
3318               vnn(0) = dir * vn(idx,0);
3319               vnn(1) = dir * vn(idx,1);
3320               vnn(2) = dir * vn(idx,2);
3321             }
3322           if (c.numel () > 0)
3323             {
3324               cc.resize (1, 3);
3325               if (has_facecolor)
3326                 cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2);
3327               else
3328                 cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2);
3329             }
3330           if (fa_mode == 0)
3331             aa = fa;
3332           else if (a.numel () > 0)
3333             {
3334               if (has_facealpha)
3335                 aa = a(i);
3336               else
3337                 aa = a(idx);
3338             }
3339 
3340           vdata[i+j*fr] = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr);
3341         }
3342 
3343     if (fl_mode > 0 || el_mode > 0)
3344       m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
3345 
3346     if (draw_all || ! props.facecolor_is ("none"))
3347       {
3348         // FIXME: adapt to double-radio property
3349         if (fa_mode == 0)
3350           {
3351             if (fc_mode == UNIFORM)
3352               {
3353                 m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
3354                 if (fl_mode > 0)
3355                   {
3356                     float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };;
3357 
3358                     for (int i = 0; i < 3; i++)
3359                       cb[i] = as * fcolor(i);
3360                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
3361 
3362                     for (int i = 0; i < 3; i++)
3363                       cb[i] = ds * fcolor(i);
3364                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
3365 
3366                     for (int i = 0; i < 3; i++)
3367                       cb[i] = ss * (scr + (1-scr) * fcolor(i));
3368                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
3369                   }
3370               }
3371 
3372             if ((fl_mode > 0) && do_lighting)
3373               m_glfcns.glEnable (GL_LIGHTING);
3374 
3375             // NOTE: Push filled part of patch backwards to avoid Z-fighting
3376             // with tessellator outline.  A value of 1.0 seems to work fine.
3377             // Value can't be too large or the patch will be pushed below the
3378             // axes planes at +2.5.
3379             patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0);
3380 
3381             std::vector<octave_idx_type>::const_iterator it;
3382             octave_idx_type i_start, i_end;
3383 
3384             for (int i = 0; i < nf; i++)
3385               {
3386                 if (clip_f(i))
3387                   continue;
3388 
3389                 bool is_non_planar = false;
3390                 if (props.coplanar_last_idx.size () > 0
3391                     && props.coplanar_last_idx[i].size () > 1)
3392                   {
3393                     is_non_planar = true;
3394                     it = props.coplanar_last_idx[i].end ();
3395                     it--;
3396                   }
3397 
3398                 // loop over planar subsets of face
3399                 do
3400                   {
3401                     if (is_non_planar)
3402                       {
3403                         i_end = *it;
3404                         if (it == props.coplanar_last_idx[i].begin ())
3405                           i_start = 0;
3406                         else
3407                           {
3408                             it--;
3409                             i_start = *it - 1;
3410                           }
3411                       }
3412                     else
3413                       {
3414                         i_end = count_f(i) - 1;
3415                         i_start = 0;
3416                       }
3417 
3418                     tess.begin_polygon (true);
3419                     tess.begin_contour ();
3420 
3421                     // Add vertices in reverse order for Matlab compatibility
3422                     for (int j = i_end; j > i_start; j--)
3423                       {
3424                         vertex_data::vertex_data_rep *vv
3425                           = vdata[i+j*fr].get_rep ();
3426 
3427                         tess.add_vertex (vv->coords.fortran_vec (), vv);
3428                       }
3429 
3430                     if (count_f(i) > 0)
3431                       {
3432                         vertex_data::vertex_data_rep *vv = vdata[i].get_rep ();
3433 
3434                         if (fc_mode == FLAT)
3435                           {
3436                             // For "flat" shading, use color of 1st vertex.
3437                             Matrix col = vv->color;
3438 
3439                             if (col.numel () == 3)
3440                               {
3441                                 m_glfcns.glColor4d (col(0), col(1), col(2), fa);
3442                                 if (fl_mode > 0)
3443                                   {
3444                                     float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
3445 
3446                                     for (int k = 0; k < 3; k++)
3447                                       cb[k] = (vv->ambient * col(k));
3448                                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
3449 
3450                                     for (int k = 0; k < 3; k++)
3451                                       cb[k] = (vv->diffuse * col(k));
3452                                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
3453 
3454                                     for (int k = 0; k < 3; k++)
3455                                       cb[k] = vv->specular *
3456                                               (vv->specular_color_refl
3457                                                + (1-vv->specular_color_refl) *
3458                                               col(k));
3459                                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
3460                                   }
3461                               }
3462                           }
3463 
3464                         tess.add_vertex (vv->coords.fortran_vec (), vv);
3465                       }
3466 
3467                     tess.end_contour ();
3468                     tess.end_polygon ();
3469                   } while (i_start > 0);
3470               }
3471 
3472             if ((fl_mode > 0) && do_lighting)
3473               m_glfcns.glDisable (GL_LIGHTING);
3474           }
3475         else
3476           {
3477             // FIXME: implement flat and interp transparency
3478           }
3479       }
3480 
3481     if (draw_all
3482         || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")))
3483       {
3484         // FIXME: adapt to double-radio property
3485         if (props.get_edgealpha_double () == 1)
3486           {
3487             if (ec_mode == UNIFORM)
3488               {
3489                 m_glfcns.glColor3dv (ecolor.data ());
3490                 if (el_mode > 0)
3491                   {
3492                     // edge lighting only uses ambient light
3493                     float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
3494                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
3495                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
3496 
3497                     for (int i = 0; i < 3; i++)
3498                       cb[i] = (as * ecolor(i));
3499                     m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
3500                   }
3501               }
3502 
3503             if ((el_mode > 0) && do_lighting)
3504               m_glfcns.glEnable (GL_LIGHTING);
3505 
3506             double linewidth = props.get_linewidth ();
3507             set_linestyle (props.get_linestyle (), false, linewidth);
3508             set_linewidth (linewidth);
3509             set_linecap ("butt");
3510             set_linejoin ("miter");
3511 
3512             // NOTE: patch contour cannot be offset.  Offset must occur with the
3513             // filled portion of the patch above.  The tessellator uses
3514             // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL
3515             // automatically sets the glType to GL_LINE_LOOP.  This primitive is
3516             // not supported by glPolygonOffset which is used to do Z offsets.
3517             patch_tessellator tess (this, ec_mode, el_mode, false);
3518 
3519             for (int i = 0; i < nf; i++)
3520               {
3521                 bool is_non_planar = false;
3522                 if (props.coplanar_last_idx.size () > 0
3523                     && props.coplanar_last_idx[i].size () > 1)
3524                   is_non_planar = true;
3525                 if (clip_f(i) || is_non_planar)
3526                   {
3527                     // This is an unclosed contour or a non-planar face.
3528                     // Draw it as a line.
3529                     bool flag = false;
3530 
3531                     m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
3532                                            ? GL_SMOOTH : GL_FLAT);
3533 
3534                     // Add vertices in reverse order for Matlab compatibility
3535                     for (int j = count_f(i)-1; j >= 0; j--)
3536                       {
3537                         if (! clip(int (f(i,j) - 1)))
3538                           {
3539                             vertex_data::vertex_data_rep *vv
3540                               = vdata[i+j*fr].get_rep ();
3541                             const Matrix m = vv->coords;
3542                             if (! flag)
3543                               {
3544                                 flag = true;
3545                                 m_glfcns.glBegin (GL_LINE_STRIP);
3546                               }
3547                             if (ec_mode != UNIFORM)
3548                               {
3549                                 Matrix col = vv->color;
3550 
3551                                 if (col.numel () == 3)
3552                                   m_glfcns.glColor3dv (col.data ());
3553                               }
3554                             m_glfcns.glVertex3d (m(0), m(1), m(2));
3555                           }
3556                         else if (flag)
3557                           {
3558                             flag = false;
3559                             m_glfcns.glEnd ();
3560                           }
3561                       }
3562                     // Do loop body with vertex N to "close" GL_LINE_STRIP
3563                     // from vertex 0 to vertex N.
3564                     int j = count_f(i)-1;
3565                     if (flag && ! clip(int (f(i,j) - 1)))
3566                       {
3567                         vertex_data::vertex_data_rep *vv
3568                           = vdata[i+j*fr].get_rep ();
3569                         const Matrix m = vv->coords;
3570                         if (ec_mode != UNIFORM)
3571                           {
3572                             Matrix col = vv->color;
3573 
3574                             if (col.numel () == 3)
3575                               m_glfcns.glColor3dv (col.data ());
3576                           }
3577                         m_glfcns.glVertex3d (m(0), m(1), m(2));
3578                       }
3579 
3580                     if (flag)
3581                       m_glfcns.glEnd ();
3582                   }
3583                 else  // Normal edge contour drawn with tessellator
3584                   {
3585                     tess.begin_polygon (false);
3586                     tess.begin_contour ();
3587 
3588                     for (int j = count_f(i)-1; j >= 0; j--)
3589                       {
3590                         vertex_data::vertex_data_rep *vv
3591                           = vdata[i+j*fr].get_rep ();
3592                         tess.add_vertex (vv->coords.fortran_vec (), vv);
3593                       }
3594 
3595                     tess.end_contour ();
3596                     tess.end_polygon ();
3597                   }
3598               }
3599 
3600             set_linestyle ("-");  // Disable LineStipple
3601             set_linewidth (0.5f);
3602 
3603             if ((el_mode > 0) && do_lighting)
3604               m_glfcns.glDisable (GL_LIGHTING);
3605           }
3606         else
3607           {
3608             // FIXME: implement transparency
3609           }
3610       }
3611 
3612     if (! props.marker_is ("none")
3613         && ! (props.markeredgecolor_is ("none")
3614               && props.markerfacecolor_is ("none")))
3615       {
3616         bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
3617         bool do_face = draw_all || ! props.markerfacecolor_is ("none");
3618 
3619         Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
3620                           props.get_markeredgecolor_rgb ());
3621         Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
3622                           props.get_markerfacecolor_rgb ());
3623 
3624         bool has_markerfacecolor = draw_all || false;
3625 
3626         if ((mecolor.isempty () && ! props.markeredgecolor_is ("none"))
3627             || (mfcolor.isempty () && ! props.markerfacecolor_is ("none")))
3628           {
3629             Matrix mc = props.get_color_data ().matrix_value ();
3630 
3631             if (mc.rows () == 1)
3632               {
3633                 // Single color specifications, we can simplify a little bit
3634                 if (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))
3635                   mfcolor = mc;
3636 
3637                 if (mecolor.isempty () && ! props.markeredgecolor_is ("none"))
3638                   mecolor = mc;
3639               }
3640             else
3641               {
3642                 if (c.isempty ())
3643                   c = props.get_color_data ().matrix_value ();
3644                 has_markerfacecolor = ((c.numel () > 0)
3645                                        && (c.rows () == f.rows ()));
3646               }
3647           }
3648 
3649         init_marker (props.get_marker (), props.get_markersize (),
3650                      props.get_linewidth ());
3651 
3652         uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
3653         uint8_t clip_ok = 0x40;
3654 
3655         for (int i = 0; i < nf; i++)
3656           for (int j = 0; j < count_f(i); j++)
3657             {
3658               int idx = int (f(i,j) - 1);
3659 
3660               if ((clip_code (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0))
3661                    & clip_mask) != clip_ok)
3662                 continue;
3663 
3664               Matrix cc;
3665               if (c.numel () > 0)
3666                 {
3667                   cc.resize (1, 3);
3668                   if (has_markerfacecolor)
3669                     cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2);
3670                   else
3671                     cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2);
3672                 }
3673 
3674               Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor)
3675                                    : Matrix ());
3676               Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor)
3677                                    : Matrix ());
3678 
3679               draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc);
3680             }
3681 
3682         end_marker ();
3683       }
3684 
3685 #else
3686 
3687     octave_unused_parameter (props);
3688 
3689     // This shouldn't happen because construction of opengl_renderer
3690     // objects is supposed to be impossible if OpenGL is not available.
3691 
3692     panic_impossible ();
3693 
3694 #endif
3695   }
3696 
3697   void
draw_light(const light::properties & props)3698   opengl_renderer::draw_light (const light::properties& props)
3699   {
3700 #if defined (HAVE_OPENGL)
3701 
3702     // enable light source
3703     m_glfcns.glEnable (m_current_light);
3704 
3705     // light position
3706     float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local
3707     Matrix lpos = props.get_position ().matrix_value ();
3708     for (int i = 0; i < 3; i++)
3709       pos[i] = lpos(i);
3710     if (props.style_is ("local"))
3711       pos[3] = 1;
3712     m_glfcns.glLightfv (m_current_light, GL_POSITION, pos);
3713 
3714     // light color
3715     float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning)
3716     Matrix lcolor = props.get_color ().matrix_value ();
3717     for (int i = 0; i < 3; i++)
3718       col[i] = lcolor(i);
3719     m_glfcns.glLightfv (m_current_light, GL_DIFFUSE,  col);
3720     m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col);
3721 
3722 #else
3723 
3724     octave_unused_parameter (props);
3725 
3726     // This shouldn't happen because construction of opengl_renderer
3727     // objects is supposed to be impossible if OpenGL is not available.
3728 
3729     panic_impossible ();
3730 
3731 #endif
3732   }
3733 
3734   void
draw_hggroup(const hggroup::properties & props)3735   opengl_renderer::draw_hggroup (const hggroup::properties& props)
3736   {
3737     draw (props.get_children ());
3738   }
3739 
3740   void
draw_text(const text::properties & props)3741   opengl_renderer::draw_text (const text::properties& props)
3742   {
3743 #if defined (HAVE_OPENGL)
3744 
3745     if (props.get_string ().isempty () || props.color_is ("none"))
3746       return;
3747 
3748     Matrix pos = xform.scale (props.get_data_position ());
3749 
3750     // Handle clipping manually when drawing text background
3751     if (! props.is_clipping ()
3752         || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40))
3753       {
3754         set_clipping (false);
3755         draw_text_background (props);
3756         set_clipping (props.is_clipping ());
3757       }
3758 
3759     set_font (props);
3760 
3761     const Matrix bbox = props.get_extent_matrix ();
3762 
3763     bool blend = m_glfcns.glIsEnabled (GL_BLEND);
3764 
3765     m_glfcns.glEnable (GL_BLEND);
3766     m_glfcns.glEnable (GL_ALPHA_TEST);
3767     m_glfcns.glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0);
3768     m_glfcns.glBitmap (0, 0, 0, 0, bbox(0), bbox(1), nullptr);
3769     m_glfcns.glDrawPixels (bbox(2), bbox(3), GL_RGBA, GL_UNSIGNED_BYTE,
3770                            props.get_pixels ().data ());
3771     m_glfcns.glDisable (GL_ALPHA_TEST);
3772     if (! blend)
3773       m_glfcns.glDisable (GL_BLEND);
3774 
3775 #else
3776 
3777     octave_unused_parameter (props);
3778 
3779     // This shouldn't happen because construction of opengl_renderer
3780     // objects is supposed to be impossible if OpenGL is not available.
3781 
3782     panic_impossible ();
3783 
3784 #endif
3785   }
3786 
3787   void
draw_text_background(const text::properties & props,bool do_rotate)3788   opengl_renderer::draw_text_background (const text::properties& props,
3789                                          bool do_rotate)
3790   {
3791 #if defined (HAVE_OPENGL)
3792 
3793     Matrix bgcol = props.get_backgroundcolor_rgb ();
3794     Matrix ecol = props.get_edgecolor_rgb ();
3795 
3796     if (bgcol.isempty () && ecol.isempty ())
3797       return;
3798 
3799     Matrix pos = props.get_data_position ();
3800     ColumnVector pixpos = get_transform ().transform (pos(0), pos(1),
3801                                                       pos(2), true);
3802 
3803     // Save current transform matrices and set orthogonal window coordinates
3804     m_glfcns.glMatrixMode (GL_PROJECTION);
3805     m_glfcns.glPushMatrix ();
3806     m_glfcns.glLoadIdentity ();
3807 
3808     Matrix vp = get_viewport_scaled ();
3809     m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
3810     m_glfcns.glMatrixMode (GL_MODELVIEW);
3811     m_glfcns.glPushMatrix ();
3812     m_glfcns.glLoadIdentity ();
3813 
3814     // Translate coordinates so that the text anchor is (0,0)
3815     m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
3816 
3817     // FIXME: Only multiples of 90° are handled by the text renderer.
3818     //        Handle others here.
3819     double rotation = props.get_rotation ();
3820 
3821     if (do_rotate && rotation != 0.0 && rotation != 90.0
3822         && rotation != 180.0 && rotation != 270.0)
3823       m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
3824 
3825     double m = points_to_pixels (props.get_margin ());
3826     const Matrix bbox = props.get_extent_matrix ();
3827     double x0 = bbox (0) / m_devpixratio - m;
3828     double x1 = x0 + bbox(2) / m_devpixratio + 2 * m;
3829     double y0 = -(bbox (1) / m_devpixratio - m);
3830     double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m);
3831 
3832     if (! bgcol.isempty ())
3833       {
3834         m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2));
3835 
3836         bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST);
3837         if (depth_test)
3838           set_polygon_offset (true, 4.0);
3839 
3840         m_glfcns.glBegin (GL_QUADS);
3841         m_glfcns.glVertex2d (x0, y0);
3842         m_glfcns.glVertex2d (x1, y0);
3843         m_glfcns.glVertex2d (x1, y1);
3844         m_glfcns.glVertex2d (x0, y1);
3845         m_glfcns.glEnd ();
3846 
3847         if (depth_test)
3848           set_polygon_offset (false);
3849       }
3850 
3851     if (! ecol.isempty ())
3852       {
3853         m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2));
3854 
3855         set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
3856         set_linewidth (props.get_linewidth ());
3857 
3858         m_glfcns.glBegin (GL_LINE_STRIP);
3859         m_glfcns.glVertex2d (x0, y0);
3860         m_glfcns.glVertex2d (x1, y0);
3861         m_glfcns.glVertex2d (x1, y1);
3862         m_glfcns.glVertex2d (x0, y1);
3863         m_glfcns.glVertex2d (x0, y0);
3864         m_glfcns.glEnd ();
3865 
3866         set_linestyle ("-");
3867       }
3868 
3869     // Restore previous coordinate system
3870     m_glfcns.glPopMatrix();
3871     m_glfcns.glMatrixMode (GL_PROJECTION);
3872     m_glfcns.glPopMatrix();
3873 
3874 #else
3875 
3876     octave_unused_parameter (props);
3877     octave_unused_parameter (do_rotate);
3878 
3879     // This shouldn't happen because construction of opengl_renderer
3880     // objects is supposed to be impossible if OpenGL is not available.
3881 
3882     panic_impossible ();
3883 
3884 #endif
3885   }
3886 
3887   void
draw_image(const image::properties & props)3888   opengl_renderer::draw_image (const image::properties& props)
3889   {
3890 #if defined (HAVE_OPENGL)
3891 
3892     octave_value cdata = props.get_color_data ();
3893     dim_vector dv (cdata.dims ());
3894     int h = dv(0);
3895     int w = dv(1);
3896     double x0, x1, y0, y1;
3897 
3898     Matrix x = props.get_xdata ().matrix_value ();
3899     double dx = 1.0;
3900     if (w > 1)
3901       dx = (x(1) - x(0)) / (w - 1);
3902 
3903     x0 = x(0)-dx/2;
3904     x1 = x(1)+dx/2;
3905 
3906     Matrix y = props.get_ydata ().matrix_value ();
3907     double dy = 1.0;
3908     if (h > 1)
3909       dy = (y(1) - y(0)) / (h - 1);
3910 
3911     y0 = y(0)-dy/2;
3912     y1 = y(1)+dy/2;
3913 
3914     // Expect RGB data
3915     if (dv.ndims () == 3 && dv(2) == 3)
3916       {
3917         opengl_texture tex  = opengl_texture::create (m_glfcns, cdata);
3918         if (tex.is_valid ())
3919           {
3920             m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0);
3921 
3922             m_glfcns.glEnable (GL_TEXTURE_2D);
3923 
3924             m_glfcns.glBegin (GL_QUADS);
3925 
3926             tex.tex_coord (0.0, 0.0);
3927             m_glfcns.glVertex3d (x0, y0, 0.0);
3928 
3929             tex.tex_coord (1.0, 0.0);
3930             m_glfcns.glVertex3d (x1, y0, 0.0);
3931 
3932             tex.tex_coord (1.0, 1.0);
3933             m_glfcns.glVertex3d (x1, y1, 0.0);
3934 
3935             tex.tex_coord (0.0, 1.0);
3936             m_glfcns.glVertex3d (x0, y1, 0.0);
3937 
3938             m_glfcns.glEnd ();
3939             m_glfcns.glDisable (GL_TEXTURE_2D);
3940           }
3941       }
3942     else
3943       warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)");
3944 
3945 #else
3946 
3947     octave_unused_parameter (props);
3948 
3949     // This shouldn't happen because construction of opengl_renderer
3950     // objects is supposed to be impossible if OpenGL is not available.
3951 
3952     panic_impossible ();
3953 
3954 #endif
3955   }
3956 
draw(const Matrix & hlist,bool toplevel)3957   void opengl_renderer::draw (const Matrix& hlist, bool toplevel)
3958   {
3959     int len = hlist.numel ();
3960 
3961     gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw");
3962 
3963     for (int i = len-1; i >= 0; i--)
3964       {
3965         graphics_object obj = gh_mgr.get_object (hlist(i));
3966 
3967         if (obj)
3968           draw (obj, toplevel);
3969       }
3970   }
3971 
3972   void
set_viewport(int w,int h)3973   opengl_renderer::set_viewport (int w, int h)
3974   {
3975 #if defined (HAVE_OPENGL)
3976 
3977     m_glfcns.glViewport (0, 0, w, h);
3978 
3979 #else
3980 
3981     octave_unused_parameter (w);
3982     octave_unused_parameter (h);
3983 
3984     // This shouldn't happen because construction of opengl_renderer
3985     // objects is supposed to be impossible if OpenGL is not available.
3986 
3987     panic_impossible ();
3988 
3989 #endif
3990   }
3991 
3992   Matrix
get_viewport_scaled(void) const3993   opengl_renderer::get_viewport_scaled (void) const
3994   {
3995     Matrix retval (1, 4, 0.0);
3996 
3997 #if defined (HAVE_OPENGL)
3998 #if defined (HAVE_FRAMEWORK_OPENGL)
3999     GLint vp[4];
4000 #else
4001     int vp[4];
4002 #endif
4003 
4004     m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
4005 
4006     for (int i = 0; i < 4; i++)
4007       retval(i) = static_cast<double> (vp[i]) / m_devpixratio;
4008 
4009 #else
4010 
4011     // This shouldn't happen because construction of opengl_renderer
4012     // objects is supposed to be impossible if OpenGL is not available.
4013 
4014     panic_impossible ();
4015 
4016 #endif
4017 
4018     return retval;
4019   }
4020 
4021   void
set_color(const Matrix & c)4022   opengl_renderer::set_color (const Matrix& c)
4023   {
4024 #if defined (HAVE_OPENGL)
4025 
4026     m_glfcns.glColor3dv (c.data ());
4027 
4028     if (! c.isempty ())
4029       txt_renderer.set_color (c);
4030 
4031 #else
4032 
4033     octave_unused_parameter (c);
4034 
4035     // This shouldn't happen because construction of opengl_renderer
4036     // objects is supposed to be impossible if OpenGL is not available.
4037 
4038     panic_impossible ();
4039 
4040 #endif
4041   }
4042 
4043   void
set_font(const base_properties & props)4044   opengl_renderer::set_font (const base_properties& props)
4045   {
4046     bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on";
4047     txt_renderer.set_anti_aliasing (do_anti_alias);
4048     txt_renderer.set_font (props.get ("fontname").string_value (),
4049                            props.get ("fontweight").string_value (),
4050                            props.get ("fontangle").string_value (),
4051                            props.get ("__fontsize_points__").double_value ()
4052                            * m_devpixratio);
4053   }
4054 
4055   void
set_polygon_offset(bool on,float offset)4056   opengl_renderer::set_polygon_offset (bool on, float offset)
4057   {
4058 #if defined (HAVE_OPENGL)
4059 
4060     if (on)
4061       {
4062         m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL);
4063         m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE);
4064         m_glfcns.glPolygonOffset (offset, offset);
4065       }
4066     else
4067       {
4068         m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL);
4069         m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE);
4070       }
4071 
4072 #else
4073 
4074     octave_unused_parameter (on);
4075     octave_unused_parameter (offset);
4076 
4077     // This shouldn't happen because construction of opengl_renderer
4078     // objects is supposed to be impossible if OpenGL is not available.
4079 
4080     panic_impossible ();
4081 
4082 #endif
4083   }
4084 
4085   void
set_linewidth(float w)4086   opengl_renderer::set_linewidth (float w)
4087   {
4088 #if defined (HAVE_OPENGL)
4089     // Measure LineWidth in points.  See bug #53056.
4090     m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio);
4091 
4092 #else
4093 
4094     octave_unused_parameter (w);
4095 
4096     // This shouldn't happen because construction of opengl_renderer
4097     // objects is supposed to be impossible if OpenGL is not available.
4098 
4099     panic_impossible ();
4100 
4101 #endif
4102   }
4103 
4104   void
set_linestyle(const std::string & s,bool use_stipple,double linewidth)4105   opengl_renderer::set_linestyle (const std::string& s, bool use_stipple,
4106                                   double linewidth)
4107   {
4108 #if defined (HAVE_OPENGL)
4109     // Measure LineWidth in points.  See bug #53056.
4110     int factor = math::round (points_to_pixels (linewidth) * m_devpixratio);
4111     if (factor < 1)
4112       factor = 1;
4113 
4114     uint16_t pattern = 0xFFFF;
4115 
4116     bool solid = false;
4117 
4118     if (s == "-")
4119       solid = true;
4120     else if (s == ":")
4121       {
4122         if (factor > 1)
4123           pattern = 0x5555;
4124         else
4125           pattern = 0x1111;
4126       }
4127     else if (s == "--")
4128       {
4129         if (factor > 1)
4130           pattern = 0x0F0F;
4131         else
4132           pattern = 0x01FF;
4133       }
4134     else if (s == "-.")
4135       {
4136         if (factor > 1)
4137           pattern = 0x6F6F;
4138         else
4139           pattern = 0x18FF;
4140       }
4141     else
4142       pattern = 0x0000;
4143 
4144     m_glfcns.glLineStipple (factor, pattern);
4145 
4146     if (solid && ! use_stipple)
4147       m_glfcns.glDisable (GL_LINE_STIPPLE);
4148     else
4149       m_glfcns.glEnable (GL_LINE_STIPPLE);
4150 
4151 #else
4152 
4153     octave_unused_parameter (s);
4154     octave_unused_parameter (use_stipple);
4155     octave_unused_parameter (linewidth);
4156 
4157     // This shouldn't happen because construction of opengl_renderer
4158     // objects is supposed to be impossible if OpenGL is not available.
4159 
4160     panic_impossible ();
4161 
4162 #endif
4163   }
4164 
4165   void
set_clipbox(double x1,double x2,double y1,double y2,double z1,double z2)4166   opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2,
4167                                 double z1, double z2)
4168   {
4169 #if defined (HAVE_OPENGL)
4170 
4171     double dx = (x2-x1);
4172     double dy = (y2-y1);
4173     double dz = (z2-z1);
4174 
4175     x1 -= 0.001*dx; x2 += 0.001*dx;
4176     y1 -= 0.001*dy; y2 += 0.001*dy;
4177     z1 -= 0.001*dz; z2 += 0.001*dz;
4178 
4179     ColumnVector p (4, 0.0);
4180 
4181     p(0) = -1; p(3) = x2;
4182     m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ());
4183     p(0) = 1; p(3) = -x1;
4184     m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ());
4185     p(0) = 0; p(1) = -1; p(3) = y2;
4186     m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ());
4187     p(1) = 1; p(3) = -y1;
4188     m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ());
4189     p(1) = 0; p(2) = -1; p(3) = z2;
4190     m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ());
4191     p(2) = 1; p(3) = -z1;
4192     m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ());
4193 
4194     xmin = x1; xmax = x2;
4195     ymin = y1; ymax = y2;
4196     zmin = z1; zmax = z2;
4197 
4198 #else
4199 
4200     octave_unused_parameter (x1);
4201     octave_unused_parameter (x2);
4202     octave_unused_parameter (y1);
4203     octave_unused_parameter (y2);
4204     octave_unused_parameter (z1);
4205     octave_unused_parameter (z2);
4206 
4207     // This shouldn't happen because construction of opengl_renderer
4208     // objects is supposed to be impossible if OpenGL is not available.
4209 
4210     panic_impossible ();
4211 
4212 #endif
4213   }
4214 
4215   void
set_clipping(bool enable)4216   opengl_renderer::set_clipping (bool enable)
4217   {
4218 #if defined (HAVE_OPENGL)
4219 
4220     bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
4221 
4222     if (enable != has_clipping)
4223       {
4224         if (enable)
4225           for (int i = 0; i < 6; i++)
4226             m_glfcns.glEnable (GL_CLIP_PLANE0+i);
4227         else
4228           for (int i = 0; i < 6; i++)
4229             m_glfcns.glDisable (GL_CLIP_PLANE0+i);
4230       }
4231 
4232 #else
4233 
4234     octave_unused_parameter (enable);
4235 
4236     // This shouldn't happen because construction of opengl_renderer
4237     // objects is supposed to be impossible if OpenGL is not available.
4238 
4239     panic_impossible ();
4240 
4241 #endif
4242   }
4243 
4244   void
init_marker(const std::string & m,double size,float width)4245   opengl_renderer::init_marker (const std::string& m, double size, float width)
4246   {
4247 #if defined (HAVE_OPENGL)
4248     m_glfcns.glMatrixMode (GL_PROJECTION);
4249     m_glfcns.glPushMatrix ();
4250     m_glfcns.glLoadIdentity ();
4251 
4252     Matrix vp = get_viewport_scaled ();
4253     m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
4254     m_glfcns.glMatrixMode (GL_MODELVIEW);
4255     m_glfcns.glPushMatrix ();
4256 
4257     set_clipping (false);
4258     set_linewidth (width);
4259 
4260     marker_id = make_marker_list (m, size, false);
4261     filled_marker_id = make_marker_list (m, size, true);
4262 
4263 #else
4264 
4265     octave_unused_parameter (m);
4266     octave_unused_parameter (size);
4267     octave_unused_parameter (width);
4268 
4269     // This shouldn't happen because construction of opengl_renderer
4270     // objects is supposed to be impossible if OpenGL is not available.
4271 
4272     panic_impossible ();
4273 
4274 #endif
4275   }
4276 
4277   void
end_marker(void)4278   opengl_renderer::end_marker (void)
4279   {
4280 #if defined (HAVE_OPENGL)
4281 
4282     m_glfcns.glDeleteLists (marker_id, 1);
4283     m_glfcns.glDeleteLists (filled_marker_id, 1);
4284 
4285     m_glfcns.glMatrixMode (GL_MODELVIEW);
4286     m_glfcns.glPopMatrix ();
4287     m_glfcns.glMatrixMode (GL_PROJECTION);
4288     m_glfcns.glPopMatrix ();
4289     set_linewidth (0.5f);
4290 
4291 #else
4292 
4293     // This shouldn't happen because construction of opengl_renderer
4294     // objects is supposed to be impossible if OpenGL is not available.
4295 
4296     panic_impossible ();
4297 
4298 #endif
4299   }
4300 
4301   void
draw_marker(double x,double y,double z,const Matrix & lc,const Matrix & fc)4302   opengl_renderer::draw_marker (double x, double y, double z,
4303                                 const Matrix& lc, const Matrix& fc)
4304   {
4305 #if defined (HAVE_OPENGL)
4306 
4307     ColumnVector tmp = xform.transform (x, y, z, false);
4308 
4309     m_glfcns.glLoadIdentity ();
4310     m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2));
4311 
4312     if (filled_marker_id > 0 && fc.numel () > 0)
4313       {
4314         m_glfcns.glColor3dv (fc.data ());
4315         set_polygon_offset (true, -1.0);
4316         m_glfcns.glCallList (filled_marker_id);
4317         if (lc.numel () > 0)
4318           {
4319             m_glfcns.glColor3dv (lc.data ());
4320             m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
4321             m_glfcns.glEdgeFlag (GL_TRUE);
4322             set_polygon_offset (true, -2.0);
4323             m_glfcns.glCallList (filled_marker_id);
4324             m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
4325           }
4326         set_polygon_offset (false);
4327       }
4328     else if (marker_id > 0 && lc.numel () > 0)
4329       {
4330         m_glfcns.glColor3dv (lc.data ());
4331         m_glfcns.glCallList (marker_id);
4332       }
4333 
4334 #else
4335 
4336     octave_unused_parameter (x);
4337     octave_unused_parameter (y);
4338     octave_unused_parameter (z);
4339     octave_unused_parameter (lc);
4340     octave_unused_parameter (fc);
4341 
4342     // This shouldn't happen because construction of opengl_renderer
4343     // objects is supposed to be impossible if OpenGL is not available.
4344 
4345     panic_impossible ();
4346 
4347 #endif
4348   }
4349 
4350   void
init_maxlights(void)4351   opengl_renderer::init_maxlights (void)
4352   {
4353 #if defined (HAVE_OPENGL)
4354 
4355     // Check actual maximum number of lights possible
4356     if (m_max_lights == 0)
4357       {
4358         GLint max_lights;
4359         m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights);
4360         m_max_lights = max_lights;
4361       }
4362 
4363 #else
4364 
4365     // This shouldn't happen because construction of opengl_renderer
4366     // objects is supposed to be impossible if OpenGL is not available.
4367 
4368     panic_impossible ();
4369 
4370 #endif
4371   }
4372 
4373   std::string
get_string(unsigned int id) const4374   opengl_renderer::get_string (unsigned int id) const
4375   {
4376 #if defined (HAVE_OPENGL)
4377 
4378     // This is kind of ugly, but glGetString returns a pointer to GLubyte
4379     // and there is no std::string constructor that matches.  Is there a
4380     // better way?
4381 
4382     std::ostringstream buf;
4383 
4384     buf << m_glfcns.glGetString (static_cast<GLenum> (id));
4385 
4386     return std::string (buf.str ());
4387 
4388 #else
4389 
4390     octave_unused_parameter (id);
4391 
4392     // This shouldn't happen because construction of opengl_renderer
4393     // objects is supposed to be impossible if OpenGL is not available.
4394 
4395     panic_impossible ();
4396     return std::string ();
4397 
4398 #endif
4399   }
4400 
4401   void
set_normal(int bfl_mode,const NDArray & n,int j,int i)4402   opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i)
4403   {
4404 #if defined (HAVE_OPENGL)
4405 
4406     double x = n(j,i,0);
4407     double y = n(j,i,1);
4408     double z = n(j,i,2);
4409 
4410     double d = sqrt (x*x + y*y + z*z);
4411 
4412     double dir = 1.0;
4413 
4414     if (bfl_mode > 0)
4415       dir = ((x * view_vector(0) + y * view_vector(1) + z * view_vector(2) < 0)
4416              ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
4417 
4418     m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d);
4419 
4420 #else
4421 
4422     octave_unused_parameter (bfl_mode);
4423     octave_unused_parameter (n);
4424     octave_unused_parameter (j);
4425     octave_unused_parameter (i);
4426 
4427     // This shouldn't happen because construction of opengl_renderer
4428     // objects is supposed to be impossible if OpenGL is not available.
4429 
4430     panic_impossible ();
4431 
4432 #endif
4433   }
4434 
4435   double
points_to_pixels(const double val) const4436   opengl_renderer::points_to_pixels (const double val) const
4437   {
4438     gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::points_to_pixels");
4439 
4440     // FIXME: Does making this static cause problems if figure is moved to a
4441     //        2nd monitor with a different value for "screenpixelsperinch"?
4442     static const double pix_per_pts =
4443       gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0;
4444 
4445     double retval = val;
4446 
4447     if (! m_printing)
4448       retval *= pix_per_pts;
4449 
4450     return retval;
4451   }
4452 
4453   unsigned int
make_marker_list(const std::string & marker,double size,bool filled) const4454   opengl_renderer::make_marker_list (const std::string& marker, double size,
4455                                      bool filled) const
4456   {
4457 #if defined (HAVE_OPENGL)
4458 
4459     char c = marker[0];
4460 
4461     if (filled && (c == '+' || c == 'x' || c == '*' || c == '.'))
4462       return 0;
4463 
4464     unsigned int ID = m_glfcns.glGenLists (1);
4465 
4466     // FIXME: See bug #53056 (measure LineWidth in points).
4467     double sz = points_to_pixels (size);
4468 
4469     // constants for the * marker
4470     const double sqrt2d4 = 0.35355339059327;
4471     double tt = sz*sqrt2d4;
4472 
4473     m_glfcns.glNewList (ID, GL_COMPILE);
4474 
4475     switch (marker[0])
4476       {
4477       case '+':
4478         m_glfcns.glBegin (GL_LINES);
4479         m_glfcns.glVertex2d (-sz/2, 0);
4480         m_glfcns.glVertex2d (sz/2, 0);
4481         m_glfcns.glVertex2d (0, -sz/2);
4482         m_glfcns.glVertex2d (0, sz/2);
4483         m_glfcns.glEnd ();
4484         break;
4485       case 'x':
4486         m_glfcns.glBegin (GL_LINES);
4487         m_glfcns.glVertex2d (-sz/2, -sz/2);
4488         m_glfcns.glVertex2d (sz/2, sz/2);
4489         m_glfcns.glVertex2d (-sz/2, sz/2);
4490         m_glfcns.glVertex2d (sz/2, -sz/2);
4491         m_glfcns.glEnd ();
4492         break;
4493       case '*':
4494         m_glfcns.glBegin (GL_LINES);
4495         m_glfcns.glVertex2d (-sz/2, 0);
4496         m_glfcns.glVertex2d (sz/2, 0);
4497         m_glfcns.glVertex2d (0, -sz/2);
4498         m_glfcns.glVertex2d (0, sz/2);
4499         m_glfcns.glVertex2d (-tt, -tt);
4500         m_glfcns.glVertex2d (+tt, +tt);
4501         m_glfcns.glVertex2d (-tt, +tt);
4502         m_glfcns.glVertex2d (+tt, -tt);
4503         m_glfcns.glEnd ();
4504         break;
4505       case '.':
4506         {
4507           // The dot marker is special and is drawn at 1/3rd the specified size
4508 
4509           // Ensure that something is drawn even at very small markersizes
4510           if (sz > 0 && sz < 3)
4511             sz = 3;
4512 
4513           int div = static_cast<int> (M_PI * sz / 12);
4514           if (! (div % 2))
4515             div += 1;               // ensure odd number for left/right symmetry
4516           div = std::max (div, 3);  // ensure at least a few vertices are drawn
4517           double ang_step = M_PI / div;
4518 
4519           m_glfcns.glBegin (GL_POLYGON);
4520           for (double ang = 0; ang < 2*M_PI; ang += ang_step)
4521             m_glfcns.glVertex2d (sz/6*cos (ang), sz/6*sin (ang));
4522           m_glfcns.glEnd ();
4523         }
4524         break;
4525       case 's':
4526         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4527         m_glfcns.glVertex2d (-sz/2, -sz/2);
4528         m_glfcns.glVertex2d (-sz/2, sz/2);
4529         m_glfcns.glVertex2d (sz/2, sz/2);
4530         m_glfcns.glVertex2d (sz/2, -sz/2);
4531         m_glfcns.glEnd ();
4532         break;
4533       case 'o':
4534         {
4535           int div = static_cast<int> (M_PI * sz / 4);
4536           if (! (div % 2))
4537             div += 1;               // ensure odd number for left/right symmetry
4538           div = std::max (div, 5);  // ensure at least a few vertices are drawn
4539           double ang_step = M_PI / div;
4540 
4541           m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4542           for (double ang = 0; ang < 2*M_PI; ang += ang_step)
4543             m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang));
4544           m_glfcns.glEnd ();
4545         }
4546         break;
4547       case 'd':
4548         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4549         m_glfcns.glVertex2d (0, -sz/2);
4550         m_glfcns.glVertex2d (sz/2, 0);
4551         m_glfcns.glVertex2d (0, sz/2);
4552         m_glfcns.glVertex2d (-sz/2, 0);
4553         m_glfcns.glEnd ();
4554         break;
4555       case 'v':
4556         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4557         m_glfcns.glVertex2d (0, sz/2);
4558         m_glfcns.glVertex2d (sz/2, -sz/2);
4559         m_glfcns.glVertex2d (-sz/2, -sz/2);
4560         m_glfcns.glEnd ();
4561         break;
4562       case '^':
4563         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4564         m_glfcns.glVertex2d (0, -sz/2);
4565         m_glfcns.glVertex2d (-sz/2, sz/2);
4566         m_glfcns.glVertex2d (sz/2, sz/2);
4567         m_glfcns.glEnd ();
4568         break;
4569       case '>':
4570         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4571         m_glfcns.glVertex2d (sz/2, 0);
4572         m_glfcns.glVertex2d (-sz/2, sz/2);
4573         m_glfcns.glVertex2d (-sz/2, -sz/2);
4574         m_glfcns.glEnd ();
4575         break;
4576       case '<':
4577         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4578         m_glfcns.glVertex2d (-sz/2, 0);
4579         m_glfcns.glVertex2d (sz/2, -sz/2);
4580         m_glfcns.glVertex2d (sz/2, sz/2);
4581         m_glfcns.glEnd ();
4582         break;
4583       case 'p':
4584         {
4585           double ang, r, dr;
4586           dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02;
4587 
4588           m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4589           for (int i = 0; i < 2*5; i++)
4590             {
4591               ang = (-0.5 + double (i+1) / 5) * M_PI;
4592               r = 1.0 - (dr * fmod (double (i+1), 2.0));
4593               m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
4594             }
4595           m_glfcns.glEnd ();
4596         }
4597         break;
4598       case 'h':
4599         {
4600           double ang, r, dr;
4601           dr = 1.0 - 0.5/sin (M_PI/3)*1.02;
4602 
4603           m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
4604           for (int i = 0; i < 2*6; i++)
4605             {
4606               ang = (0.5 + double (i+1) / 6.0) * M_PI;
4607               r = 1.0 - (dr * fmod (double (i+1), 2.0));
4608               m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
4609             }
4610           m_glfcns.glEnd ();
4611         }
4612         break;
4613       default:
4614         warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ());
4615         break;
4616       }
4617 
4618     m_glfcns.glEndList ();
4619 
4620     return ID;
4621 
4622 #else
4623 
4624     octave_unused_parameter (marker);
4625     octave_unused_parameter (size);
4626     octave_unused_parameter (filled);
4627 
4628     // This shouldn't happen because construction of opengl_renderer
4629     // objects is supposed to be impossible if OpenGL is not available.
4630 
4631     panic_impossible ();
4632 
4633 #endif
4634   }
4635 
4636   void
text_to_pixels(const std::string & txt,uint8NDArray & pixels,Matrix & bbox,int halign,int valign,double rotation)4637   opengl_renderer::text_to_pixels (const std::string& txt,
4638                                    uint8NDArray& pixels,
4639                                    Matrix& bbox,
4640                                    int halign, int valign, double rotation)
4641   {
4642     txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign,
4643                                  rotation, interpreter);
4644   }
4645 
4646   void
text_to_strlist(const std::string & txt,std::list<text_renderer::string> & lst,Matrix & bbox,int halign,int valign,double rotation)4647   opengl_renderer::text_to_strlist (const std::string& txt,
4648                                     std::list<text_renderer::string>& lst,
4649                                     Matrix& bbox,
4650                                     int halign, int valign, double rotation)
4651   {
4652     txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign,
4653                                   rotation, interpreter);
4654   }
4655 
4656   Matrix
render_text(const std::string & txt,double x,double y,double z,int halign,int valign,double rotation)4657   opengl_renderer::render_text (const std::string& txt,
4658                                 double x, double y, double z,
4659                                 int halign, int valign, double rotation)
4660   {
4661 #if defined (HAVE_OPENGL)
4662 
4663     Matrix bbox (1, 4, 0.0);
4664 
4665     if (txt.empty ())
4666       return bbox;
4667 
4668     if (txt_renderer.ok ())
4669       {
4670         uint8NDArray pixels;
4671         text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
4672 
4673         bool blend = m_glfcns.glIsEnabled (GL_BLEND);
4674 
4675         m_glfcns.glEnable (GL_BLEND);
4676         m_glfcns.glEnable (GL_ALPHA_TEST);
4677         m_glfcns.glRasterPos3d (x, y, z);
4678         m_glfcns.glBitmap(0, 0, 0, 0, bbox(0), bbox(1), nullptr);
4679         m_glfcns.glDrawPixels (bbox(2), bbox(3),
4680                                GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
4681         m_glfcns.glDisable (GL_ALPHA_TEST);
4682 
4683         if (! blend)
4684           m_glfcns.glDisable (GL_BLEND);
4685       }
4686 
4687     return bbox;
4688 
4689 #else
4690 
4691     octave_unused_parameter (txt);
4692     octave_unused_parameter (x);
4693     octave_unused_parameter (y);
4694     octave_unused_parameter (z);
4695     octave_unused_parameter (halign);
4696     octave_unused_parameter (valign);
4697     octave_unused_parameter (rotation);
4698 
4699     // This shouldn't happen because construction of opengl_renderer
4700     // objects is supposed to be impossible if OpenGL is not available.
4701 
4702     panic_impossible ();
4703 
4704 #endif
4705   }
4706 }
4707