GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
gl-render.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2008-2013 Michael Goffioul
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #if defined (HAVE_OPENGL)
28 
29 #include <iostream>
30 
31 #include <lo-mappers.h>
32 #include "oct-locbuf.h"
33 #include "oct-refcount.h"
34 #include "gl-render.h"
35 #include "txt-eng.h"
36 #include "txt-eng-ft.h"
37 
38 #define LIGHT_MODE GL_FRONT_AND_BACK
39 
40 // Win32 API requires the CALLBACK attributes for
41 // GLU callback functions. Define it to empty on
42 // other platforms.
43 #ifndef CALLBACK
44 #define CALLBACK
45 #endif
46 
47 class
48 opengl_texture
49 {
50 protected:
51  class texture_rep
52  {
53  public:
54  texture_rep (void)
55  : id (), w (), h (), tw (), th (), tx (), ty (),
56  valid (false), count (1)
57  { }
58 
59  texture_rep (GLuint id_arg, int w_arg, int h_arg, int tw_arg, int th_arg)
60  : id (id_arg), w (w_arg), h (h_arg), tw (tw_arg), th (th_arg),
61  tx (double(w)/tw), ty (double(h)/th), valid (true),
62  count (1) { }
63 
64  ~texture_rep (void)
65  {
66  if (valid)
67  glDeleteTextures (1, &id);
68  }
69 
70  void bind (int mode) const
71  { if (valid) glBindTexture (mode, id); }
72 
73  void tex_coord (double q, double r) const
74  { if (valid) glTexCoord2d (q*tx, r*ty); }
75 
76  GLuint id;
77  int w, h;
78  int tw, th;
79  double tx, ty;
80  bool valid;
82  };
83 
84  texture_rep *rep;
85 
86 private:
87  opengl_texture (texture_rep *_rep) : rep (_rep) { }
88 
89 public:
90  opengl_texture (void) : rep (new texture_rep ()) { }
91 
92  opengl_texture (const opengl_texture& tx)
93  : rep (tx.rep)
94  {
95  rep->count++;
96  }
97 
98  ~opengl_texture (void)
99  {
100  if (--rep->count == 0)
101  delete rep;
102  }
103 
104  opengl_texture& operator = (const opengl_texture& tx)
105  {
106  if (--rep->count == 0)
107  delete rep;
108 
109  rep = tx.rep;
110  rep->count++;
111 
112  return *this;
113  }
114 
115  static opengl_texture create (const octave_value& data);
116 
117  void bind (int mode = GL_TEXTURE_2D) const
118  { rep->bind (mode); }
119 
120  void tex_coord (double q, double r) const
121  { rep->tex_coord (q, r); }
122 
123  bool is_valid (void) const
124  { return rep->valid; }
125 };
126 
127 static int
128 next_power_of_2 (int n)
129 {
130  int m = 1;
131 
132  while (m < n && m < std::numeric_limits<int>::max ())
133  m <<= 1;
134 
135  return m;
136 }
137 
138 opengl_texture
139 opengl_texture::create (const octave_value& data)
140 {
141  opengl_texture retval;
142 
143  dim_vector dv (data.dims ());
144 
145  // Expect RGB data
146  if (dv.length () == 3 && dv(2) == 3)
147  {
148  // FIXME: dim_vectors hold octave_idx_type values.
149  // Should we check for dimensions larger than intmax?
150  int h = dv(0), w = dv(1), tw, th;
151  GLuint id;
152  bool ok = true;
153 
154  tw = next_power_of_2 (w);
155  th = next_power_of_2 (w);
156 
157  glGenTextures (1, &id);
158  glBindTexture (GL_TEXTURE_2D, id);
159 
160  if (data.is_double_type ())
161  {
162  const NDArray xdata = data.array_value ();
163 
164  OCTAVE_LOCAL_BUFFER (float, a, (3*tw*th));
165 
166  for (int i = 0; i < h; i++)
167  {
168  for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
169  {
170  a[idx] = xdata(i,j,0);
171  a[idx+1] = xdata(i,j,1);
172  a[idx+2] = xdata(i,j,2);
173  }
174  }
175 
176  glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a);
177  }
178  else if (data.is_uint8_type ())
179  {
180  const uint8NDArray xdata = data.uint8_array_value ();
181 
182  OCTAVE_LOCAL_BUFFER (octave_uint8, a, (3*tw*th));
183 
184  for (int i = 0; i < h; i++)
185  {
186  for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
187  {
188  a[idx] = xdata(i,j,0);
189  a[idx+1] = xdata(i,j,1);
190  a[idx+2] = xdata(i,j,2);
191  }
192  }
193 
194  glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
195  GL_RGB, GL_UNSIGNED_BYTE, a);
196  }
197  else
198  {
199  ok = false;
200  warning ("opengl_texture::create: invalid texture data type (expected double or uint8)");
201  }
202 
203  if (ok)
204  {
205  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
206  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
207 
208  if (glGetError () != GL_NO_ERROR)
209  warning ("opengl_texture::create: OpenGL error while generating texture data");
210  else
211  retval = opengl_texture (new texture_rep (id, w, h, tw, th));
212  }
213  }
214  else
215  warning ("opengl_texture::create: invalid texture data size");
216 
217  return retval;
218 }
219 
220 class
221 opengl_tesselator
222 {
223 public:
224 #if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS)
225  typedef GLvoid (CALLBACK *fcn) (...);
226 #else
227  typedef void (CALLBACK *fcn) (void);
228 #endif
229 
230 public:
231 
232  opengl_tesselator (void) : glu_tess (0), fill () { init (); }
233 
234  virtual ~opengl_tesselator (void)
235  { if (glu_tess) gluDeleteTess (glu_tess); }
236 
237  void begin_polygon (bool filled = true)
238  {
239  gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY,
240  (filled ? GL_FALSE : GL_TRUE));
241  fill = filled;
242  gluTessBeginPolygon (glu_tess, this);
243  }
244 
245  void end_polygon (void) const
246  { gluTessEndPolygon (glu_tess); }
247 
248  void begin_contour (void) const
249  { gluTessBeginContour (glu_tess); }
250 
251  void end_contour (void) const
252  { gluTessEndContour (glu_tess); }
253 
254  void add_vertex (double *loc, void *data) const
255  { gluTessVertex (glu_tess, loc, data); }
256 
257 protected:
258  virtual void begin (GLenum /*type*/) { }
259 
260  virtual void end (void) { }
261 
262  virtual void vertex (void * /*data*/) { }
263 
264  virtual void combine (GLdouble [3] /*c*/, void * [4] /*data*/,
265  GLfloat [4] /*w*/, void ** /*out_data*/) { }
266 
267  virtual void edge_flag (GLboolean /*flag*/) { }
268 
269  virtual void error (GLenum err)
270  { ::error ("OpenGL tesselation error (%d)", err); }
271 
272  virtual void init (void)
273  {
274  glu_tess = gluNewTess ();
275 
276  gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA,
277  reinterpret_cast<fcn> (tess_begin));
278  gluTessCallback (glu_tess, GLU_TESS_END_DATA,
279  reinterpret_cast<fcn> (tess_end));
280  gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA,
281  reinterpret_cast<fcn> (tess_vertex));
282  gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA,
283  reinterpret_cast<fcn> (tess_combine));
284  gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA,
285  reinterpret_cast<fcn> (tess_edge_flag));
286  gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA,
287  reinterpret_cast<fcn> (tess_error));
288  }
289 
290  bool is_filled (void) const { return fill; }
291 
292 private:
293  static void CALLBACK tess_begin (GLenum type, void *t)
294  { reinterpret_cast<opengl_tesselator *> (t)->begin (type); }
295 
296  static void CALLBACK tess_end (void *t)
297  { reinterpret_cast<opengl_tesselator *> (t)->end (); }
298 
299  static void CALLBACK tess_vertex (void *v, void *t)
300  { reinterpret_cast<opengl_tesselator *> (t)->vertex (v); }
301 
302  static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4],
303  void **out, void *t)
304  { reinterpret_cast<opengl_tesselator *> (t)->combine (c, v, w, out); }
305 
306  static void CALLBACK tess_edge_flag (GLboolean flag, void *t)
307  { reinterpret_cast<opengl_tesselator *> (t)->edge_flag (flag); }
308 
309  static void CALLBACK tess_error (GLenum err, void *t)
310  { reinterpret_cast<opengl_tesselator *> (t)->error (err); }
311 
312 private:
313 
314  // No copying!
315 
316  opengl_tesselator (const opengl_tesselator&);
317 
318  opengl_tesselator operator = (const opengl_tesselator&);
319 
320  GLUtesselator *glu_tess;
321  bool fill;
322 };
323 
324 class
325 vertex_data
326 {
327 public:
328  class vertex_data_rep
329  {
330  public:
331  Matrix coords;
332  Matrix color;
333  Matrix normal;
334  double alpha;
335  float ambient;
336  float diffuse;
337  float specular;
338  float specular_exp;
339 
340  // reference counter
341  octave_refcount<int> count;
342 
343  vertex_data_rep (void)
344  : coords (), color (), normal (), alpha (),
345  ambient (), diffuse (), specular (), specular_exp (),count (1) { }
346 
347  vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& n,
348  double a, float as, float ds, float ss, float se)
349  : coords (c), color (col), normal (n), alpha (a),
350  ambient (as), diffuse (ds), specular (ss), specular_exp (se),
351  count (1) { }
352  };
353 
354 private:
355  vertex_data_rep *rep;
356 
357  vertex_data_rep *nil_rep (void) const
358  {
359  static vertex_data_rep *nr = new vertex_data_rep ();
360 
361  return nr;
362  }
363 
364 public:
365  vertex_data (void) : rep (nil_rep ())
366  { rep->count++; }
367 
368  vertex_data (const vertex_data& v) : rep (v.rep)
369  { rep->count++; }
370 
371  vertex_data (const Matrix& c, const Matrix& col, const Matrix& n,
372  double a, float as, float ds, float ss, float se)
373  : rep (new vertex_data_rep (c, col, n, a, as, ds, ss, se))
374  { }
375 
376  vertex_data (vertex_data_rep *new_rep)
377  : rep (new_rep) { }
378 
379  ~vertex_data (void)
380  {
381  if (--rep->count == 0)
382  delete rep;
383  }
384 
385  vertex_data& operator = (const vertex_data& v)
386  {
387  if (--rep->count == 0)
388  delete rep;
389 
390  rep = v.rep;
391  rep->count++;
392 
393  return *this;
394  }
395 
396  vertex_data_rep *get_rep (void) const { return rep; }
397 };
398 
399 class
400 opengl_renderer::patch_tesselator : public opengl_tesselator
401 {
402 public:
403  patch_tesselator (opengl_renderer *r, int cmode, int lmode, int idx = 0)
404  : opengl_tesselator (), renderer (r),
405  color_mode (cmode), light_mode (lmode), index (idx),
406  first (true), tmp_vdata ()
407  { }
408 
409 protected:
410  void begin (GLenum type)
411  {
412  //printf ("patch_tesselator::begin (%d)\n", type);
413  first = true;
414 
415  if (color_mode == 2 || light_mode == 2)
416  glShadeModel (GL_SMOOTH);
417  else
418  glShadeModel (GL_FLAT);
419 
420  if (is_filled ())
421  renderer->set_polygon_offset (true, 1+index);
422 
423  glBegin (type);
424  }
425 
426  void end (void)
427  {
428  //printf ("patch_tesselator::end\n");
429  glEnd ();
430  renderer->set_polygon_offset (false);
431  }
432 
433  void vertex (void *data)
434  {
435  vertex_data::vertex_data_rep *v
436  = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
437  //printf ("patch_tesselator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->coords(2));
438 
439  // FIXME: why did I need to keep the first vertex of the face
440  // in JHandles? I think it's related to the fact that the
441  // tessellation process might re-order the vertices, such that
442  // the first one you get here might not be the first one of the face;
443  // but I can't figure out the actual reason.
444  if (color_mode > 0 && (first || color_mode == 2))
445  {
446  Matrix col = v->color;
447 
448  if (col.numel () == 3)
449  {
450  glColor3dv (col.data ());
451  if (light_mode > 0)
452  {
453  float buf[4] = { 0, 0, 0, 1 };
454 
455  for (int k = 0; k < 3; k++)
456  buf[k] = (v->ambient * col(k));
457  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
458 
459  for (int k = 0; k < 3; k++)
460  buf[k] = (v->diffuse * col(k));
461  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
462  }
463  }
464  }
465 
466  if (light_mode > 0 && (first || light_mode == 2))
467  glNormal3dv (v->normal.data ());
468 
469  glVertex3dv (v->coords.data ());
470 
471  first = false;
472  }
473 
474  void combine (GLdouble xyz[3], void *data[4], GLfloat w[4],
475  void **out_data)
476  {
477  //printf ("patch_tesselator::combine\n");
478 
479  vertex_data::vertex_data_rep *v[4];
480  int vmax = 4;
481 
482  for (int i = 0; i < 4; i++)
483  {
484  v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]);
485 
486  if (vmax == 4 && ! v[i])
487  vmax = i;
488  }
489 
490  Matrix vv (1, 3, 0.0);
491  Matrix cc;
492  Matrix nn (1, 3, 0.0);
493  double aa = 0.0;
494 
495  vv(0) = xyz[0];
496  vv(1) = xyz[1];
497  vv(2) = xyz[2];
498 
499  if (v[0]->color.numel ())
500  {
501  cc.resize (1, 3, 0.0);
502  for (int ic = 0; ic < 3; ic++)
503  for (int iv = 0; iv < vmax; iv++)
504  cc(ic) += (w[iv] * v[iv]->color (ic));
505  }
506 
507  if (v[0]->normal.numel () > 0)
508  {
509  for (int in = 0; in < 3; in++)
510  for (int iv = 0; iv < vmax; iv++)
511  nn(in) += (w[iv] * v[iv]->normal (in));
512  }
513 
514  for (int iv = 0; iv < vmax; iv++)
515  aa += (w[iv] * v[iv]->alpha);
516 
517  vertex_data new_v (vv, cc, nn, aa, v[0]->ambient, v[0]->diffuse,
518  v[0]->specular, v[0]->specular_exp);
519  tmp_vdata.push_back (new_v);
520 
521  *out_data = new_v.get_rep ();
522  }
523 
524 private:
525 
526  // No copying!
527 
528  patch_tesselator (const patch_tesselator&);
529 
530  patch_tesselator& operator = (const patch_tesselator&);
531 
532  opengl_renderer *renderer;
533  int color_mode; // 0: uni, 1: flat, 2: interp
534  int light_mode; // 0: none, 1: flat, 2: gouraud
535  int index;
536  bool first;
537  std::list<vertex_data> tmp_vdata;
538 };
539 
540 void
541 opengl_renderer::draw (const graphics_object& go, bool toplevel)
542 {
543  if (! go.valid_object ())
544  return;
545 
546  const base_properties& props = go.get_properties ();
547 
548  if (! toolkit)
549  toolkit = props.get_toolkit ();
550 
551  if (go.isa ("figure"))
552  draw_figure (dynamic_cast<const figure::properties&> (props));
553  else if (go.isa ("axes"))
554  draw_axes (dynamic_cast<const axes::properties&> (props));
555  else if (go.isa ("line"))
556  draw_line (dynamic_cast<const line::properties&> (props));
557  else if (go.isa ("surface"))
558  draw_surface (dynamic_cast<const surface::properties&> (props));
559  else if (go.isa ("patch"))
560  draw_patch (dynamic_cast<const patch::properties&> (props));
561  else if (go.isa ("hggroup"))
562  draw_hggroup (dynamic_cast<const hggroup::properties&> (props));
563  else if (go.isa ("text"))
564  draw_text (dynamic_cast<const text::properties&> (props));
565  else if (go.isa ("image"))
566  draw_image (dynamic_cast<const image::properties&> (props));
567  else if (go.isa ("uimenu") || go.isa ("uicontrol")
568  || go.isa ("uicontextmenu") || go.isa ("uitoolbar")
569  || go.isa ("uipushtool") || go.isa ("uitoggletool"))
570  /* SKIP */;
571  else if (go.isa ("uipanel"))
572  {
573  if (toplevel)
574  draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go);
575  }
576  else
577  {
578  warning ("opengl_renderer: cannot render object of type '%s'",
579  props.graphics_object_name ().c_str ());
580  }
581 }
582 
583 void
585 {
586  // Initialize OpenGL context
587 
588  init_gl_context (props.is___enhanced__ (), props.get_color_rgb ());
589 
590  // Draw children
591 
592  draw (props.get_all_children (), false);
593 }
594 
595 void
597  const graphics_object& go)
598 {
599  graphics_object fig = go.get_ancestor ("figure");
600  const figure::properties& figProps =
601  dynamic_cast<const figure::properties&> (fig.get_properties ());
602 
603  // Initialize OpenGL context
604 
605  init_gl_context (figProps.is___enhanced__ (),
606  props.get_backgroundcolor_rgb ());
607 
608  // Draw children
609 
610  draw (props.get_all_children (), false);
611 }
612 
613 void
614 opengl_renderer::init_gl_context (bool enhanced, const Matrix& c)
615 {
616  // Initialize OpenGL context
617 
618  glEnable (GL_DEPTH_TEST);
619  glDepthFunc (GL_LEQUAL);
620  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
621  glAlphaFunc (GL_GREATER, 0.0f);
622  glEnable (GL_NORMALIZE);
623 
624  if (enhanced)
625  {
626  glEnable (GL_BLEND);
627  glEnable (GL_LINE_SMOOTH);
628  }
629  else
630  {
631  glDisable (GL_BLEND);
632  glDisable (GL_LINE_SMOOTH);
633  }
634 
635  // Clear background
636 
637  if (c.length () >= 3)
638  {
639  glClearColor (c(0), c(1), c(2), 1);
640  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
641  }
642 }
643 
644 void
645 opengl_renderer::render_grid (const std::string& gridstyle,
646  const Matrix& ticks, double lim1, double lim2,
647  double p1, double p1N, double p2, double p2N,
648  int xyz, bool is_3D)
649 {
650  set_linestyle (gridstyle, true);
651  glBegin (GL_LINES);
652  for (int i = 0; i < ticks.numel (); i++)
653  {
654  double val = ticks(i);
655  if (lim1 <= val && val <= lim2)
656  {
657  if (xyz == 0) // X
658  {
659  glVertex3d (val, p1N, p2);
660  glVertex3d (val, p1, p2);
661  if (is_3D)
662  {
663  glVertex3d (val, p1, p2N);
664  glVertex3d (val, p1, p2);
665  }
666  }
667  else if (xyz == 1) // Y
668  {
669  glVertex3d (p1N, val, p2);
670  glVertex3d (p1, val, p2);
671  if (is_3D)
672  {
673  glVertex3d (p1, val, p2N);
674  glVertex3d (p1, val, p2);
675  }
676  }
677  else if (xyz == 2) // Z
678  {
679  glVertex3d (p1N, p2, val);
680  glVertex3d (p1, p2, val);
681  glVertex3d (p1, p2N, val);
682  glVertex3d (p1, p2, val);
683  }
684  }
685  }
686  glEnd ();
687  set_linestyle ("-", true);
688 }
689 
690 void
692  double lim1, double lim2,
693  double p1, double p1N,
694  double p2, double p2N,
695  double dx, double dy, double dz,
696  int xyz, bool mirror)
697 {
698  glBegin (GL_LINES);
699 
700  for (int i = 0; i < ticks.numel (); i++)
701  {
702  double val = ticks(i);
703 
704  if (lim1 <= val && val <= lim2)
705  {
706  if (xyz == 0) // X
707  {
708  glVertex3d (val, p1, p2);
709  glVertex3d (val, p1+dy, p2+dz);
710  if (mirror)
711  {
712  glVertex3d (val, p1N, p2N);
713  glVertex3d (val, p1N-dy, p2N-dz);
714  }
715  }
716  else if (xyz == 1) // Y
717  {
718  glVertex3d (p1, val, p2);
719  glVertex3d (p1+dx, val, p2+dz);
720  if (mirror)
721  {
722  glVertex3d (p1N, val, p2N);
723  glVertex3d (p1N-dx, val, p2N-dz);
724  }
725  }
726  else if (xyz == 2) // Z
727  {
728  glVertex3d (p1, p2, val);
729  glVertex3d (p1+dx, p2+dy, val);
730  if (mirror)
731  {
732  glVertex3d (p1N, p2N, val);
733  glVertex3d (p1N-dx, p2N-dy, val);
734  }
735  }
736  }
737  }
738 
739  glEnd ();
740 }
741 
742 void
744  const string_vector& ticklabels,
745  double lim1, double lim2,
746  double p1, double p2,
747  int xyz, int ha, int va,
748  int& wmax, int& hmax)
749 {
750  int nticks = ticks.numel ();
751  int nlabels = ticklabels.numel ();
752 
753  if (nlabels == 0)
754  return;
755 
756  for (int i = 0; i < nticks; i++)
757  {
758  double val = ticks(i);
759 
760  if (lim1 <= val && val <= lim2)
761  {
762  Matrix b;
763 
764  std::string label (ticklabels(i % nlabels));
765  label.erase (0, label.find_first_not_of (" "));
766  label = label.substr (0, label.find_last_not_of (" ")+1);
767 
768  // FIXME: as tick text is transparent, shouldn't it be
769  // drawn after axes object, for correct rendering?
770  if (xyz == 0) // X
771  {
772  b = render_text (label, val, p1, p2, ha, va);
773  }
774  else if (xyz == 1) // Y
775  {
776  b = render_text (label, p1, val, p2, ha, va);
777  }
778  else if (xyz == 2) // Z
779  {
780  b = render_text (label, p1, p2, val, ha, va);
781  }
782 
783  wmax = std::max (wmax, static_cast<int> (b(2)));
784  hmax = std::max (hmax, static_cast<int> (b(3)));
785  }
786  }
787 }
788 
789 void
791 {
792  // setup OpenGL transformation
793 
794  Matrix x_zlim = props.get_transform_zlim ();
795 
796  xZ1 = x_zlim(0)-(x_zlim(1)-x_zlim(0))/2;
797  xZ2 = x_zlim(1)+(x_zlim(1)-x_zlim(0))/2;
798 
799  Matrix x_mat1 = props.get_opengl_matrix_1 ();
800  Matrix x_mat2 = props.get_opengl_matrix_2 ();
801 
802 #if defined (HAVE_FRAMEWORK_OPENGL)
803  GLint vw[4];
804 #else
805  int vw[4];
806 #endif
807 
808  glGetIntegerv (GL_VIEWPORT, vw);
809 
810  glMatrixMode (GL_MODELVIEW);
811  glLoadIdentity ();
812  glScaled (1, 1, -1);
813  glMultMatrixd (x_mat1.data ());
814  glMatrixMode (GL_PROJECTION);
815  glLoadIdentity ();
816  glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
817  glMultMatrixd (x_mat2.data ());
818  glMatrixMode (GL_MODELVIEW);
819 
820  glClear (GL_DEPTH_BUFFER_BIT);
821 
822  glDisable (GL_LINE_SMOOTH);
823 
824  // store axes transformation data
825 
826  xform = props.get_transform ();
827 }
828 
829 void
831 {
832  double xPlane = props.get_xPlane ();
833  double yPlane = props.get_yPlane ();
834  double zPlane = props.get_zPlane ();
835  double xPlaneN = props.get_xPlaneN ();
836  double yPlaneN = props.get_yPlaneN ();
837  double zPlaneN = props.get_zPlaneN ();
838 
839  // Axes planes
840  Matrix axe_color = props.get_color_rgb ();
841  if (axe_color.numel () > 0 && props.is_visible ())
842  {
843  set_color (axe_color);
844  set_polygon_offset (true, 2.5);
845 
846  glBegin (GL_QUADS);
847 
848  // X plane
849  glVertex3d (xPlane, yPlaneN, zPlaneN);
850  glVertex3d (xPlane, yPlane, zPlaneN);
851  glVertex3d (xPlane, yPlane, zPlane);
852  glVertex3d (xPlane, yPlaneN, zPlane);
853 
854  // Y plane
855  glVertex3d (xPlaneN, yPlane, zPlaneN);
856  glVertex3d (xPlane, yPlane, zPlaneN);
857  glVertex3d (xPlane, yPlane, zPlane);
858  glVertex3d (xPlaneN, yPlane, zPlane);
859 
860  // Z plane
861  glVertex3d (xPlaneN, yPlaneN, zPlane);
862  glVertex3d (xPlane, yPlaneN, zPlane);
863  glVertex3d (xPlane, yPlane, zPlane);
864  glVertex3d (xPlaneN, yPlane, zPlane);
865 
866  glEnd ();
867 
868  set_polygon_offset (false);
869  }
870 }
871 
872 void
874 {
875  bool xySym = props.get_xySym ();
876  double xPlane = props.get_xPlane ();
877  double yPlane = props.get_yPlane ();
878  double zPlane = props.get_zPlane ();
879  double xPlaneN = props.get_xPlaneN ();
880  double yPlaneN = props.get_yPlaneN ();
881  double zPlaneN = props.get_zPlaneN ();
882  double xpTick = props.get_xpTick ();
883  double ypTick = props.get_ypTick ();
884  double zpTick = props.get_zpTick ();
885  double xpTickN = props.get_xpTickN ();
886  double ypTickN = props.get_ypTickN ();
887  double zpTickN = props.get_zpTickN ();
888 
889  bool plotyy = (props.has_property ("__plotyy_axes__"));
890 
891  // Axes box
892 
893  set_linestyle ("-", true);
894  set_linewidth (props.get_linewidth ());
895 
896  if (props.is_visible ())
897  {
898  glBegin (GL_LINES);
899 
900  // X box
901  set_color (props.get_xcolor_rgb ());
902  glVertex3d (xPlaneN, ypTick, zpTick);
903  glVertex3d (xPlane, ypTick, zpTick);
904 
905  if (props.is_box ())
906  {
907  glVertex3d (xPlaneN, ypTickN, zpTick);
908  glVertex3d (xPlane, ypTickN, zpTick);
909  glVertex3d (xPlaneN, ypTickN, zpTickN);
910  glVertex3d (xPlane, ypTickN, zpTickN);
911  glVertex3d (xPlaneN, ypTick, zpTickN);
912  glVertex3d (xPlane, ypTick, zpTickN);
913  }
914 
915  // Y box
916  set_color (props.get_ycolor_rgb ());
917  glVertex3d (xpTick, yPlaneN, zpTick);
918  glVertex3d (xpTick, yPlane, zpTick);
919 
920  if (props.is_box () && ! plotyy)
921  {
922  glVertex3d (xpTickN, yPlaneN, zpTick);
923  glVertex3d (xpTickN, yPlane, zpTick);
924  glVertex3d (xpTickN, yPlaneN, zpTickN);
925  glVertex3d (xpTickN, yPlane, zpTickN);
926  glVertex3d (xpTick, yPlaneN, zpTickN);
927  glVertex3d (xpTick, yPlane, zpTickN);
928  }
929 
930  // Z box
931  set_color (props.get_zcolor_rgb ());
932 
933  if (xySym)
934  {
935  glVertex3d (xPlaneN, yPlane, zPlaneN);
936  glVertex3d (xPlaneN, yPlane, zPlane);
937  }
938  else
939  {
940  glVertex3d (xPlane, yPlaneN, zPlaneN);
941  glVertex3d (xPlane, yPlaneN, zPlane);
942  }
943 
944  if (props.is_box ())
945  {
946  glVertex3d (xPlane, yPlane, zPlaneN);
947  glVertex3d (xPlane, yPlane, zPlane);
948 
949  if (xySym)
950  {
951  glVertex3d (xPlane, yPlaneN, zPlaneN);
952  glVertex3d (xPlane, yPlaneN, zPlane);
953  }
954  else
955  {
956  glVertex3d (xPlaneN, yPlane, zPlaneN);
957  glVertex3d (xPlaneN, yPlane, zPlane);
958  }
959 
960  glVertex3d (xPlaneN, yPlaneN, zPlaneN);
961  glVertex3d (xPlaneN, yPlaneN, zPlane);
962  }
963 
964  glEnd ();
965  }
966 }
967 
968 void
970 {
971  int xstate = props.get_xstate ();
972  int zstate = props.get_zstate ();
973  bool x2Dtop = props.get_x2Dtop ();
974  bool layer2Dtop = props.get_layer2Dtop ();
975  bool xyzSym = props.get_xyzSym ();
976  bool nearhoriz = props.get_nearhoriz ();
977  double xticklen = props.get_xticklen ();
978  double xtickoffset = props.get_xtickoffset ();
979  double fy = props.get_fy ();
980  double fz = props.get_fz ();
981  double x_min = props.get_x_min ();
982  double x_max = props.get_x_max ();
983  double yPlane = props.get_yPlane ();
984  double yPlaneN = props.get_yPlaneN ();
985  double ypTick = props.get_ypTick ();
986  double ypTickN = props.get_ypTickN ();
987  double zPlane = props.get_zPlane ();
988  double zPlaneN = props.get_zPlaneN ();
989  double zpTick = props.get_zpTick ();
990  double zpTickN = props.get_zpTickN ();
991 
992  // X grid
993 
994  if (props.is_visible () && xstate != AXE_DEPTH_DIR)
995  {
996  std::string gridstyle = props.get_gridlinestyle ();
997  std::string minorgridstyle = props.get_minorgridlinestyle ();
998  bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
999  bool do_xminorgrid = (props.is_xminorgrid ()
1000  && (minorgridstyle != "none"));
1001  bool do_xminortick = props.is_xminortick ();
1002  Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ());
1003  Matrix xmticks = xform.xscale (props.get_xmtick ().matrix_value ());
1004  string_vector xticklabels = props.get_xticklabel ().all_strings ();
1005  int wmax = 0, hmax = 0;
1006  bool tick_along_z = nearhoriz || xisinf (fy);
1007  bool mirror = props.is_box () && xstate != AXE_ANY_DIR;
1008 
1009  set_color (props.get_xcolor_rgb ());
1010 
1011  // grid lines
1012  if (do_xgrid)
1013  render_grid (gridstyle, xticks, x_min, x_max,
1014  yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane,
1015  zPlaneN, 0, (zstate != AXE_DEPTH_DIR));
1016 
1017  // tick marks
1018  if (tick_along_z)
1019  {
1020  render_tickmarks (xticks, x_min, x_max, ypTick, ypTick,
1021  zpTick, zpTickN, 0., 0.,
1022  signum (zpTick-zpTickN)*fz*xticklen,
1023  0, mirror);
1024  }
1025  else
1026  {
1027  render_tickmarks (xticks, x_min, x_max, ypTick, ypTickN,
1028  zpTick, zpTick, 0.,
1029  signum (ypTick-ypTickN)*fy*xticklen,
1030  0., 0, mirror);
1031  }
1032 
1033  // tick texts
1034  if (xticklabels.numel () > 0)
1035  {
1036  int halign = (xstate == AXE_HORZ_DIR ? 1 : (xyzSym ? 0 : 2));
1037  int valign = (xstate == AXE_VERT_DIR ? 1 : (x2Dtop ? 0 : 2));
1038 
1039  if (tick_along_z)
1040  render_ticktexts (xticks, xticklabels, x_min, x_max, ypTick,
1041  zpTick+signum (zpTick-zpTickN)*fz*xtickoffset,
1042  0, halign, valign, wmax, hmax);
1043  else
1044  render_ticktexts (xticks, xticklabels, x_min, x_max,
1045  ypTick+signum (ypTick-ypTickN)*fy*xtickoffset,
1046  zpTick, 0, halign, valign, wmax, hmax);
1047  }
1048 
1049  // minor grid lines
1050  if (do_xminorgrid)
1051  render_grid (minorgridstyle, xmticks, x_min, x_max,
1052  yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane,
1053  zPlaneN, 0, (zstate != AXE_DEPTH_DIR));
1054 
1055  // minor tick marks
1056  if (do_xminortick)
1057  {
1058  if (tick_along_z)
1059  render_tickmarks (xmticks, x_min, x_max, ypTick, ypTick,
1060  zpTick, zpTickN, 0., 0.,
1061  signum (zpTick-zpTickN)*fz*xticklen/2,
1062  0, mirror);
1063  else
1064  render_tickmarks (xmticks, x_min, x_max, ypTick, ypTickN,
1065  zpTick, zpTick, 0.,
1066  signum (ypTick-ypTickN)*fy*xticklen/2,
1067  0., 0, mirror);
1068  }
1069 
1070  gh_manager::get_object (props.get_xlabel ()).set ("visible", "on");
1071  }
1072  else
1073  gh_manager::get_object (props.get_xlabel ()).set ("visible", "off");
1074 }
1075 
1076 void
1078 {
1079  int ystate = props.get_ystate ();
1080  int zstate = props.get_zstate ();
1081  bool y2Dright = props.get_y2Dright ();
1082  bool layer2Dtop = props.get_layer2Dtop ();
1083  bool xyzSym = props.get_xyzSym ();
1084  bool nearhoriz = props.get_nearhoriz ();
1085  double yticklen = props.get_yticklen ();
1086  double ytickoffset = props.get_ytickoffset ();
1087  double fx = props.get_fx ();
1088  double fz = props.get_fz ();
1089  double xPlane = props.get_xPlane ();
1090  double xPlaneN = props.get_xPlaneN ();
1091  double xpTick = props.get_xpTick ();
1092  double xpTickN = props.get_xpTickN ();
1093  double y_min = props.get_y_min ();
1094  double y_max = props.get_y_max ();
1095  double zPlane = props.get_zPlane ();
1096  double zPlaneN = props.get_zPlaneN ();
1097  double zpTick = props.get_zpTick ();
1098  double zpTickN = props.get_zpTickN ();
1099 
1100  // Y grid
1101 
1102  if (ystate != AXE_DEPTH_DIR && props.is_visible ())
1103  {
1104  std::string gridstyle = props.get_gridlinestyle ();
1105  std::string minorgridstyle = props.get_minorgridlinestyle ();
1106  bool do_ygrid = (props.is_ygrid () && (gridstyle != "none"));
1107  bool do_yminorgrid = (props.is_yminorgrid ()
1108  && (minorgridstyle != "none"));
1109  bool do_yminortick = props.is_yminortick ();
1110  Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ());
1111  Matrix ymticks = xform.yscale (props.get_ymtick ().matrix_value ());
1112  string_vector yticklabels = props.get_yticklabel ().all_strings ();
1113  int wmax = 0, hmax = 0;
1114  bool tick_along_z = nearhoriz || xisinf (fx);
1115  bool mirror = props.is_box () && ystate != AXE_ANY_DIR
1116  && (! props.has_property ("__plotyy_axes__"));
1117 
1118  set_color (props.get_ycolor_rgb ());
1119 
1120  // grid lines
1121  if (do_ygrid)
1122  render_grid (gridstyle, yticks, y_min, y_max,
1123  xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane,
1124  zPlaneN, 1, (zstate != AXE_DEPTH_DIR));
1125 
1126  // tick marks
1127  if (tick_along_z)
1128  render_tickmarks (yticks, y_min, y_max, xpTick, xpTick,
1129  zpTick, zpTickN, 0., 0.,
1130  signum (zpTick-zpTickN)*fz*yticklen,
1131  1, mirror);
1132  else
1133  render_tickmarks (yticks, y_min, y_max, xpTick, xpTickN,
1134  zpTick, zpTick,
1135  signum (xPlaneN-xPlane)*fx*yticklen,
1136  0., 0., 1, mirror);
1137 
1138  // tick texts
1139  if (yticklabels.numel () > 0)
1140  {
1141  int halign = (ystate == AXE_HORZ_DIR
1142  ? 1 : (!xyzSym || y2Dright ? 0 : 2));
1143  int valign = (ystate == AXE_VERT_DIR ? 1 : 2);
1144 
1145  if (tick_along_z)
1146  render_ticktexts (yticks, yticklabels, y_min, y_max, xpTick,
1147  zpTick+signum (zpTick-zpTickN)*fz*ytickoffset,
1148  1, halign, valign, wmax, hmax);
1149  else
1150  render_ticktexts (yticks, yticklabels, y_min, y_max,
1151  xpTick+signum (xpTick-xpTickN)*fx*ytickoffset,
1152  zpTick, 1, halign, valign, wmax, hmax);
1153  }
1154 
1155  // minor grid lines
1156  if (do_yminorgrid)
1157  render_grid (minorgridstyle, ymticks, y_min, y_max,
1158  xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane,
1159  zPlaneN, 1, (zstate != AXE_DEPTH_DIR));
1160 
1161  // minor tick marks
1162  if (do_yminortick)
1163  {
1164  if (tick_along_z)
1165  render_tickmarks (ymticks, y_min, y_max, xpTick, xpTick,
1166  zpTick, zpTickN, 0., 0.,
1167  signum (zpTick-zpTickN)*fz*yticklen/2,
1168  1, mirror);
1169  else
1170  render_tickmarks (ymticks, y_min, y_max, xpTick, xpTickN,
1171  zpTick, zpTick,
1172  signum (xpTick-xpTickN)*fx*yticklen/2,
1173  0., 0., 1, mirror);
1174  }
1175 
1176  gh_manager::get_object (props.get_ylabel ()).set ("visible", "on");
1177  }
1178  else
1179  gh_manager::get_object (props.get_ylabel ()).set ("visible", "off");
1180 }
1181 
1182 void
1184 {
1185  int zstate = props.get_zstate ();
1186  bool xySym = props.get_xySym ();
1187  bool zSign = props.get_zSign ();
1188  double zticklen = props.get_zticklen ();
1189  double ztickoffset = props.get_ztickoffset ();
1190  double fx = props.get_fx ();
1191  double fy = props.get_fy ();
1192  double xPlane = props.get_xPlane ();
1193  double xPlaneN = props.get_xPlaneN ();
1194  double yPlane = props.get_yPlane ();
1195  double yPlaneN = props.get_yPlaneN ();
1196  double z_min = props.get_z_min ();
1197  double z_max = props.get_z_max ();
1198 
1199  // Z Grid
1200 
1201  if (zstate != AXE_DEPTH_DIR && props.is_visible ())
1202  {
1203  std::string gridstyle = props.get_gridlinestyle ();
1204  std::string minorgridstyle = props.get_minorgridlinestyle ();
1205  bool do_zgrid = (props.is_zgrid () && (gridstyle != "none"));
1206  bool do_zminorgrid = (props.is_zminorgrid ()
1207  && (minorgridstyle != "none"));
1208  bool do_zminortick = props.is_zminortick ();
1209  Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ());
1210  Matrix zmticks = xform.zscale (props.get_zmtick ().matrix_value ());
1211  string_vector zticklabels = props.get_zticklabel ().all_strings ();
1212  int wmax = 0, hmax = 0;
1213  bool mirror = props.is_box () && zstate != AXE_ANY_DIR;
1214 
1215  set_color (props.get_zcolor_rgb ());
1216 
1217  // grid lines
1218  if (do_zgrid)
1219  render_grid (gridstyle, zticks, z_min, z_max,
1220  xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
1221 
1222  // tick marks
1223  if (xySym)
1224  {
1225  if (xisinf (fy))
1226  render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
1227  yPlane, yPlane,
1228  signum (xPlaneN-xPlane)*fx*zticklen,
1229  0., 0., 2, mirror);
1230  else
1231  render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN,
1232  yPlane, yPlane, 0.,
1233  signum (yPlane-yPlaneN)*fy*zticklen,
1234  0., 2, false);
1235  }
1236  else
1237  {
1238  if (xisinf (fx))
1239  render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
1240  yPlaneN, yPlane, 0.,
1241  signum (yPlaneN-yPlane)*fy*zticklen,
1242  0., 2, mirror);
1243  else
1244  render_tickmarks (zticks, z_min, z_max, xPlane, xPlane,
1245  yPlaneN, yPlane,
1246  signum (xPlane-xPlaneN)*fx*zticklen,
1247  0., 0., 2, false);
1248  }
1249 
1250  // FIXME: tick texts
1251  if (zticklabels.numel () > 0)
1252  {
1253  int halign = 2;
1254  int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2));
1255 
1256  if (xySym)
1257  {
1258  if (xisinf (fy))
1259  render_ticktexts (zticks, zticklabels, z_min, z_max,
1260  xPlaneN+signum (xPlaneN-xPlane)*fx*ztickoffset,
1261  yPlane, 2, halign, valign, wmax, hmax);
1262  else
1263  render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN,
1264  yPlane+signum (yPlane-yPlaneN)*fy*ztickoffset,
1265  2, halign, valign, wmax, hmax);
1266  }
1267  else
1268  {
1269  if (xisinf (fx))
1270  render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane,
1271  yPlaneN+signum (yPlaneN-yPlane)*fy*ztickoffset,
1272  2, halign, valign, wmax, hmax);
1273  else
1274  render_ticktexts (zticks, zticklabels, z_min, z_max,
1275  xPlane+signum (xPlane-xPlaneN)*fx*ztickoffset,
1276  yPlaneN, 2, halign, valign, wmax, hmax);
1277  }
1278  }
1279 
1280  // minor grid lines
1281  if (do_zminorgrid)
1282  render_grid (minorgridstyle, zmticks, z_min, z_max,
1283  xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
1284 
1285  // minor tick marks
1286  if (do_zminortick)
1287  {
1288  if (xySym)
1289  {
1290  if (xisinf (fy))
1291  render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane,
1292  yPlane, yPlane,
1293  signum (xPlaneN-xPlane)*fx*zticklen/2,
1294  0., 0., 2, mirror);
1295  else
1296  render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN,
1297  yPlane, yPlane, 0.,
1298  signum (yPlane-yPlaneN)*fy*zticklen/2,
1299  0., 2, false);
1300  }
1301  else
1302  {
1303  if (xisinf (fx))
1304  render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
1305  yPlaneN, yPlane, 0.,
1306  signum (yPlaneN-yPlane)*fy*zticklen/2,
1307  0., 2, mirror);
1308  else
1309  render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
1310  yPlaneN, yPlaneN,
1311  signum (xPlane-xPlaneN)*fx*zticklen/2,
1312  0., 0., 2, false);
1313  }
1314  }
1315 
1316  gh_manager::get_object (props.get_zlabel ()).set ("visible", "on");
1317  }
1318  else
1319  gh_manager::get_object (props.get_zlabel ()).set ("visible", "off");
1320 }
1321 
1322 void
1324 {
1325  // Children
1326 
1327  GLboolean antialias;
1328  glGetBooleanv (GL_LINE_SMOOTH, &antialias);
1329 
1330  if (antialias == GL_TRUE)
1331  glEnable (GL_LINE_SMOOTH);
1332 
1333  Matrix children = props.get_all_children ();
1334  std::list<graphics_object> obj_list;
1335  std::list<graphics_object>::iterator it;
1336 
1337  // 1st pass: draw light objects
1338 
1339  // Start with the last element of the array of child objects to
1340  // display them in the oder they were added to the array.
1341 
1342  for (octave_idx_type i = children.numel () - 1; i >= 0; i--)
1343  {
1344  graphics_object go = gh_manager::get_object (children (i));
1345 
1346  if (go.get_properties ().is_visible ())
1347  {
1348  if (go.isa ("light"))
1349  draw (go);
1350  else
1351  obj_list.push_back (go);
1352  }
1353  }
1354 
1355  // 2nd pass: draw other objects (with units set to "data")
1356 
1357  it = obj_list.begin ();
1358  while (it != obj_list.end ())
1359  {
1360  graphics_object go = (*it);
1361 
1362  // FIXME: check whether object has "units" property and it is set
1363  // to "data"
1364  if (! go.isa ("text") || go.get ("units").string_value () == "data")
1365  {
1367  draw (go);
1368 
1369  it = obj_list.erase (it);
1370  }
1371  else
1372  it++;
1373  }
1374 
1375  // 3rd pass: draw remaining objects
1376 
1377  glDisable (GL_DEPTH_TEST);
1378 
1379  for (it = obj_list.begin (); it != obj_list.end (); it++)
1380  {
1381  graphics_object go = (*it);
1382 
1384  draw (go);
1385  }
1386 
1387  glEnable (GL_DEPTH_TEST);
1388 
1389  set_clipping (false);
1390 
1391  // FIXME: finalize rendering (transparency processing)
1392  // FIXME: draw zoom box, if needed
1393 }
1394 
1395 void
1397 {
1398  static double floatmax = std::numeric_limits<float>::max ();
1399 
1400  double x_min = props.get_x_min ();
1401  double x_max = props.get_x_max ();
1402  double y_min = props.get_y_min ();
1403  double y_max = props.get_y_max ();
1404  double z_min = props.get_z_min ();
1405  double z_max = props.get_z_max ();
1406 
1407  if (x_max > floatmax || y_max > floatmax || z_max > floatmax
1408  || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax)
1409  {
1410  warning ("gl-render: data values greater than float capacity. (1) Scale data, or (2) Use gnuplot");
1411  return;
1412  }
1413 
1415 
1416  // draw axes object
1417 
1418  draw_axes_planes (props);
1419  draw_axes_boxes (props);
1420 
1421  set_font (props);
1422 
1423  draw_axes_x_grid (props);
1424  draw_axes_y_grid (props);
1425  draw_axes_z_grid (props);
1426 
1427  set_linestyle ("-");
1428 
1429  set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max);
1430 
1431  draw_axes_children (props);
1432 }
1433 
1434 void
1436 {
1437  Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
1438  Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
1439  Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
1440 
1441  bool has_z = (z.numel () > 0);
1442  int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()),
1443  (has_z ? z.numel ()
1445  octave_uint8 clip_mask = (props.is_clipping () ? 0x7F : 0x40), clip_ok (0x40);
1446 
1447  std::vector<octave_uint8> clip (n);
1448 
1449  if (has_z)
1450  for (int i = 0; i < n; i++)
1451  clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask);
1452  else
1453  {
1454  double z_mid = (zmin+zmax)/2;
1455 
1456  for (int i = 0; i < n; i++)
1457  clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask);
1458  }
1459 
1460  if (! props.linestyle_is ("none"))
1461  {
1462  set_color (props.get_color_rgb ());
1463  set_linestyle (props.get_linestyle (), false);
1464  set_linewidth (props.get_linewidth ());
1465 
1466  if (has_z)
1467  {
1468  bool flag = false;
1469 
1470  for (int i = 1; i < n; i++)
1471  {
1472  if ((clip[i-1] & clip[i]) == clip_ok)
1473  {
1474  if (! flag)
1475  {
1476  flag = true;
1477  glBegin (GL_LINE_STRIP);
1478  glVertex3d (x(i-1), y(i-1), z(i-1));
1479  }
1480  glVertex3d (x(i), y(i), z(i));
1481  }
1482  else if (flag)
1483  {
1484  flag = false;
1485  glEnd ();
1486  }
1487  }
1488 
1489  if (flag)
1490  glEnd ();
1491  }
1492  else
1493  {
1494  bool flag = false;
1495 
1496  for (int i = 1; i < n; i++)
1497  {
1498  if ((clip[i-1] & clip[i]) == clip_ok)
1499  {
1500  if (! flag)
1501  {
1502  flag = true;
1503  glBegin (GL_LINE_STRIP);
1504  glVertex2d (x(i-1), y(i-1));
1505  }
1506  glVertex2d (x(i), y(i));
1507  }
1508  else if (flag)
1509  {
1510  flag = false;
1511  glEnd ();
1512  }
1513  }
1514 
1515  if (flag)
1516  glEnd ();
1517  }
1518 
1519  set_linewidth (0.5);
1520  set_linestyle ("-");
1521  }
1522 
1523  set_clipping (false);
1524 
1525  if (! props.marker_is ("none") &&
1526  ! (props.markeredgecolor_is ("none")
1527  && props.markerfacecolor_is ("none")))
1528  {
1529  Matrix lc, fc;
1530 
1531  if (props.markeredgecolor_is ("auto"))
1532  lc = props.get_color_rgb ();
1533  else if (! props.markeredgecolor_is ("none"))
1534  lc = props.get_markeredgecolor_rgb ();
1535 
1536  if (props.markerfacecolor_is ("auto"))
1537  fc = props.get_color_rgb ();
1538  else if (! props.markerfacecolor_is ("none"))
1539  fc = props.get_markerfacecolor_rgb ();
1540 
1541  init_marker (props.get_marker (), props.get_markersize (),
1542  props.get_linewidth ());
1543 
1544  for (int i = 0; i < n; i++)
1545  {
1546  if (clip[i] == clip_ok)
1547  draw_marker (x(i), y(i),
1548  has_z ? z(i) : static_cast<double> (i) / n,
1549  lc, fc);
1550  }
1551 
1552  end_marker ();
1553  }
1554 
1555  set_clipping (props.is_clipping ());
1556 }
1557 
1558 void
1560 {
1561  const Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
1562  const Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
1563  const Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
1564 
1565  int zr = z.rows (), zc = z.columns ();
1566 
1567  NDArray c;
1568  const NDArray n = props.get_vertexnormals ().array_value ();
1569 
1570  // FIXME: handle transparency
1571  Matrix a;
1572 
1573  if (props.facelighting_is ("phong") || props.edgelighting_is ("phong"))
1574  warning ("opengl_renderer::draw: phong light model not supported");
1575 
1576  int fc_mode = (props.facecolor_is_rgb () ? 0 :
1577  (props.facecolor_is ("flat") ? 1 :
1578  (props.facecolor_is ("interp") ? 2 :
1579  (props.facecolor_is ("texturemap") ? 3 : -1))));
1580  int fl_mode = (props.facelighting_is ("none") ? 0 :
1581  (props.facelighting_is ("flat") ? 1 : 2));
1582  int fa_mode = (props.facealpha_is_double () ? 0 :
1583  (props.facealpha_is ("flat") ? 1 : 2));
1584  int ec_mode = (props.edgecolor_is_rgb () ? 0 :
1585  (props.edgecolor_is ("flat") ? 1 :
1586  (props.edgecolor_is ("interp") ? 2 : -1)));
1587  int el_mode = (props.edgelighting_is ("none") ? 0 :
1588  (props.edgelighting_is ("flat") ? 1 : 2));
1589  int ea_mode = (props.edgealpha_is_double () ? 0 :
1590  (props.edgealpha_is ("flat") ? 1 : 2));
1591 
1592  Matrix fcolor = (fc_mode == 3 ? Matrix (1, 3, 1.0)
1593  : props.get_facecolor_rgb ());
1594  Matrix ecolor = props.get_edgecolor_rgb ();
1595 
1596  float as = props.get_ambientstrength ();
1597  float ds = props.get_diffusestrength ();
1598  float ss = props.get_specularstrength ();
1599  float se = props.get_specularexponent ();
1600  float cb[4] = { 0.0, 0.0, 0.0, 1.0 };
1601  double d = 1.0;
1602 
1603  opengl_texture tex;
1604 
1605  int i1, i2, j1, j2;
1606  bool x_mat = (x.rows () == z.rows ());
1607  bool y_mat = (y.columns () == z.columns ());
1608 
1609  i1 = i2 = j1 = j2 = 0;
1610 
1611  if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0)
1612  c = props.get_color_data ().array_value ();
1613 
1614  boolMatrix clip (z.dims (), false);
1615 
1616  for (int i = 0; i < zr; i++)
1617  {
1618  if (x_mat)
1619  i1 = i;
1620 
1621  for (int j = 0; j < zc; j++)
1622  {
1623  if (y_mat)
1624  j1 = j;
1625 
1626  clip(i,j) = is_nan_or_inf (x(i1,j), y(i,j1), z(i,j));
1627  }
1628  }
1629 
1630  if (fa_mode > 0 || ea_mode > 0)
1631  {
1632  // FIXME: implement alphadata conversion
1633  //a = props.get_alpha_data ();
1634  }
1635 
1636  if (fl_mode > 0 || el_mode > 0)
1637  {
1638  float buf[4] = { ss, ss, ss, 1 };
1639 
1640  glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
1641  glMaterialf (LIGHT_MODE, GL_SHININESS, se);
1642  }
1643 
1644  // FIXME: good candidate for caching, transfering pixel
1645  // data to OpenGL is time consuming.
1646  if (fc_mode == 3)
1647  tex = opengl_texture::create (props.get_color_data ());
1648 
1649  if (! props.facecolor_is ("none"))
1650  {
1651  if (props.get_facealpha_double () == 1)
1652  {
1653  if (fc_mode == 0 || fc_mode == 3)
1654  {
1655  glColor3dv (fcolor.data ());
1656  if (fl_mode > 0)
1657  {
1658  for (int i = 0; i < 3; i++)
1659  cb[i] = as * fcolor(i);
1660  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1661 
1662  for (int i = 0; i < 3; i++)
1663  cb[i] = ds * fcolor(i);
1664  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1665  }
1666  }
1667 
1668  if (fl_mode > 0)
1669  glEnable (GL_LIGHTING);
1670  glShadeModel ((fc_mode == 2 || fl_mode == 2) ? GL_SMOOTH : GL_FLAT);
1671  set_polygon_offset (true, 1);
1672  if (fc_mode == 3)
1673  glEnable (GL_TEXTURE_2D);
1674 
1675  for (int i = 1; i < zc; i++)
1676  {
1677  if (y_mat)
1678  {
1679  i1 = i-1;
1680  i2 = i;
1681  }
1682 
1683  for (int j = 1; j < zr; j++)
1684  {
1685 
1686  if (clip(j-1, i-1) || clip(j, i-1)
1687  || clip(j-1, i) || clip(j, i))
1688  continue;
1689 
1690  if (fc_mode == 1)
1691  {
1692  // "flat" only needs color at lower-left vertex
1693  if (! xfinite (c(j-1,i-1)))
1694  continue;
1695  }
1696  else if (fc_mode == 2)
1697  {
1698  // "interp" needs valid color at all 4 vertices
1699  if (! (xfinite (c(j-1, i-1)) && xfinite (c(j, i-1))
1700  && xfinite (c(j-1, i)) && xfinite (c(j, i))))
1701  continue;
1702  }
1703 
1704  if (x_mat)
1705  {
1706  j1 = j-1;
1707  j2 = j;
1708  }
1709 
1710  glBegin (GL_QUADS);
1711 
1712  // Vertex 1
1713  if (fc_mode == 3)
1714  tex.tex_coord (double (i-1) / (zc-1),
1715  double (j-1) / (zr-1));
1716  else if (fc_mode > 0)
1717  {
1718  // FIXME: is there a smarter way to do this?
1719  for (int k = 0; k < 3; k++)
1720  cb[k] = c(j-1, i-1, k);
1721  glColor3fv (cb);
1722 
1723  if (fl_mode > 0)
1724  {
1725  for (int k = 0; k < 3; k++)
1726  cb[k] *= as;
1727  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1728 
1729  for (int k = 0; k < 3; k++)
1730  cb[k] = ds * c(j-1, i-1, k);
1731  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1732  }
1733  }
1734  if (fl_mode > 0)
1735  {
1736  d = sqrt (n(j-1,i-1,0) * n(j-1,i-1,0)
1737  + n(j-1,i-1,1) * n(j-1,i-1,1)
1738  + n(j-1,i-1,2) * n(j-1,i-1,2));
1739  glNormal3d (n(j-1,i-1,0)/d,
1740  n(j-1,i-1,1)/d,
1741  n(j-1,i-1,2)/d);
1742  }
1743  glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
1744 
1745  // Vertex 2
1746  if (fc_mode == 3)
1747  tex.tex_coord (double (i) / (zc-1), double (j-1) / (zr-1));
1748  else if (fc_mode == 2)
1749  {
1750  for (int k = 0; k < 3; k++)
1751  cb[k] = c(j-1, i, k);
1752  glColor3fv (cb);
1753 
1754  if (fl_mode > 0)
1755  {
1756  for (int k = 0; k < 3; k++)
1757  cb[k] *= as;
1758  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1759 
1760  for (int k = 0; k < 3; k++)
1761  cb[k] = ds * c(j-1, i, k);
1762  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1763  }
1764  }
1765 
1766  if (fl_mode == 2)
1767  {
1768  d = sqrt (n(j-1,i,0) * n(j-1,i,0)
1769  + n(j-1,i,1) * n(j-1,i,1)
1770  + n(j-1,i,2) * n(j-1,i,2));
1771  glNormal3d (n(j-1,i,0)/d, n(j-1,i,1)/d, n(j-1,i,2)/d);
1772  }
1773 
1774  glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
1775 
1776  // Vertex 3
1777  if (fc_mode == 3)
1778  tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1));
1779  else if (fc_mode == 2)
1780  {
1781  for (int k = 0; k < 3; k++)
1782  cb[k] = c(j, i, k);
1783  glColor3fv (cb);
1784 
1785  if (fl_mode > 0)
1786  {
1787  for (int k = 0; k < 3; k++)
1788  cb[k] *= as;
1789  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1790 
1791  for (int k = 0; k < 3; k++)
1792  cb[k] = ds * c(j, i, k);
1793  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1794  }
1795  }
1796  if (fl_mode == 2)
1797  {
1798  d = sqrt (n(j,i,0) * n(j,i,0)
1799  + n(j,i,1) * n(j,i,1)
1800  + n(j,i,2) * n(j,i,2));
1801  glNormal3d (n(j,i,0)/d, n(j,i,1)/d, n(j,i,2)/d);
1802  }
1803  glVertex3d (x(j2,i), y(j,i2), z(j,i));
1804 
1805  // Vertex 4
1806  if (fc_mode == 3)
1807  tex.tex_coord (double (i-1) / (zc-1), double (j) / (zr-1));
1808  else if (fc_mode == 2)
1809  {
1810  for (int k = 0; k < 3; k++)
1811  cb[k] = c(j, i-1, k);
1812  glColor3fv (cb);
1813 
1814  if (fl_mode > 0)
1815  {
1816  for (int k = 0; k < 3; k++)
1817  cb[k] *= as;
1818  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1819 
1820  for (int k = 0; k < 3; k++)
1821  cb[k] = ds * c(j, i-1, k);
1822  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1823  }
1824  }
1825  if (fl_mode == 2)
1826  {
1827  d = sqrt (n(j,i-1,0) * n(j,i-1,0)
1828  + n(j,i-1,1) * n(j,i-1,1)
1829  + n(j,i-1,2) * n(j,i-1,2));
1830  glNormal3d (n(j,i-1,0)/d, n(j,i-1,1)/d, n(j,i-1,2)/d);
1831  }
1832  glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
1833 
1834  glEnd ();
1835  }
1836  }
1837 
1838  set_polygon_offset (false);
1839  if (fc_mode == 3)
1840  glDisable (GL_TEXTURE_2D);
1841 
1842  if (fl_mode > 0)
1843  glDisable (GL_LIGHTING);
1844  }
1845  else
1846  {
1847  // FIXME: implement transparency
1848  }
1849  }
1850 
1851  if (! props.edgecolor_is ("none"))
1852  {
1853  if (props.get_edgealpha_double () == 1)
1854  {
1855  if (ec_mode == 0)
1856  {
1857  glColor3dv (ecolor.data ());
1858  if (fl_mode > 0)
1859  {
1860  for (int i = 0; i < 3; i++)
1861  cb[i] = as * ecolor(i);
1862  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1863 
1864  for (int i = 0; i < 3; i++)
1865  cb[i] = ds * ecolor(i);
1866  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1867  }
1868  }
1869 
1870  if (el_mode > 0)
1871  glEnable (GL_LIGHTING);
1872  glShadeModel ((ec_mode == 2 || el_mode == 2) ? GL_SMOOTH : GL_FLAT);
1873 
1874  set_linestyle (props.get_linestyle (), false);
1875  set_linewidth (props.get_linewidth ());
1876 
1877  // Mesh along Y-axis
1878 
1879  if (props.meshstyle_is ("both") || props.meshstyle_is ("column"))
1880  {
1881  for (int i = 0; i < zc; i++)
1882  {
1883  if (y_mat)
1884  {
1885  i1 = i-1;
1886  i2 = i;
1887  }
1888 
1889  for (int j = 1; j < zr; j++)
1890  {
1891  if (clip(j-1,i) || clip(j,i))
1892  continue;
1893 
1894  if (ec_mode == 1)
1895  {
1896  // "flat" only needs color at lower-left vertex
1897  if (! xfinite (c(j-1,i)))
1898  continue;
1899  }
1900  else if (ec_mode == 2)
1901  {
1902  // "interp" needs valid color at both vertices
1903  if (! (xfinite (c(j-1, i)) && xfinite (c(j, i))))
1904  continue;
1905  }
1906 
1907  if (x_mat)
1908  {
1909  j1 = j-1;
1910  j2 = j;
1911  }
1912 
1913  glBegin (GL_LINES);
1914 
1915  // Vertex 1
1916  if (ec_mode > 0)
1917  {
1918  for (int k = 0; k < 3; k++)
1919  cb[k] = c(j-1, i, k);
1920  glColor3fv (cb);
1921 
1922  if (fl_mode > 0)
1923  {
1924  for (int k = 0; k < 3; k++)
1925  cb[k] *= as;
1926  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1927 
1928  for (int k = 0; k < 3; k++)
1929  cb[k] = ds * c(j-1, i, k);
1930  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1931  }
1932  }
1933  if (el_mode > 0)
1934  {
1935  d = sqrt (n(j-1,i,0) * n(j-1,i,0)
1936  + n(j-1,i,1) * n(j-1,i,1)
1937  + n(j-1,i,2) * n(j-1,i,2));
1938  glNormal3d (n(j-1,i,0)/d, n(j-1,i,1)/d, n(j-1,i,2)/d);
1939  }
1940  glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
1941 
1942  // Vertex 2
1943  if (ec_mode == 2)
1944  {
1945  for (int k = 0; k < 3; k++)
1946  cb[k] = c(j, i, k);
1947  glColor3fv (cb);
1948 
1949  if (fl_mode > 0)
1950  {
1951  for (int k = 0; k < 3; k++)
1952  cb[k] *= as;
1953  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
1954 
1955  for (int k = 0; k < 3; k++)
1956  cb[k] = ds * c(j, i, k);
1957  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
1958  }
1959  }
1960  if (el_mode == 2)
1961  {
1962  d = sqrt (n(j,i,0) * n(j,i,0)
1963  + n(j,i,1) * n(j,i,1)
1964  + n(j,i,2) * n(j,i,2));
1965  glNormal3d (n(j,i,0)/d, n(j,i,1)/d, n(j,i,2)/d);
1966  }
1967  glVertex3d (x(j2,i), y(j,i2), z(j,i));
1968 
1969  glEnd ();
1970  }
1971  }
1972  }
1973 
1974  // Mesh along X-axis
1975 
1976  if (props.meshstyle_is ("both") || props.meshstyle_is ("row"))
1977  {
1978  for (int j = 0; j < zr; j++)
1979  {
1980  if (x_mat)
1981  {
1982  j1 = j-1;
1983  j2 = j;
1984  }
1985 
1986  for (int i = 1; i < zc; i++)
1987  {
1988  if (clip(j,i-1) || clip(j,i))
1989  continue;
1990 
1991  if (ec_mode == 1)
1992  {
1993  // "flat" only needs color at lower-left vertex
1994  if (! xfinite (c(j,i-1)))
1995  continue;
1996  }
1997  else if (ec_mode == 2)
1998  {
1999  // "interp" needs valid color at both vertices
2000  if (! (xfinite (c(j, i-1)) && xfinite (c(j, i))))
2001  continue;
2002  }
2003 
2004  if (y_mat)
2005  {
2006  i1 = i-1;
2007  i2 = i;
2008  }
2009 
2010  glBegin (GL_LINES);
2011 
2012  // Vertex 1
2013  if (ec_mode > 0)
2014  {
2015  for (int k = 0; k < 3; k++)
2016  cb[k] = c(j, i-1, k);
2017  glColor3fv (cb);
2018 
2019  if (fl_mode > 0)
2020  {
2021  for (int k = 0; k < 3; k++)
2022  cb[k] *= as;
2023  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2024 
2025  for (int k = 0; k < 3; k++)
2026  cb[k] = ds * c(j, i-1, k);
2027  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2028  }
2029  }
2030  if (el_mode > 0)
2031  {
2032  d = sqrt (n(j,i-1,0) * n(j,i-1,0)
2033  + n(j,i-1,1) * n(j,i-1,1)
2034  + n(j,i-1,2) * n(j,i-1,2));
2035  glNormal3d (n(j,i-1,0)/d, n(j,i-1,1)/d, n(j,i-1,2)/d);
2036  }
2037  glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
2038 
2039  // Vertex 2
2040  if (ec_mode == 2)
2041  {
2042  for (int k = 0; k < 3; k++)
2043  cb[k] = c(j, i, k);
2044  glColor3fv (cb);
2045 
2046  if (fl_mode > 0)
2047  {
2048  for (int k = 0; k < 3; k++)
2049  cb[k] *= as;
2050  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2051 
2052  for (int k = 0; k < 3; k++)
2053  cb[k] = ds * c(j, i, k);
2054  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2055  }
2056  }
2057  if (el_mode == 2)
2058  {
2059  d = sqrt (n(j,i,0) * n(j,i,0)
2060  + n(j,i,1) * n(j,i,1)
2061  + n(j,i,2) * n(j,i,2));
2062  glNormal3d (n(j,i,0)/d, n(j,i,1)/d, n(j,i,2)/d);
2063  }
2064  glVertex3d (x(j2,i), y(j,i2), z(j,i));
2065 
2066  glEnd ();
2067  }
2068  }
2069  }
2070 
2071  set_linestyle ("-");
2072  set_linewidth (0.5);
2073 
2074  if (el_mode > 0)
2075  glDisable (GL_LIGHTING);
2076  }
2077  else
2078  {
2079  // FIXME: implement transparency
2080  }
2081  }
2082 
2083  if (! props.marker_is ("none") &&
2084  ! (props.markeredgecolor_is ("none")
2085  && props.markerfacecolor_is ("none")))
2086  {
2087  // FIXME: check how transparency should be handled in markers
2088  // FIXME: check what to do with marker facecolor set to auto
2089  // and facecolor set to none.
2090 
2091  bool do_edge = ! props.markeredgecolor_is ("none");
2092  bool do_face = ! props.markerfacecolor_is ("none");
2093 
2094  Matrix mecolor = props.get_markeredgecolor_rgb ();
2095  Matrix mfcolor = props.get_markerfacecolor_rgb ();
2096  Matrix cc (1, 3, 0.0);
2097 
2098  if (mecolor.numel () == 0 && props.markeredgecolor_is ("auto"))
2099  {
2100  mecolor = props.get_edgecolor_rgb ();
2101  do_edge = ! props.edgecolor_is ("none");
2102  }
2103 
2104  if (mfcolor.numel () == 0 && props.markerfacecolor_is ("auto"))
2105  {
2106  mfcolor = props.get_facecolor_rgb ();
2107  do_face = ! props.facecolor_is ("none");
2108  }
2109 
2110  if ((mecolor.numel () == 0 || mfcolor.numel () == 0)
2111  && c.numel () == 0)
2112  c = props.get_color_data ().array_value ();
2113 
2114  init_marker (props.get_marker (), props.get_markersize (),
2115  props.get_linewidth ());
2116 
2117  for (int i = 0; i < zc; i++)
2118  {
2119  if (y_mat)
2120  i1 = i;
2121 
2122  for (int j = 0; j < zr; j++)
2123  {
2124  if (clip(j,i))
2125  continue;
2126 
2127  if (x_mat)
2128  j1 = j;
2129 
2130  if ((do_edge && mecolor.numel () == 0)
2131  || (do_face && mfcolor.numel () == 0))
2132  {
2133  if (! xfinite (c(j,i)))
2134  continue; // Skip NaNs in color data
2135 
2136  for (int k = 0; k < 3; k++)
2137  cc(k) = c(j,i,k);
2138  }
2139 
2140  Matrix lc = (do_edge ? (mecolor.numel () == 0 ? cc : mecolor)
2141  : Matrix ());
2142  Matrix fc = (do_face ? (mfcolor.numel () == 0 ? cc : mfcolor)
2143  : Matrix ());
2144 
2145  draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc);
2146  }
2147  }
2148 
2149  end_marker ();
2150  }
2151 }
2152 
2153 // FIXME: global optimization (rendering, data structures...), there
2154 // is probably a smarter/faster/less-memory-consuming way to do this.
2155 void
2157 {
2158  const Matrix f = props.get_faces ().matrix_value ();
2159  const Matrix v = xform.scale (props.get_vertices ().matrix_value ());
2160  Matrix c;
2161  const Matrix n = props.get_vertexnormals ().matrix_value ();
2162  Matrix a;
2163 
2164  int nv = v.rows ();
2165  // int vmax = v.columns ();
2166  int nf = f.rows ();
2167  int fcmax = f.columns ();
2168 
2169  bool has_z = (v.columns () > 2);
2170  bool has_facecolor = false;
2171  bool has_facealpha = false;
2172 
2173  int fc_mode = ((props.facecolor_is ("none")
2174  || props.facecolor_is_rgb ()) ? 0 :
2175  (props.facecolor_is ("flat") ? 1 : 2));
2176  int fl_mode = (props.facelighting_is ("none") ? 0 :
2177  (props.facelighting_is ("flat") ? 1 : 2));
2178  int fa_mode = (props.facealpha_is_double () ? 0 :
2179  (props.facealpha_is ("flat") ? 1 : 2));
2180  int ec_mode = ((props.edgecolor_is ("none")
2181  || props.edgecolor_is_rgb ()) ? 0 :
2182  (props.edgecolor_is ("flat") ? 1 : 2));
2183  int el_mode = (props.edgelighting_is ("none") ? 0 :
2184  (props.edgelighting_is ("flat") ? 1 : 2));
2185  int ea_mode = (props.edgealpha_is_double () ? 0 :
2186  (props.edgealpha_is ("flat") ? 1 : 2));
2187 
2188  Matrix fcolor = props.get_facecolor_rgb ();
2189  Matrix ecolor = props.get_edgecolor_rgb ();
2190 
2191  float as = props.get_ambientstrength ();
2192  float ds = props.get_diffusestrength ();
2193  float ss = props.get_specularstrength ();
2194  float se = props.get_specularexponent ();
2195 
2196  boolMatrix clip (1, nv, false);
2197 
2198  if (has_z)
2199  for (int i = 0; i < nv; i++)
2200  clip(i) = is_nan_or_inf (v(i,0), v(i,1), v(i,2));
2201  else
2202  for (int i = 0; i < nv; i++)
2203  clip(i) = is_nan_or_inf (v(i,0), v(i,1), 0);
2204 
2205  boolMatrix clip_f (1, nf, false);
2206  Array<int> count_f (dim_vector (nf, 1), 0);
2207 
2208  for (int i = 0; i < nf; i++)
2209  {
2210  bool fclip = false;
2211  int count = 0;
2212 
2213  for (int j = 0; j < fcmax && ! xisnan (f(i,j)); j++, count++)
2214  fclip = (fclip || clip(int (f(i,j) - 1)));
2215 
2216  clip_f(i) = fclip;
2217  count_f(i) = count;
2218  }
2219 
2220  if (fc_mode > 0 || ec_mode > 0)
2221  {
2222  c = props.get_color_data ().matrix_value ();
2223 
2224  if (c.rows () == 1)
2225  {
2226  // Single color specifications, we can simplify a little bit
2227 
2228  if (fc_mode > 0)
2229  {
2230  fcolor = c;
2231  fc_mode = 0;
2232  }
2233 
2234  if (ec_mode > 0)
2235  {
2236  ecolor = c;
2237  ec_mode = 0;
2238  }
2239 
2240  c = Matrix ();
2241  }
2242  else
2243  has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ()));
2244  }
2245 
2246  if (fa_mode > 0 || ea_mode > 0)
2247  {
2248  // FIXME: retrieve alpha data from patch object
2249  //a = props.get_alpha_data ();
2250  has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ()));
2251  }
2252 
2253  octave_idx_type fr = f.rows ();
2254  std::vector<vertex_data> vdata (f.numel ());
2255 
2256  for (int i = 0; i < nf; i++)
2257  for (int j = 0; j < count_f(i); j++)
2258  {
2259  int idx = int (f(i,j) - 1);
2260 
2261  Matrix vv (1, 3, 0.0);
2262  Matrix cc;
2263  Matrix nn(1, 3, 0.0);
2264  double aa = 1.0;
2265 
2266  vv(0) = v(idx,0); vv(1) = v(idx,1);
2267  if (has_z)
2268  vv(2) = v(idx,2);
2269  // FIXME: uncomment when patch object has normal computation
2270  //nn(0) = n(idx,0); nn(1) = n(idx,1); nn(2) = n(idx,2);
2271  if (c.numel () > 0)
2272  {
2273  cc.resize (1, 3);
2274  if (has_facecolor)
2275  cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2);
2276  else
2277  cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2);
2278  }
2279  if (a.numel () > 0)
2280  {
2281  if (has_facealpha)
2282  aa = a(i);
2283  else
2284  aa = a(idx);
2285  }
2286 
2287  vdata[i+j*fr] =
2288  vertex_data (vv, cc, nn, aa, as, ds, ss, se);
2289  }
2290 
2291  if (fl_mode > 0 || el_mode > 0)
2292  {
2293  float buf[4] = { ss, ss, ss, 1 };
2294 
2295  glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
2296  glMaterialf (LIGHT_MODE, GL_SHININESS, se);
2297  }
2298 
2299  if (! props.facecolor_is ("none"))
2300  {
2301  // FIXME: adapt to double-radio property
2302  if (props.get_facealpha_double () == 1)
2303  {
2304  if (fc_mode == 0)
2305  {
2306  glColor3dv (fcolor.data ());
2307  if (fl_mode > 0)
2308  {
2309  float cb[4] = { 0, 0, 0, 1 };
2310 
2311  for (int i = 0; i < 3; i++)
2312  cb[i] = (as * fcolor(i));
2313  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2314 
2315  for (int i = 0; i < 3; i++)
2316  cb[i] = ds * fcolor(i);
2317  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2318  }
2319  }
2320 
2321  if (fl_mode > 0)
2322  glEnable (GL_LIGHTING);
2323 
2324  // FIXME: use __index__ property from patch object
2325  patch_tesselator tess (this, fc_mode, fl_mode, 0);
2326 
2327  for (int i = 0; i < nf; i++)
2328  {
2329  if (clip_f(i))
2330  continue;
2331 
2332  tess.begin_polygon (true);
2333  tess.begin_contour ();
2334 
2335  for (int j = 0; j < count_f(i); j++)
2336  {
2337  vertex_data::vertex_data_rep *vv = vdata[i+j*fr].get_rep ();
2338 
2339  tess.add_vertex (vv->coords.fortran_vec (), vv);
2340  }
2341 
2342  tess.end_contour ();
2343  tess.end_polygon ();
2344  }
2345 
2346  if (fl_mode > 0)
2347  glDisable (GL_LIGHTING);
2348  }
2349  else
2350  {
2351  // FIXME: implement transparency
2352  }
2353  }
2354 
2355  if (! props.edgecolor_is ("none"))
2356  {
2357  // FIXME: adapt to double-radio property
2358  if (props.get_edgealpha_double () == 1)
2359  {
2360  if (ec_mode == 0)
2361  {
2362  glColor3dv (ecolor.data ());
2363  if (el_mode > 0)
2364  {
2365  float cb[4] = { 0, 0, 0, 1 };
2366 
2367  for (int i = 0; i < 3; i++)
2368  cb[i] = (as * ecolor(i));
2369  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
2370 
2371  for (int i = 0; i < 3; i++)
2372  cb[i] = ds * ecolor(i);
2373  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
2374  }
2375  }
2376 
2377  if (el_mode > 0)
2378  glEnable (GL_LIGHTING);
2379 
2380  set_linestyle (props.get_linestyle (), false);
2381  set_linewidth (props.get_linewidth ());
2382 
2383 
2384  // FIXME: use __index__ property from patch object; should we
2385  // offset patch contour as well?
2386  patch_tesselator tess (this, ec_mode, el_mode);
2387 
2388  for (int i = 0; i < nf; i++)
2389  {
2390  if (clip_f(i))
2391  {
2392  // This is an unclosed contour. Draw it as a line
2393  bool flag = false;
2394 
2395  for (int j = 0; j < count_f(i); j++)
2396  {
2397  if (! clip(int (f(i,j) - 1)))
2398  {
2399  vertex_data::vertex_data_rep *vv
2400  = vdata[i+j*fr].get_rep ();
2401  const Matrix m = vv->coords;
2402  if (! flag)
2403  {
2404  flag = true;
2405  glBegin (GL_LINE_STRIP);
2406  }
2407  glVertex3d (m(0), m(1), m(2));
2408  }
2409  else if (flag)
2410  {
2411  flag = false;
2412  glEnd ();
2413  }
2414  }
2415 
2416  if (flag)
2417  glEnd ();
2418  }
2419  else
2420  {
2421  tess.begin_polygon (false);
2422  tess.begin_contour ();
2423 
2424  for (int j = 0; j < count_f(i); j++)
2425  {
2426  vertex_data::vertex_data_rep *vv
2427  = vdata[i+j*fr].get_rep ();
2428  tess.add_vertex (vv->coords.fortran_vec (), vv);
2429  }
2430 
2431  tess.end_contour ();
2432  tess.end_polygon ();
2433  }
2434  }
2435 
2436  set_linestyle ("-");
2437  set_linewidth (0.5);
2438 
2439  if (el_mode > 0)
2440  glDisable (GL_LIGHTING);
2441  }
2442  else
2443  {
2444  // FIXME: implement transparency
2445  }
2446  }
2447 
2448  if (! props.marker_is ("none")
2449  && ! (props.markeredgecolor_is ("none")
2450  && props.markerfacecolor_is ("none")))
2451  {
2452  bool do_edge = ! props.markeredgecolor_is ("none");
2453  bool do_face = ! props.markerfacecolor_is ("none");
2454 
2455  Matrix mecolor = props.get_markeredgecolor_rgb ();
2456  Matrix mfcolor = props.get_markerfacecolor_rgb ();
2457 
2458  bool has_markerfacecolor = false;
2459 
2460  if ((mecolor.numel () == 0 && ! props.markeredgecolor_is ("none"))
2461  || (mfcolor.numel () == 0 && ! props.markerfacecolor_is ("none")))
2462  {
2463  Matrix mc = props.get_color_data ().matrix_value ();
2464 
2465  if (mc.rows () == 1)
2466  {
2467  // Single color specifications, we can simplify a little bit
2468 
2469  if (mfcolor.numel () == 0
2470  && ! props.markerfacecolor_is ("none"))
2471  mfcolor = mc;
2472 
2473  if (mecolor.numel () == 0
2474  && ! props.markeredgecolor_is ("none"))
2475  mecolor = mc;
2476  }
2477  else
2478  {
2479  if (c.numel () == 0)
2480  c = props.get_color_data ().matrix_value ();
2481  has_markerfacecolor = ((c.numel () > 0)
2482  && (c.rows () == f.rows ()));
2483  }
2484  }
2485 
2486 
2487  init_marker (props.get_marker (), props.get_markersize (),
2488  props.get_linewidth ());
2489 
2490  for (int i = 0; i < nf; i++)
2491  for (int j = 0; j < count_f(i); j++)
2492  {
2493  int idx = int (f(i,j) - 1);
2494 
2495  if (clip(idx))
2496  continue;
2497 
2498  Matrix cc;
2499  if (c.numel () > 0)
2500  {
2501  cc.resize (1, 3);
2502  if (has_markerfacecolor)
2503  cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2);
2504  else
2505  cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2);
2506  }
2507 
2508  Matrix lc = (do_edge ? (mecolor.numel () == 0 ? cc : mecolor)
2509  : Matrix ());
2510  Matrix fc = (do_face ? (mfcolor.numel () == 0 ? cc : mfcolor)
2511  : Matrix ());
2512 
2513  draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc);
2514  }
2515 
2516  end_marker ();
2517  }
2518 }
2519 
2520 void
2522 {
2523  draw (props.get_children ());
2524 }
2525 
2526 void
2528 {
2529  if (props.get_string ().is_empty ())
2530  return;
2531 
2532  Matrix pos = xform.scale (props.get_data_position ());
2533  const Matrix bbox = props.get_extent_matrix ();
2534 
2535  // FIXME: handle margin and surrounding box
2536  bool blend = glIsEnabled (GL_BLEND);
2537 
2538  glEnable (GL_BLEND);
2539  glEnable (GL_ALPHA_TEST);
2540  glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0);
2541  glBitmap (0, 0, 0, 0, bbox(0), bbox(1), 0);
2542  glDrawPixels (bbox(2), bbox(3),
2543  GL_RGBA, GL_UNSIGNED_BYTE, props.get_pixels ().data ());
2544  glDisable (GL_ALPHA_TEST);
2545  if (! blend)
2546  glDisable (GL_BLEND);
2547 
2548 }
2549 
2550 void
2552 {
2553  octave_value cdata = props.get_color_data ();
2554  dim_vector dv (cdata.dims ());
2555  int h = dv(0), w = dv(1);
2556 
2557  Matrix x = props.get_xdata ().matrix_value ();
2558  Matrix y = props.get_ydata ().matrix_value ();
2559 
2560  // Someone wants us to draw an empty image? No way.
2561  if (x.is_empty () || y.is_empty ())
2562  return;
2563 
2564  if (w > 1 && x(1) == x(0))
2565  x(1) = x(1) + (w-1);
2566 
2567  if (h > 1 && y(1) == y(0))
2568  y(1) = y(1) + (h-1);
2569 
2570  const ColumnVector p0 = xform.transform (x(0), y(0), 0);
2571  const ColumnVector p1 = xform.transform (x(1), y(1), 0);
2572 
2573  if (xisnan (p0(0)) || xisnan (p0(1)) || xisnan (p1(0)) || xisnan (p1(1)))
2574  {
2575  warning ("gl-render: image x,y data too large to draw");
2576  return;
2577  }
2578 
2579  // image pixel size in screen pixel units
2580  float pix_dx, pix_dy;
2581  // image pixel size in normalized units
2582  float nor_dx, nor_dy;
2583 
2584  if (w > 1)
2585  {
2586  pix_dx = (p1(0) - p0(0))/(w-1);
2587  nor_dx = (x(1) - x(0))/(w-1);
2588  }
2589  else
2590  {
2591  const ColumnVector p1w = xform.transform (x(1) + 1, y(1), 0);
2592  pix_dx = p1w(0) - p0(0);
2593  nor_dx = 1;
2594  }
2595 
2596  if (h > 1)
2597  {
2598  pix_dy = (p1(1) - p0(1))/(h-1);
2599  nor_dy = (y(1) - y(0))/(h-1);
2600  }
2601  else
2602  {
2603  const ColumnVector p1h = xform.transform (x(1), y(1) + 1, 0);
2604  pix_dy = p1h(1) - p0(1);
2605  nor_dy = 1;
2606  }
2607 
2608 
2609  // OpenGL won't draw the image if it's origin is outside the
2610  // viewport/clipping plane so we must do the clipping
2611  // ourselfes - only draw part of the image
2612 
2613  int j0 = 0, j1 = w;
2614  int i0 = 0, i1 = h;
2615 
2616  float im_xmin = x(0) - nor_dx/2;
2617  float im_xmax = x(1) + nor_dx/2;
2618  float im_ymin = y(0) - nor_dy/2;
2619  float im_ymax = y(1) + nor_dy/2;
2620  if (props.is_clipping ()) // clip to axes
2621  {
2622  if (im_xmin < xmin)
2623  j0 += (xmin - im_xmin)/nor_dx + 1;
2624  if (im_xmax > xmax)
2625  j1 -= (im_xmax - xmax)/nor_dx ;
2626 
2627  if (im_ymin < ymin)
2628  i0 += (ymin - im_ymin)/nor_dy + 1;
2629  if (im_ymax > ymax)
2630  i1 -= (im_ymax - ymax)/nor_dy;
2631  }
2632  else // clip to viewport
2633  {
2634  GLfloat vp[4];
2635  glGetFloatv (GL_VIEWPORT, vp);
2636  // FIXME: actually add the code to do it!
2637 
2638  }
2639 
2640  if (i0 >= i1 || j0 >= j1)
2641  return;
2642 
2643  glPixelZoom (pix_dx, -pix_dy);
2644  glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0);
2645 
2646  // by default this is 4
2647  glPixelStorei (GL_UNPACK_ALIGNMENT,1);
2648 
2649  // Expect RGB data
2650  if (dv.length () == 3 && dv(2) == 3)
2651  {
2652  if (cdata.is_double_type ())
2653  {
2654  const NDArray xcdata = cdata.array_value ();
2655 
2656  OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0));
2657 
2658  for (int i = i0; i < i1; i++)
2659  {
2660  for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
2661  {
2662  a[idx] = xcdata(i,j,0);
2663  a[idx+1] = xcdata(i,j,1);
2664  a[idx+2] = xcdata(i,j,2);
2665  }
2666  }
2667 
2668  draw_pixels (j1-j0, i1-i0, GL_RGB, GL_FLOAT, a);
2669 
2670  }
2671  else if (cdata.is_uint16_type ())
2672  {
2673  const uint16NDArray xcdata = cdata.uint16_array_value ();
2674 
2675  OCTAVE_LOCAL_BUFFER (GLushort, a, 3*(j1-j0)*(i1-i0));
2676 
2677  for (int i = i0; i < i1; i++)
2678  {
2679  for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
2680  {
2681  a[idx] = xcdata(i,j,0);
2682  a[idx+1] = xcdata(i,j,1);
2683  a[idx+2] = xcdata(i,j,2);
2684  }
2685  }
2686 
2687  draw_pixels (j1-j0, i1-i0, GL_RGB, GL_UNSIGNED_SHORT, a);
2688 
2689  }
2690  else if (cdata.is_uint8_type ())
2691  {
2692  const uint8NDArray xcdata = cdata.uint8_array_value ();
2693 
2694  OCTAVE_LOCAL_BUFFER (GLubyte, a, 3*(j1-j0)*(i1-i0));
2695 
2696  for (int i = i0; i < i1; i++)
2697  {
2698  for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3)
2699  {
2700  a[idx] = xcdata(i,j,0);
2701  a[idx+1] = xcdata(i,j,1);
2702  a[idx+2] = xcdata(i,j,2);
2703  }
2704  }
2705 
2706  draw_pixels (j1-j0, i1-i0, GL_RGB, GL_UNSIGNED_BYTE, a);
2707  }
2708  else
2709  warning ("opengl_texture::draw: invalid image data type (expected double, uint16, or uint8)");
2710  }
2711  else
2712  warning ("opengl_texture::draw: invalid image size (expected n*m*3 or n*m)");
2713 
2714  glPixelZoom (1, 1);
2715 }
2716 
2717 void
2718 opengl_renderer::set_viewport (int w, int h)
2719 {
2720  glViewport (0, 0, w, h);
2721 }
2722 
2723 void
2724 opengl_renderer::draw_pixels (GLsizei width, GLsizei height, GLenum format,
2725  GLenum type, const GLvoid *data)
2726 {
2727  glDrawPixels (width, height, format, type, data);
2728 }
2729 
2730 void
2732 {
2733  glColor3dv (c.data ());
2734 #if HAVE_FREETYPE
2736 #endif
2737 }
2738 
2739 void
2741 {
2742 #if HAVE_FREETYPE
2743  text_renderer.set_font (props.get ("fontname").string_value (),
2744  props.get ("fontweight").string_value (),
2745  props.get ("fontangle").string_value (),
2746  props.get ("fontsize").double_value ());
2747 #endif
2748 }
2749 
2750 void
2751 opengl_renderer::set_polygon_offset (bool on, double offset)
2752 {
2753  if (on)
2754  {
2755  glPolygonOffset (offset, offset);
2756  glEnable (GL_POLYGON_OFFSET_FILL);
2757  glEnable (GL_POLYGON_OFFSET_LINE);
2758  }
2759  else
2760  {
2761  glDisable (GL_POLYGON_OFFSET_FILL);
2762  glDisable (GL_POLYGON_OFFSET_LINE);
2763  }
2764 }
2765 
2766 void
2768 {
2769  glLineWidth (w);
2770 }
2771 
2772 void
2773 opengl_renderer::set_linestyle (const std::string& s, bool use_stipple)
2774 {
2775  bool solid = false;
2776 
2777  if (s == "-")
2778  {
2779  glLineStipple (1, static_cast<unsigned short> (0xFFFF));
2780  solid = true;
2781  }
2782  else if (s == ":")
2783  glLineStipple (1, static_cast<unsigned short> (0x8888));
2784  else if (s == "--")
2785  glLineStipple (1, static_cast<unsigned short> (0x0FFF));
2786  else if (s == "-.")
2787  glLineStipple (1, static_cast<unsigned short> (0x020F));
2788  else
2789  glLineStipple (1, static_cast<unsigned short> (0x0000));
2790 
2791  if (solid && ! use_stipple)
2792  glDisable (GL_LINE_STIPPLE);
2793  else
2794  glEnable (GL_LINE_STIPPLE);
2795 }
2796 
2797 void
2798 opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2,
2799  double z1, double z2)
2800 {
2801  double dx = (x2-x1);
2802  double dy = (y2-y1);
2803  double dz = (z2-z1);
2804 
2805  x1 -= 0.001*dx; x2 += 0.001*dx;
2806  y1 -= 0.001*dy; y2 += 0.001*dy;
2807  z1 -= 0.001*dz; z2 += 0.001*dz;
2808 
2809  ColumnVector p (4, 0.0);
2810 
2811  p(0) = -1; p(3) = x2;
2812  glClipPlane (GL_CLIP_PLANE0, p.data ());
2813  p(0) = 1; p(3) = -x1;
2814  glClipPlane (GL_CLIP_PLANE1, p.data ());
2815  p(0) = 0; p(1) = -1; p(3) = y2;
2816  glClipPlane (GL_CLIP_PLANE2, p.data ());
2817  p(1) = 1; p(3) = -y1;
2818  glClipPlane (GL_CLIP_PLANE3, p.data ());
2819  p(1) = 0; p(2) = -1; p(3) = z2;
2820  glClipPlane (GL_CLIP_PLANE4, p.data ());
2821  p(2) = 1; p(3) = -z1;
2822  glClipPlane (GL_CLIP_PLANE5, p.data ());
2823 
2824  xmin = x1; xmax = x2;
2825  ymin = y1; ymax = y2;
2826  zmin = z1; zmax = z2;
2827 }
2828 
2829 void
2830 opengl_renderer::set_clipping (bool enable)
2831 {
2832  bool has_clipping = (glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
2833 
2834  if (enable != has_clipping)
2835  {
2836  if (enable)
2837  for (int i = 0; i < 6; i++)
2838  glEnable (GL_CLIP_PLANE0+i);
2839  else
2840  for (int i = 0; i < 6; i++)
2841  glDisable (GL_CLIP_PLANE0+i);
2842  }
2843 }
2844 
2845 void
2846 opengl_renderer::init_marker (const std::string& m, double size, float width)
2847 {
2848 #if defined (HAVE_FRAMEWORK_OPENGL)
2849  GLint vw[4];
2850 #else
2851  int vw[4];
2852 #endif
2853 
2854  glGetIntegerv (GL_VIEWPORT, vw);
2855 
2856  glMatrixMode (GL_PROJECTION);
2857  glPushMatrix ();
2858  glLoadIdentity ();
2859  glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
2860  glMatrixMode (GL_MODELVIEW);
2861  glPushMatrix ();
2862 
2863  set_clipping (false);
2864  set_linewidth (width);
2865 
2866  marker_id = make_marker_list (m, size, false);
2867  filled_marker_id = make_marker_list (m, size, true);
2868 }
2869 
2870 void
2872 {
2873  glDeleteLists (marker_id, 1);
2874  glDeleteLists (filled_marker_id, 1);
2875 
2876  glMatrixMode (GL_MODELVIEW);
2877  glPopMatrix ();
2878  glMatrixMode (GL_PROJECTION);
2879  glPopMatrix ();
2880  set_linewidth (0.5f);
2881 }
2882 
2883 void
2884 opengl_renderer::draw_marker (double x, double y, double z,
2885  const Matrix& lc, const Matrix& fc)
2886 {
2887  ColumnVector tmp = xform.transform (x, y, z, false);
2888 
2889  glLoadIdentity ();
2890  glTranslated (tmp(0), tmp(1), -tmp(2));
2891 
2892  if (filled_marker_id > 0 && fc.numel () > 0)
2893  {
2894  glColor3dv (fc.data ());
2895  set_polygon_offset (true, -1.0);
2896  glCallList (filled_marker_id);
2897  if (lc.numel () > 0)
2898  {
2899  glColor3dv (lc.data ());
2900  glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
2901  glEdgeFlag (GL_TRUE);
2902  set_polygon_offset (true, -2.0);
2903  glCallList (filled_marker_id);
2904  glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
2905  }
2906  set_polygon_offset (false);
2907  }
2908  else if (marker_id > 0 && lc.numel () > 0)
2909  {
2910  glColor3dv (lc.data ());
2911  glCallList (marker_id);
2912  }
2913 }
2914 
2915 unsigned int
2916 opengl_renderer::make_marker_list (const std::string& marker, double size,
2917  bool filled) const
2918 {
2919  char c = marker[0];
2920 
2921  if (filled && (c == '+' || c == 'x' || c == '*' || c == '.'))
2922  return 0;
2923 
2924  unsigned int ID = glGenLists (1);
2925  double sz = size * toolkit.get_screen_resolution () / 72.0;
2926 
2927  // constants for the * marker
2928  const double sqrt2d4 = 0.35355339059327;
2929  double tt = sz*sqrt2d4;
2930 
2931  glNewList (ID, GL_COMPILE);
2932 
2933  switch (marker[0])
2934  {
2935  case '+':
2936  glBegin (GL_LINES);
2937  glVertex2f (-sz/2, 0);
2938  glVertex2f (sz/2, 0);
2939  glVertex2f (0, -sz/2);
2940  glVertex2f (0, sz/2);
2941  glEnd ();
2942  break;
2943  case 'x':
2944  glBegin (GL_LINES);
2945  glVertex2f (-sz/2, -sz/2);
2946  glVertex2f (sz/2, sz/2);
2947  glVertex2f (-sz/2, sz/2);
2948  glVertex2f (sz/2, -sz/2);
2949  glEnd ();
2950  break;
2951  case '*':
2952  glBegin (GL_LINES);
2953  glVertex2f (-sz/2, 0);
2954  glVertex2f (sz/2, 0);
2955  glVertex2f (0, -sz/2);
2956  glVertex2f (0, sz/2);
2957  glVertex2f (-tt, -tt);
2958  glVertex2f (+tt, +tt);
2959  glVertex2f (-tt, +tt);
2960  glVertex2f (+tt, -tt);
2961  glEnd ();
2962  break;
2963  case '.':
2964  {
2965  double ang_step = M_PI / 5;
2966 
2967  glBegin (GL_POLYGON);
2968  for (double ang = 0; ang < (2*M_PI); ang += ang_step)
2969  glVertex2d (sz*cos (ang)/3, sz*sin (ang)/3);
2970  glEnd ();
2971  }
2972  break;
2973  case 's':
2974  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
2975  glVertex2d (-sz/2, -sz/2);
2976  glVertex2d (-sz/2, sz/2);
2977  glVertex2d (sz/2, sz/2);
2978  glVertex2d (sz/2, -sz/2);
2979  glEnd ();
2980  break;
2981  case 'o':
2982  {
2983  double ang_step = M_PI / 5;
2984 
2985  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
2986  for (double ang = 0; ang < (2*M_PI); ang += ang_step)
2987  glVertex2d (sz*cos (ang)/2, sz*sin (ang)/2);
2988  glEnd ();
2989  }
2990  break;
2991  case 'd':
2992  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
2993  glVertex2d (0, -sz/2);
2994  glVertex2d (sz/2, 0);
2995  glVertex2d (0, sz/2);
2996  glVertex2d (-sz/2, 0);
2997  glEnd ();
2998  break;
2999  case 'v':
3000  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
3001  glVertex2f (0, sz/2);
3002  glVertex2f (sz/2, -sz/2);
3003  glVertex2f (-sz/2, -sz/2);
3004  glEnd ();
3005  break;
3006  case '^':
3007  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
3008  glVertex2f (0, -sz/2);
3009  glVertex2f (-sz/2, sz/2);
3010  glVertex2f (sz/2, sz/2);
3011  glEnd ();
3012  break;
3013  case '>':
3014  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
3015  glVertex2f (sz/2, 0);
3016  glVertex2f (-sz/2, sz/2);
3017  glVertex2f (-sz/2, -sz/2);
3018  glEnd ();
3019  break;
3020  case '<':
3021  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
3022  glVertex2f (-sz/2, 0);
3023  glVertex2f (sz/2, -sz/2);
3024  glVertex2f (sz/2, sz/2);
3025  glEnd ();
3026  break;
3027  case 'p':
3028  {
3029  double ang;
3030  double r;
3031  double dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02;
3032 
3033  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
3034  for (int i = 0; i < 2*5; i++)
3035  {
3036  ang = (-0.5 + double(i+1)/5) * M_PI;
3037  r = 1.0 - (dr * fmod (double(i+1), 2.0));
3038  glVertex2d (sz*r*cos (ang)/2, sz*r*sin (ang)/2);
3039  }
3040  glEnd ();
3041  }
3042  break;
3043  case 'h':
3044  {
3045  double ang;
3046  double r;
3047  double dr = 1.0 - 0.5/sin (M_PI/3)*1.02;
3048 
3049  glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
3050  for (int i = 0; i < 2*6; i++)
3051  {
3052  ang = (0.5 + double(i+1)/6.0) * M_PI;
3053  r = 1.0 - (dr * fmod (double(i+1), 2.0));
3054  glVertex2d (sz*r*cos (ang)/2, sz*r*sin (ang)/2);
3055  }
3056  glEnd ();
3057  }
3058  break;
3059  default:
3060  warning ("opengl_renderer: unsupported marker '%s'",
3061  marker.c_str ());
3062  break;
3063  }
3064 
3065  glEndList ();
3066 
3067  return ID;
3068 }
3069 
3070 void
3071 opengl_renderer::text_to_pixels (const std::string& txt,
3072  uint8NDArray& pixels,
3073  Matrix& bbox,
3074  int halign, int valign, double rotation)
3075 {
3076 #if HAVE_FREETYPE
3077  text_renderer.text_to_pixels (txt, pixels, bbox,
3078  halign, valign, rotation, "none");
3079 #endif
3080 }
3081 
3082 Matrix
3083 opengl_renderer::render_text (const std::string& txt,
3084  double x, double y, double z,
3085  int halign, int valign, double rotation)
3086 {
3087 #if HAVE_FREETYPE
3088  if (txt.empty ())
3089  return Matrix (1, 4, 0.0);
3090 
3091  uint8NDArray pixels;
3092  Matrix bbox;
3093  text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
3094 
3095  bool blend = glIsEnabled (GL_BLEND);
3096 
3097  glEnable (GL_BLEND);
3098  glEnable (GL_ALPHA_TEST);
3099  glRasterPos3d (x, y, z);
3100  glBitmap(0, 0, 0, 0, bbox(0), bbox(1), 0);
3101  glDrawPixels (bbox(2), bbox(3),
3102  GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
3103  glDisable (GL_ALPHA_TEST);
3104  if (! blend)
3105  glDisable (GL_BLEND);
3106 
3107  return bbox;
3108 #else
3109  ::warning ("render_text: cannot render text, Freetype library not available");
3110  return Matrix (1, 4, 0.0);
3111 #endif
3112 }
3113 
3114 #endif