diff libinterp/corefcn/gl-render.cc @ 31607:aac27ad79be6 stable

maint: Re-indent code after switch to using namespace macros. * build-env.h, build-env.in.cc, Cell.h, __betainc__.cc, __eigs__.cc, __ftp__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __magick_read__.cc, __pchip_deriv__.cc, amd.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, dasrt.cc, data.cc, debug.cc, defaults.cc, defaults.h, det.cc, display.cc, display.h, dlmread.cc, dynamic-ld.cc, dynamic-ld.h, ellipj.cc, environment.cc, environment.h, error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, gsvd.cc, gtk-manager.cc, gtk-manager.h, help.cc, help.h, hook-fcn.cc, hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h, inv.cc, jsondecode.cc, jsonencode.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-hdf5.cc, ls-mat4.cc, ls-mat5.cc, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex.cc, mexproto.h, mxarray.h, mxtypes.in.h, oct-errno.in.cc, oct-hdf5-types.cc, oct-hist.cc, oct-hist.h, oct-map.cc, oct-map.h, oct-opengl.h, oct-prcstrm.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.h, octave-default-image.h, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-output.cc, psi.cc, qr.cc, quadcc.cc, rand.cc, regexp.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xpow.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfns.cc, svd.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, __delaunayn__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audioread.cc, convhulln.cc, gzip.cc, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc, cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h, cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h, cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc, ov-base-mat.h, ov-base-scalar.cc, ov-base.cc, ov-base.h, ov-bool-mat.cc, ov-bool-mat.h, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.h, ov-cell.cc, ov-ch-mat.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc, ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc, ov-flt-re-mat.cc, ov-flt-re-mat.h, ov-intx.h, ov-java.cc, ov-lazy-idx.cc, ov-legacy-range.cc, ov-magic-int.cc, ov-mex-fcn.cc, ov-mex-fcn.h, ov-null-mat.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc, ov-re-mat.h, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, ovl.h, octave.cc, octave.h, op-b-sbm.cc, op-bm-sbm.cc, op-cs-scm.cc, op-fm-fcm.cc, op-fs-fcm.cc, op-s-scm.cc, op-scm-cs.cc, op-scm-s.cc, op-sm-cs.cc, ops.h, anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, oct-lvalue.cc, oct-lvalue.h, parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h, pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h, idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h, aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h, gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc, lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h, oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc, oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h, randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h, sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc, sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h, file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h, lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h, oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc, oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h, action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h, cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h, lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h, lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc, oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc, oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h: Re-indent code after switch to using namespace macros.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 18:02:15 -0800
parents e88a07dec498
children a74935a6cc75 597f3ee61a48
line wrap: on
line diff
--- a/libinterp/corefcn/gl-render.cc	Thu Dec 01 14:23:45 2022 -0800
+++ b/libinterp/corefcn/gl-render.cc	Thu Dec 01 18:02:15 2022 -0800
@@ -49,4941 +49,4942 @@
 
 #if defined (HAVE_OPENGL)
 
-  static int
-  next_power_of_2 (int n)
-  {
-    int m = 1;
-
-    while (m < n && m < std::numeric_limits<int>::max ())
-      m <<= 1;
-
-    return m;
-  }
+static int
+next_power_of_2 (int n)
+{
+  int m = 1;
+
+  while (m < n && m < std::numeric_limits<int>::max ())
+    m <<= 1;
+
+  return m;
+}
 
 #define LIGHT_MODE GL_FRONT_AND_BACK
 
-  // Use symbolic names for axes
-  enum
-  {
-    X_AXIS,
-    Y_AXIS,
-    Z_AXIS
-  };
-
-  // Use symbolic names for color mode
-  enum
-  {
-    UNIFORM,
-    FLAT,
-    INTERP,
-    TEXTURE
-  };
-
-  // Use symbolic names for lighting
-  enum
-  {
-    NONE,
-    //FLAT,  // Already declared in anonymous enum for color mode
-    GOURAUD = 2
-  };
-
-  // Win32 API requires the CALLBACK attributes for
-  // GLU callback functions.  Define it to empty on
-  // other platforms.
+// Use symbolic names for axes
+enum
+{
+  X_AXIS,
+  Y_AXIS,
+  Z_AXIS
+};
+
+// Use symbolic names for color mode
+enum
+{
+  UNIFORM,
+  FLAT,
+  INTERP,
+  TEXTURE
+};
+
+// Use symbolic names for lighting
+enum
+{
+  NONE,
+  //FLAT,  // Already declared in anonymous enum for color mode
+  GOURAUD = 2
+};
+
+// Win32 API requires the CALLBACK attributes for
+// GLU callback functions.  Define it to empty on
+// other platforms.
 #if ! defined (CALLBACK)
 #  define CALLBACK
 #endif
 
-  class opengl_texture
-  {
-  private:
-
-    class texture_rep
-    {
-    public:
-
-      texture_rep (opengl_functions& glfcns)
-        : m_glfcns (glfcns), m_id (), m_w (), m_h (), m_tw (), m_th (),
-          m_tx (), m_ty (), m_valid (false)
-      { }
-
-      texture_rep (opengl_functions& glfcns, GLuint id, int w, int h,
-                   int tw, int th)
-        : m_glfcns (glfcns), m_id (id), m_w (w), m_h (h), m_tw (tw), m_th (th),
-          m_tx (double(m_w)/m_tw), m_ty (double(m_h)/m_th), m_valid (true)
-      { }
-
-      ~texture_rep (void)
-      {
-        if (m_valid)
-          m_glfcns.glDeleteTextures (1, &m_id);
-      }
-
-      void bind (int mode) const
-      {
-        if (m_valid)
-          m_glfcns.glBindTexture (mode, m_id);
-      }
-
-      void tex_coord (double q, double r) const
-      {
-        if (m_valid)
-          m_glfcns.glTexCoord2d (q*m_tx, r*m_ty);
-      }
-
-      opengl_functions& m_glfcns;
-      GLuint m_id;
-      int m_w, m_h;
-      int m_tw, m_th;
-      double m_tx, m_ty;
-      bool m_valid;
-    };
-
-  public:
-
-    opengl_texture (opengl_functions& glfcns)
-      : m_rep (new texture_rep (glfcns))
-    { }
-
-    opengl_texture (opengl_functions& glfcns, GLuint id, int w, int h,
-                    int tw, int th)
-      : m_rep (new texture_rep (glfcns, id, w, h, tw, th))
-    { }
-
-    opengl_texture (const opengl_texture&) = default;
-
-    ~opengl_texture (void) = default;
-
-    opengl_texture& operator = (const opengl_texture&) = default;
-
-    static opengl_texture create (opengl_functions& glfcns,
-                                  const octave_value& data);
-
-    void bind (int mode = GL_TEXTURE_2D) const { m_rep->bind (mode); }
-
-    void tex_coord (double q, double r) const { m_rep->tex_coord (q, r); }
-
-    bool is_valid (void) const { return m_rep->m_valid; }
-
-  private:
-
-    opengl_texture (const std::shared_ptr<texture_rep>& new_rep)
-      : m_rep (new_rep)
-    { }
-
-    std::shared_ptr<texture_rep> m_rep;
-  };
-
-  opengl_texture
-  opengl_texture::create (opengl_functions& glfcns, const octave_value& data)
-  {
-    opengl_texture retval (glfcns);
-
-    dim_vector dv (data.dims ());
-
-    // Expect RGB data
-    if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4))
-      {
-        // FIXME: dim_vectors hold octave_idx_type values.
-        //        Should we check for dimensions larger than intmax?
-        int h, w, tw, th;
-        h = dv(0), w = dv(1);
-
-        // Return early if the image data are larger than the texture
-        // can hold
-        int max_size;
-        glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size);
-        static bool warned = false;
-        if (h > max_size || w > max_size)
-          {
-            if (! warned)
-              {
-                warning ("opengl_texture::create: the opengl library in use "
-                         "doesn't support images with either dimension larger "
-                         "than %d. Not rendering.", max_size);
-                warned = true;
-              }
-
-            return opengl_texture (glfcns);
-          }
-
-        GLuint id;
-        bool ok = true;
-
-        tw = next_power_of_2 (w);
-        th = next_power_of_2 (h);
-
-        glfcns.glGenTextures (1, &id);
-        glfcns.glBindTexture (GL_TEXTURE_2D, id);
-
-        if (data.is_double_type ())
-          {
-            const NDArray xdata = data.array_value ();
-
-            OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th));
-
-            for (int i = 0; i < h; i++)
-              {
-                for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
-                  {
-                    a[idx]   = xdata(i,j,0);
-                    a[idx+1] = xdata(i,j,1);
-                    a[idx+2] = xdata(i,j,2);
-                  }
-              }
-
-            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB,
-                                 GL_FLOAT, a);
-          }
-
-        else if (data.is_single_type ())
-          {
-            const FloatNDArray xdata = data.float_array_value ();
-
-            OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th));
-
-            for (int i = 0; i < h; i++)
-              {
-                for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
-                  {
-                    a[idx]   = xdata(i,j,0);
-                    a[idx+1] = xdata(i,j,1);
-                    a[idx+2] = xdata(i,j,2);
-                  }
-              }
-
-            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB,
-                                 GL_FLOAT, a);
-          }
-        else if (data.is_uint16_type ())
-          {
-            const uint16NDArray xdata = data.uint16_array_value ();
-
-            OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th));
-
-            for (int i = 0; i < h; i++)
-              {
-                for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
-                  {
-                    a[idx]   = xdata(i,j,0);
-                    a[idx+1] = xdata(i,j,1);
-                    a[idx+2] = xdata(i,j,2);
-                  }
-              }
-
-            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
-                                 GL_RGB, GL_UNSIGNED_SHORT, a);
-          }
-        else if (data.is_uint8_type () && dv(2) == 3)
-          {
-            const uint8NDArray xdata = data.uint8_array_value ();
-
-            OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th));
-
-            for (int i = 0; i < h; i++)
-              {
-                for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
-                  {
-                    a[idx]   = xdata(i,j,0);
-                    a[idx+1] = xdata(i,j,1);
-                    a[idx+2] = xdata(i,j,2);
-                  }
-              }
-
-            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
-                                 GL_RGB, GL_UNSIGNED_BYTE, a);
-          }
-        else if (data.is_uint8_type () && dv(2) == 4)
-          {
-            const uint8NDArray xdata = data.uint8_array_value ();
-
-            OCTAVE_LOCAL_BUFFER (GLubyte, a, (4*tw*th));
-
-            for (int i = 0; i < h; i++)
-              {
-                for (int j = 0, idx = i*tw*4; j < w; j++, idx += 4)
-                  {
-                    a[idx]   = xdata(i,j,0);
-                    a[idx+1] = xdata(i,j,1);
-                    a[idx+2] = xdata(i,j,2);
-                    a[idx+3] = xdata(i,j,3);
-                  }
-              }
-
-            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0,
-                                 GL_RGBA, GL_UNSIGNED_BYTE, a);
-          }
-        else
-          {
-            ok = false;
-            warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16");
-          }
-
-        if (ok)
-          {
-            glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
-                                    GL_NEAREST);
-            glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
-                                    GL_NEAREST);
-
-            if (glfcns.glGetError () != GL_NO_ERROR)
-              warning ("opengl_texture::create: OpenGL error while generating texture data");
-            else
-              retval = opengl_texture (glfcns, id, w, h, tw, th);
-          }
-      }
-    else
-      warning ("opengl_texture::create: invalid texture data size");
-
-    return retval;
-  }
-
-  class
-  opengl_tessellator
+class opengl_texture
+{
+private:
+
+  class texture_rep
   {
   public:
-#if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS)
-    typedef GLvoid (CALLBACK *fcn) (...);
-#else
-    typedef void (CALLBACK *fcn) (void);
-#endif
-
-  public:
-
-    opengl_tessellator (void) : m_glu_tess (nullptr), m_fill () { init (); }
-
-    // No copying!
-
-    opengl_tessellator (const opengl_tessellator&) = delete;
-
-    opengl_tessellator operator = (const opengl_tessellator&) = delete;
-
-    virtual ~opengl_tessellator (void)
-    { if (m_glu_tess) gluDeleteTess (m_glu_tess); }
-
-    void begin_polygon (bool filled = true)
+
+    texture_rep (opengl_functions& glfcns)
+      : m_glfcns (glfcns), m_id (), m_w (), m_h (), m_tw (), m_th (),
+        m_tx (), m_ty (), m_valid (false)
+    { }
+
+    texture_rep (opengl_functions& glfcns, GLuint id, int w, int h,
+                 int tw, int th)
+      : m_glfcns (glfcns), m_id (id), m_w (w), m_h (h), m_tw (tw), m_th (th),
+        m_tx (double(m_w)/m_tw), m_ty (double(m_h)/m_th), m_valid (true)
+    { }
+
+    ~texture_rep (void)
     {
-      gluTessProperty (m_glu_tess, GLU_TESS_BOUNDARY_ONLY,
-                       (filled ? GL_FALSE : GL_TRUE));
-      m_fill = filled;
-      gluTessBeginPolygon (m_glu_tess, this);
+      if (m_valid)
+        m_glfcns.glDeleteTextures (1, &m_id);
+    }
+
+    void bind (int mode) const
+    {
+      if (m_valid)
+        m_glfcns.glBindTexture (mode, m_id);
+    }
+
+    void tex_coord (double q, double r) const
+    {
+      if (m_valid)
+        m_glfcns.glTexCoord2d (q*m_tx, r*m_ty);
     }
 
-    void end_polygon (void) const
-    { gluTessEndPolygon (m_glu_tess); }
-
-    void begin_contour (void) const
-    { gluTessBeginContour (m_glu_tess); }
-
-    void end_contour (void) const
-    { gluTessEndContour (m_glu_tess); }
-
-    void add_vertex (double *loc, void *data) const
-    { gluTessVertex (m_glu_tess, loc, data); }
-
-  protected:
-    virtual void begin (GLenum /*type*/) { }
-
-    virtual void end (void) { }
-
-    virtual void vertex (void * /*data*/) { }
-
-    virtual void combine (GLdouble [3] /*c*/, void * [4] /*data*/,
-                          GLfloat  [4] /*w*/, void ** /*out_data*/) { }
-
-    virtual void edge_flag (GLboolean /*flag*/) { }
-
-    virtual void error (GLenum err)
-    { ::error ("OpenGL tessellation error (%d)", err); }
-
-    virtual void init (void)
-    {
-      m_glu_tess = gluNewTess ();
-
-      gluTessCallback (m_glu_tess, GLU_TESS_BEGIN_DATA,
-                       reinterpret_cast<fcn> (tess_begin));
-      gluTessCallback (m_glu_tess, GLU_TESS_END_DATA,
-                       reinterpret_cast<fcn> (tess_end));
-      gluTessCallback (m_glu_tess, GLU_TESS_VERTEX_DATA,
-                       reinterpret_cast<fcn> (tess_vertex));
-      gluTessCallback (m_glu_tess, GLU_TESS_COMBINE_DATA,
-                       reinterpret_cast<fcn> (tess_combine));
-      gluTessCallback (m_glu_tess, GLU_TESS_EDGE_FLAG_DATA,
-                       reinterpret_cast<fcn> (tess_edge_flag));
-      gluTessCallback (m_glu_tess, GLU_TESS_ERROR_DATA,
-                       reinterpret_cast<fcn> (tess_error));
-    }
-
-    bool is_filled (void) const { return m_fill; }
-
-  private:
-    static void CALLBACK tess_begin (GLenum type, void *t)
-    { reinterpret_cast<opengl_tessellator *> (t)->begin (type); }
-
-    static void CALLBACK tess_end (void *t)
-    { reinterpret_cast<opengl_tessellator *> (t)->end (); }
-
-    static void CALLBACK tess_vertex (void *v, void *t)
-    { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); }
-
-    static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4],
-                                       void **out,  void *t)
-    { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); }
-
-    static void CALLBACK tess_edge_flag (GLboolean flag, void *t)
-    { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); }
-
-    static void CALLBACK tess_error (GLenum err, void *t)
-    { reinterpret_cast<opengl_tessellator *> (t)->error (err); }
-
-    //--------
-
-    GLUtesselator *m_glu_tess;
-    bool m_fill;
+    opengl_functions& m_glfcns;
+    GLuint m_id;
+    int m_w, m_h;
+    int m_tw, m_th;
+    double m_tx, m_ty;
+    bool m_valid;
   };
 
-  class vertex_data
-  {
-  public:
-
-    class vertex_data_rep
+public:
+
+  opengl_texture (opengl_functions& glfcns)
+    : m_rep (new texture_rep (glfcns))
+  { }
+
+  opengl_texture (opengl_functions& glfcns, GLuint id, int w, int h,
+                  int tw, int th)
+    : m_rep (new texture_rep (glfcns, id, w, h, tw, th))
+  { }
+
+  opengl_texture (const opengl_texture&) = default;
+
+  ~opengl_texture (void) = default;
+
+  opengl_texture& operator = (const opengl_texture&) = default;
+
+  static opengl_texture create (opengl_functions& glfcns,
+                                const octave_value& data);
+
+  void bind (int mode = GL_TEXTURE_2D) const { m_rep->bind (mode); }
+
+  void tex_coord (double q, double r) const { m_rep->tex_coord (q, r); }
+
+  bool is_valid (void) const { return m_rep->m_valid; }
+
+private:
+
+  opengl_texture (const std::shared_ptr<texture_rep>& new_rep)
+    : m_rep (new_rep)
+  { }
+
+  std::shared_ptr<texture_rep> m_rep;
+};
+
+opengl_texture
+opengl_texture::create (opengl_functions& glfcns, const octave_value& data)
+{
+  opengl_texture retval (glfcns);
+
+  dim_vector dv (data.dims ());
+
+  // Expect RGB data
+  if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4))
     {
-    public:
-
-      vertex_data_rep (void)
-        : m_coords (), m_color (), m_vertex_normal (), m_face_normal (),
-          m_alpha (), m_ambient (), m_diffuse (), m_specular (),
-          m_specular_exp (), m_specular_color_refl ()
-      { }
-
-      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)
-        : m_coords (c), m_color (col), m_vertex_normal (vn),
-          m_face_normal (fn), m_alpha (a), m_ambient (as), m_diffuse (ds),
-          m_specular (ss), m_specular_exp (se), m_specular_color_refl (scr)
-      { }
-
-      Matrix m_coords;
-      Matrix m_color;
-      Matrix m_vertex_normal;
-      Matrix m_face_normal;
-      double m_alpha;
-      float m_ambient;
-      float m_diffuse;
-      float m_specular;
-      float m_specular_exp;
-      float m_specular_color_refl;
-    };
-
-  public:
-
-    // Required to instantiate std::list<vertex_data> objects.
-    vertex_data (void) : m_rep (nil_rep ()) { }
-
-    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)
-      : m_rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr))
-    { }
-
-    vertex_data (const vertex_data&) = default;
-
-    ~vertex_data (void) = default;
-
-    vertex_data& operator = (const vertex_data&) = default;
-
-    vertex_data_rep * get_rep (void) const { return m_rep.get (); }
-
-  private:
-
-    static std::shared_ptr<vertex_data_rep> nil_rep (void)
-    {
-      static std::shared_ptr<vertex_data_rep> nr (new vertex_data_rep ());
-
-      return nr;
-    }
-
-    std::shared_ptr<vertex_data_rep> m_rep;
-  };
-
-  class
-  opengl_renderer::patch_tessellator : public opengl_tessellator
-  {
-  public:
-    patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl,
-                       float idx = 0.0)
-      : opengl_tessellator (), m_renderer (r),
-        m_color_mode (cmode), m_light_mode (lmode), m_face_lighting (fl),
-        m_index (idx), m_first (true), m_tmp_vdata ()
-    { }
-
-  protected:
-    void begin (GLenum type)
-    {
-      opengl_functions& glfcns = m_renderer->get_opengl_functions ();
-
-      //printf ("patch_tessellator::begin (%d)\n", type);
-      m_first = true;
-
-      if (m_color_mode == INTERP || m_light_mode == GOURAUD)
-        glfcns.glShadeModel (GL_SMOOTH);
-      else
-        glfcns.glShadeModel (GL_FLAT);
-
-      if (is_filled ())
-        m_renderer->set_polygon_offset (true, m_index);
-
-      glfcns.glBegin (type);
-    }
-
-    void end (void)
-    {
-      opengl_functions& glfcns = m_renderer->get_opengl_functions ();
-
-      //printf ("patch_tessellator::end\n");
-      glfcns.glEnd ();
-      m_renderer->set_polygon_offset (false);
-    }
-
-    void vertex (void *data)
-    {
-      opengl_functions& glfcns = m_renderer->get_opengl_functions ();
-
-      vertex_data::vertex_data_rep *v
-        = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
-      //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->m_coords(0), v->m_coords(1), v->m_coords(2));
-
-      // NOTE: OpenGL can re-order vertices.  For "flat" coloring of FaceColor
-      // the first vertex must be identified in the draw_patch routine.
-
-      if (m_color_mode == INTERP || (m_color_mode == FLAT && ! is_filled ()))
+      // FIXME: dim_vectors hold octave_idx_type values.
+      //        Should we check for dimensions larger than intmax?
+      int h, w, tw, th;
+      h = dv(0), w = dv(1);
+
+      // Return early if the image data are larger than the texture
+      // can hold
+      int max_size;
+      glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size);
+      static bool warned = false;
+      if (h > max_size || w > max_size)
+        {
+          if (! warned)
+            {
+              warning ("opengl_texture::create: the opengl library in use "
+                       "doesn't support images with either dimension larger "
+                       "than %d. Not rendering.", max_size);
+              warned = true;
+            }
+
+          return opengl_texture (glfcns);
+        }
+
+      GLuint id;
+      bool ok = true;
+
+      tw = next_power_of_2 (w);
+      th = next_power_of_2 (h);
+
+      glfcns.glGenTextures (1, &id);
+      glfcns.glBindTexture (GL_TEXTURE_2D, id);
+
+      if (data.is_double_type ())
+        {
+          const NDArray xdata = data.array_value ();
+
+          OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th));
+
+          for (int i = 0; i < h; i++)
+            {
+              for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
+                {
+                  a[idx]   = xdata(i, j, 0);
+                  a[idx+1] = xdata(i, j, 1);
+                  a[idx+2] = xdata(i, j, 2);
+                }
+            }
+
+          glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB,
+                               GL_FLOAT, a);
+        }
+
+      else if (data.is_single_type ())
         {
-          Matrix col = v->m_color;
-
-          if (col.numel () == 3)
+          const FloatNDArray xdata = data.float_array_value ();
+
+          OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th));
+
+          for (int i = 0; i < h; i++)
             {
-              glfcns.glColor4d (col(0), col(1), col(2), v->m_alpha);
-              if (m_light_mode > 0)
+              for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
+                {
+                  a[idx]   = xdata(i, j, 0);
+                  a[idx+1] = xdata(i, j, 1);
+                  a[idx+2] = xdata(i, j, 2);
+                }
+            }
+
+          glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB,
+                               GL_FLOAT, a);
+        }
+      else if (data.is_uint16_type ())
+        {
+          const uint16NDArray xdata = data.uint16_array_value ();
+
+          OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th));
+
+          for (int i = 0; i < h; i++)
+            {
+              for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
                 {
-                  // edge lighting only uses ambient light
-                  float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };;
-
-                  if (m_face_lighting)
-                    for (int k = 0; k < 3; k++)
-                      buf[k] = (v->m_specular
-                                * (v->m_specular_color_refl +
-                                   (1 - v->m_specular_color_refl) * col(k)));
-                  glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
-
-                  if (m_face_lighting)
-                    for (int k = 0; k < 3; k++)
-                      buf[k] = (v->m_diffuse * col(k));
-                  glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf);
-
-                  for (int k = 0; k < 3; k++)
-                    buf[k] = (v->m_ambient * col(k));
-                  glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
+                  a[idx]   = xdata(i, j, 0);
+                  a[idx+1] = xdata(i, j, 1);
+                  a[idx+2] = xdata(i, j, 2);
+                }
+            }
+
+          glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
+                               GL_RGB, GL_UNSIGNED_SHORT, a);
+        }
+      else if (data.is_uint8_type () && dv(2) == 3)
+        {
+          const uint8NDArray xdata = data.uint8_array_value ();
+
+          OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th));
+
+          for (int i = 0; i < h; i++)
+            {
+              for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3)
+                {
+                  a[idx]   = xdata(i, j, 0);
+                  a[idx+1] = xdata(i, j, 1);
+                  a[idx+2] = xdata(i, j, 2);
+                }
+            }
+
+          glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
+                               GL_RGB, GL_UNSIGNED_BYTE, a);
+        }
+      else if (data.is_uint8_type () && dv(2) == 4)
+        {
+          const uint8NDArray xdata = data.uint8_array_value ();
+
+          OCTAVE_LOCAL_BUFFER (GLubyte, a, (4*tw*th));
+
+          for (int i = 0; i < h; i++)
+            {
+              for (int j = 0, idx = i*tw*4; j < w; j++, idx += 4)
+                {
+                  a[idx]   = xdata(i, j, 0);
+                  a[idx+1] = xdata(i, j, 1);
+                  a[idx+2] = xdata(i, j, 2);
+                  a[idx+3] = xdata(i, j, 3);
                 }
             }
+
+          glfcns.glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0,
+                               GL_RGBA, GL_UNSIGNED_BYTE, a);
         }
-
-      if (m_light_mode == FLAT && m_first)
-        glfcns.glNormal3dv (v->m_face_normal.data ());
-      else if (m_light_mode == GOURAUD)
-        glfcns.glNormal3dv (v->m_vertex_normal.data ());
-
-      glfcns.glVertex3dv (v->m_coords.data ());
-
-      m_first = false;
-    }
-
-    void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data)
-    {
-      vertex_data::vertex_data_rep *v[4];
-      int vmax = 4;
-
-      for (int i = 0; i < 4; i++)
+      else
         {
-          v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]);
-
-          if (vmax == 4 && ! v[i])
-            vmax = i;
+          ok = false;
+          warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16");
         }
 
-      Matrix vv (1, 3, 0.0);
-      Matrix cc;
-      Matrix vnn (1, 3, 0.0);
-      Matrix fnn (1, 3, 0.0);
-      double aa = 0.0;
-
-      vv(0) = xyz[0];
-      vv(1) = xyz[1];
-      vv(2) = xyz[2];
-
-      if (v[0]->m_color.numel ())
-        {
-          cc.resize (1, 3, 0.0);
-          for (int ic = 0; ic < 3; ic++)
-            for (int iv = 0; iv < vmax; iv++)
-              cc(ic) += (w[iv] * v[iv]->m_color (ic));
-        }
-
-      if (v[0]->m_vertex_normal.numel () > 0)
+      if (ok)
         {
-          for (int in = 0; in < 3; in++)
-            for (int iv = 0; iv < vmax; iv++)
-              vnn(in) += (w[iv] * v[iv]->m_vertex_normal (in));
-        }
-
-      if (v[0]->m_face_normal.numel () > 0)
-        {
-          for (int in = 0; in < 3; in++)
-            for (int iv = 0; iv < vmax; iv++)
-              fnn(in) += (w[iv] * v[iv]->m_face_normal (in));
+          glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                                  GL_NEAREST);
+          glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+                                  GL_NEAREST);
+
+          if (glfcns.glGetError () != GL_NO_ERROR)
+            warning ("opengl_texture::create: OpenGL error while generating texture data");
+          else
+            retval = opengl_texture (glfcns, id, w, h, tw, th);
         }
-
-      for (int iv = 0; iv < vmax; iv++)
-        aa += (w[iv] * v[iv]->m_alpha);
-
-      vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->m_ambient, v[0]->m_diffuse,
-                         v[0]->m_specular, v[0]->m_specular_exp,
-                         v[0]->m_specular_color_refl);
-      m_tmp_vdata.push_back (new_v);
-
-      *out_data = new_v.get_rep ();
     }
-
-  private:
-
-    // No copying!
-
-    patch_tessellator (const patch_tessellator&) = delete;
-
-    patch_tessellator& operator = (const patch_tessellator&) = delete;
-
-    opengl_renderer *m_renderer;
-    int m_color_mode;
-    int m_light_mode;
-    bool m_face_lighting;
-    int m_index;
-    bool m_first;
-    std::list<vertex_data> m_tmp_vdata;
-  };
-
+  else
+    warning ("opengl_texture::create: invalid texture data size");
+
+  return retval;
+}
+
+class
+opengl_tessellator
+{
+public:
+#if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS)
+  typedef GLvoid (CALLBACK *fcn) (...);
 #else
-
-  class
-  opengl_renderer::patch_tessellator
-  {
-    // Dummy class.
-  };
-
+  typedef void (CALLBACK *fcn) (void);
 #endif
 
-  opengl_renderer::opengl_renderer (opengl_functions& glfcns)
-    : m_glfcns (glfcns), m_xmin (), m_xmax (), m_ymin (), m_ymax (),
-      m_zmin (), m_zmax (), m_devpixratio (1.0), m_xform (), m_toolkit (),
-      m_xZ1 (), m_xZ2 (), m_marker_id (), m_filled_marker_id (),
-      m_camera_pos (), m_camera_dir (), m_view_vector (),
-      m_interpreter ("none"), m_txt_renderer (), m_current_light (0),
-      m_max_lights (0), m_selecting (false), m_printing (false)
-  {
-    // This constructor will fail if we don't have OpenGL or if the data
-    // types we assumed in our public interface aren't compatible with the
-    // OpenGL types.
-
-#if defined (HAVE_OPENGL)
-
-    // Ensure that we can't request an image larger than OpenGL can handle.
-    // FIXME: should we check signed vs. unsigned?
-
-    static bool ok = (sizeof (int) <= sizeof (GLsizei));
-
-    if (! ok)
-      error ("the size of GLsizei is smaller than the size of int");
-
-#else
-
-    err_disabled_feature ("opengl_renderer", "OpenGL");
-
-#endif
-  }
-
-  void
-  opengl_renderer::draw (const graphics_object& go, bool toplevel)
-  {
-    if (! go.valid_object ())
-      return;
-
-    const base_properties& props = go.get_properties ();
-
-    if (! m_toolkit)
-      m_toolkit = props.get_toolkit ();
-
-    if (go.isa ("figure"))
-      draw_figure (dynamic_cast<const figure::properties&> (props));
-    else if (go.isa ("axes"))
-      draw_axes (dynamic_cast<const axes::properties&> (props));
-    else if (go.isa ("line"))
-      draw_line (dynamic_cast<const line::properties&> (props));
-    else if (go.isa ("surface"))
-      draw_surface (dynamic_cast<const surface::properties&> (props));
-    else if (go.isa ("patch"))
-      draw_patch (dynamic_cast<const patch::properties&> (props));
-    else if (go.isa ("scatter"))
-      draw_scatter (dynamic_cast<const scatter::properties&> (props));
-    else if (go.isa ("light"))
-      draw_light (dynamic_cast<const light::properties&> (props));
-    else if (go.isa ("hggroup"))
-      draw_hggroup (dynamic_cast<const hggroup::properties&> (props));
-    else if (go.isa ("text"))
-      draw_text (dynamic_cast<const text::properties&> (props));
-    else if (go.isa ("image"))
-      draw_image (dynamic_cast<const image::properties&> (props));
-    else if (go.isa ("uimenu") || go.isa ("uicontrol")
-             || go.isa ("uicontextmenu") || go.isa ("uitoolbar")
-             || go.isa ("uipushtool") || go.isa ("uitoggletool")
-             || go.isa ("uitable"))
-      ; // SKIP
-    else if (go.isa ("uipanel"))
-      {
-        if (toplevel)
-          draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go);
-      }
-    else if (go.isa ("uibuttongroup"))
-      {
-        if (toplevel)
-          draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go);
-      }
-    else
-      {
-        warning ("opengl_renderer: cannot render object of type '%s'",
-                 props.graphics_object_name ().c_str ());
-      }
-
-#if defined (HAVE_OPENGL)
-
-    GLenum gl_error = m_glfcns.glGetError ();
-    if (gl_error)
-      warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object",
-               gluErrorString (gl_error), gl_error,
-               props.graphics_object_name ().c_str ());
-
-#endif
-  }
-
-  void
-  opengl_renderer::draw_figure (const figure::properties& props)
-  {
-    m_printing = props.is___printing__ ();
-
-    // Initialize OpenGL context
-    init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ());
-
-#if defined (HAVE_OPENGL)
-
-    props.set___gl_extensions__ (get_string (GL_EXTENSIONS));
-    props.set___gl_renderer__ (get_string (GL_RENDERER));
-    props.set___gl_vendor__ (get_string (GL_VENDOR));
-    props.set___gl_version__ (get_string (GL_VERSION));
-
-#endif
-
-    // Draw children
-
-    draw (props.get_all_children (), false);
-  }
-
-  void
-  opengl_renderer::draw_uipanel (const uipanel::properties& props,
-                                 const graphics_object& go)
-  {
-    graphics_object fig = go.get_ancestor ("figure");
-    const figure::properties& figProps
-      = dynamic_cast<const figure::properties&> (fig.get_properties ());
-
-    // Initialize OpenGL context
-
-    init_gl_context (figProps.is_graphicssmoothing (),
-                     props.get_backgroundcolor_rgb ());
-
-    // Draw children
-
-    draw (props.get_all_children (), false);
-  }
-
-  void
-  opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props,
-                                       const graphics_object& go)
+public:
+
+  opengl_tessellator (void) : m_glu_tess (nullptr), m_fill () { init (); }
+
+  // No copying!
+
+  opengl_tessellator (const opengl_tessellator&) = delete;
+
+  opengl_tessellator operator = (const opengl_tessellator&) = delete;
+
+  virtual ~opengl_tessellator (void)
+  { if (m_glu_tess) gluDeleteTess (m_glu_tess); }
+
+  void begin_polygon (bool filled = true)
   {
-    graphics_object fig = go.get_ancestor ("figure");
-    const figure::properties& figProps
-      = dynamic_cast<const figure::properties&> (fig.get_properties ());
-
-    // Initialize OpenGL context
-
-    init_gl_context (figProps.is_graphicssmoothing (),
-                     props.get_backgroundcolor_rgb ());
-
-    // Draw children
-
-    draw (props.get_all_children (), false);
+    gluTessProperty (m_glu_tess, GLU_TESS_BOUNDARY_ONLY,
+                     (filled ? GL_FALSE : GL_TRUE));
+    m_fill = filled;
+    gluTessBeginPolygon (m_glu_tess, this);
   }
 
-  void
-  opengl_renderer::init_gl_context (bool enhanced, const Matrix& c)
-  {
-#if defined (HAVE_OPENGL)
-
-    // Initialize OpenGL context
-
-    m_glfcns.glEnable (GL_DEPTH_TEST);
-    m_glfcns.glDepthFunc (GL_LEQUAL);
-    m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    m_glfcns.glAlphaFunc (GL_GREATER, 0.0f);
-    m_glfcns.glEnable (GL_NORMALIZE);
-    m_glfcns.glEnable (GL_BLEND);
-
-    if (enhanced)
-      {
-        m_glfcns.glEnable (GL_MULTISAMPLE);
-        bool has_multisample = false;
-        if (! m_glfcns.glGetError ())
-          {
-            GLint iMultiSample, iNumSamples;
-            m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample);
-            m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples);
-            if (iMultiSample == GL_TRUE && iNumSamples > 0)
-              has_multisample = true;
-          }
-
-        if (! has_multisample)
-          {
-            // MultiSample not implemented.  Use old-style anti-aliasing
-            m_glfcns.glDisable (GL_MULTISAMPLE);
-            // Disabling GL_MULTISAMPLE will raise a gl error if it is not
-            // implemented.  Thus, call glGetError to reset the error state.
-            m_glfcns.glGetError ();
-
-            m_glfcns.glEnable (GL_LINE_SMOOTH);
-            m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
-          }
-      }
-    else
-      {
-        m_glfcns.glDisable (GL_LINE_SMOOTH);
-      }
-
-    // Clear background
-
-    if (c.numel () >= 3)
-      {
-        m_glfcns.glClearColor (c(0), c(1), c(2), 1);
-        m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-      }
-
-    GLenum gl_error = m_glfcns.glGetError ();
-    if (gl_error)
-      warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context",
-               gluErrorString (gl_error), gl_error);
-
-#else
-
-    octave_unused_parameter (enhanced);
-    octave_unused_parameter (c);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::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)
+  void end_polygon (void) const
+  { gluTessEndPolygon (m_glu_tess); }
+
+  void begin_contour (void) const
+  { gluTessBeginContour (m_glu_tess); }
+
+  void end_contour (void) const
+  { gluTessEndContour (m_glu_tess); }
+
+  void add_vertex (double *loc, void *data) const
+  { gluTessVertex (m_glu_tess, loc, data); }
+
+protected:
+  virtual void begin (GLenum /*type*/) { }
+
+  virtual void end (void) { }
+
+  virtual void vertex (void * /*data*/) { }
+
+  virtual void combine (GLdouble [3] /*c*/, void *[4] /*data*/,
+                        GLfloat  [4] /*w*/, void ** /*out_data*/) { }
+
+  virtual void edge_flag (GLboolean /*flag*/) { }
+
+  virtual void error (GLenum err)
+  { ::error ("OpenGL tessellation error (%d)", err); }
+
+  virtual void init (void)
   {
-#if defined (HAVE_OPENGL)
-
-    m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha);
-    set_linestyle (gridstyle, true, linewidth);
-    m_glfcns.glBegin (GL_LINES);
-    for (int i = 0; i < ticks.numel (); i++)
-      {
-        double val = ticks(i);
-        if (lim1 <= val && val <= lim2)
-          {
-            if (xyz == X_AXIS)
-              {
-                m_glfcns.glVertex3d (val, p1N, p2);
-                m_glfcns.glVertex3d (val, p1, p2);
-                if (is_3D)
-                  {
-                    m_glfcns.glVertex3d (val, p1, p2N);
-                    m_glfcns.glVertex3d (val, p1, p2);
-                  }
-              }
-            else if (xyz == Y_AXIS)
-              {
-                m_glfcns.glVertex3d (p1N, val, p2);
-                m_glfcns.glVertex3d (p1, val, p2);
-                if (is_3D)
-                  {
-                    m_glfcns.glVertex3d (p1, val, p2N);
-                    m_glfcns.glVertex3d (p1, val, p2);
-                  }
-              }
-            else if (xyz == Z_AXIS)
-              {
-                m_glfcns.glVertex3d (p1N, p2, val);
-                m_glfcns.glVertex3d (p1, p2, val);
-                m_glfcns.glVertex3d (p1, p2N, val);
-                m_glfcns.glVertex3d (p1, p2, val);
-              }
-          }
-      }
-    m_glfcns.glEnd ();
-    set_linestyle ("-");  // Disable LineStipple
-    double black[3] = {0, 0, 0};
-    m_glfcns.glColor3dv (black);
-
-#else
-
-    octave_unused_parameter (linewidth);
-    octave_unused_parameter (gridstyle);
-    octave_unused_parameter (gridcolor);
-    octave_unused_parameter (gridalpha);
-    octave_unused_parameter (ticks);
-    octave_unused_parameter (lim1);
-    octave_unused_parameter (lim2);
-    octave_unused_parameter (p1);
-    octave_unused_parameter (p1N);
-    octave_unused_parameter (p2);
-    octave_unused_parameter (p2N);
-    octave_unused_parameter (xyz);
-    octave_unused_parameter (is_3D);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::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)
-  {
-#if defined (HAVE_OPENGL)
-
-    m_glfcns.glBegin (GL_LINES);
-
-    for (int i = 0; i < ticks.numel (); i++)
-      {
-        double val = ticks(i);
-
-        if (lim1 <= val && val <= lim2)
-          {
-            if (xyz == X_AXIS)
-              {
-                m_glfcns.glVertex3d (val, p1, p2);
-                m_glfcns.glVertex3d (val, p1+dy, p2+dz);
-                if (mirror)
-                  {
-                    m_glfcns.glVertex3d (val, p1N, p2N);
-                    m_glfcns.glVertex3d (val, p1N-dy, p2N-dz);
-                  }
-              }
-            else if (xyz == Y_AXIS)
-              {
-                m_glfcns.glVertex3d (p1, val, p2);
-                m_glfcns.glVertex3d (p1+dx, val, p2+dz);
-                if (mirror)
-                  {
-                    m_glfcns.glVertex3d (p1N, val, p2N);
-                    m_glfcns.glVertex3d (p1N-dx, val, p2N-dz);
-                  }
-              }
-            else if (xyz == Z_AXIS)
-              {
-                m_glfcns.glVertex3d (p1, p2, val);
-                m_glfcns.glVertex3d (p1+dx, p2+dy, val);
-                if (mirror)
-                  {
-                    m_glfcns.glVertex3d (p1N, p2N, val);
-                    m_glfcns.glVertex3d (p1N-dx, p2N-dy, val);
-                  }
-              }
-          }
-      }
-
-    m_glfcns.glEnd ();
-
-#else
-
-    octave_unused_parameter (ticks);
-    octave_unused_parameter (lim1);
-    octave_unused_parameter (lim2);
-    octave_unused_parameter (p1);
-    octave_unused_parameter (p1N);
-    octave_unused_parameter (p2);
-    octave_unused_parameter (p2N);
-    octave_unused_parameter (dx);
-    octave_unused_parameter (dy);
-    octave_unused_parameter (dz);
-    octave_unused_parameter (xyz);
-    octave_unused_parameter (mirror);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
+    m_glu_tess = gluNewTess ();
+
+    gluTessCallback (m_glu_tess, GLU_TESS_BEGIN_DATA,
+                     reinterpret_cast<fcn> (tess_begin));
+    gluTessCallback (m_glu_tess, GLU_TESS_END_DATA,
+                     reinterpret_cast<fcn> (tess_end));
+    gluTessCallback (m_glu_tess, GLU_TESS_VERTEX_DATA,
+                     reinterpret_cast<fcn> (tess_vertex));
+    gluTessCallback (m_glu_tess, GLU_TESS_COMBINE_DATA,
+                     reinterpret_cast<fcn> (tess_combine));
+    gluTessCallback (m_glu_tess, GLU_TESS_EDGE_FLAG_DATA,
+                     reinterpret_cast<fcn> (tess_edge_flag));
+    gluTessCallback (m_glu_tess, GLU_TESS_ERROR_DATA,
+                     reinterpret_cast<fcn> (tess_error));
   }
 
-  void
-  opengl_renderer::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)
-  {
-#if defined (HAVE_OPENGL)
-
-    int nticks  = ticks.numel ();
-    int nlabels = ticklabels.numel ();
-
-    if (nlabels == 0)
-      return;
-
-    for (int i = 0; i < nticks; i++)
-      {
-        double val = ticks(i);
-
-        if (lim1 <= val && val <= lim2)
-          {
-            Matrix b;
-
-            std::string label (ticklabels(i % nlabels));
-            label.erase (0, label.find_first_not_of (' '));
-            label = label.substr (0, label.find_last_not_of (' ')+1);
-
-            // FIXME: As tick text is transparent, shouldn't it be
-            //        drawn after axes object, for correct rendering?
-            if (xyz == X_AXIS)
-              {
-                b = render_text (label, val, p1, p2, ha, va);
-              }
-            else if (xyz == Y_AXIS)
-              {
-                b = render_text (label, p1, val, p2, ha, va);
-              }
-            else if (xyz == Z_AXIS)
-              {
-                b = render_text (label, p1, p2, val, ha, va);
-              }
-
-            wmax = std::max (wmax, static_cast<int> (b(2)));
-            hmax = std::max (hmax, static_cast<int> (b(3)));
-          }
-      }
-
-#else
-
-    octave_unused_parameter (ticks);
-    octave_unused_parameter (ticklabels);
-    octave_unused_parameter (lim1);
-    octave_unused_parameter (lim2);
-    octave_unused_parameter (p1);
-    octave_unused_parameter (p2);
-    octave_unused_parameter (xyz);
-    octave_unused_parameter (ha);
-    octave_unused_parameter (va);
-    octave_unused_parameter (wmax);
-    octave_unused_parameter (hmax);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2)
-  {
-#if defined (HAVE_OPENGL)
-
-    m_glfcns.glVertex2d (x1, y1);
-    m_glfcns.glVertex2d (x2, y1);
-    m_glfcns.glVertex2d (x2, y2);
-    m_glfcns.glVertex2d (x1, y2);
-    m_glfcns.glVertex2d (x1, y1);
-
-#else
-
-    octave_unused_parameter (x1);
-    octave_unused_parameter (x2);
-    octave_unused_parameter (y1);
-    octave_unused_parameter (y2);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::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)
+  bool is_filled (void) const { return m_fill; }
+
+private:
+  static void CALLBACK tess_begin (GLenum type, void *t)
+  { reinterpret_cast<opengl_tessellator *> (t)->begin (type); }
+
+  static void CALLBACK tess_end (void *t)
+  { reinterpret_cast<opengl_tessellator *> (t)->end (); }
+
+  static void CALLBACK tess_vertex (void *v, void *t)
+  { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); }
+
+  static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4],
+                                     void **out,  void *t)
+  { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); }
+
+  static void CALLBACK tess_edge_flag (GLboolean flag, void *t)
+  { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); }
+
+  static void CALLBACK tess_error (GLenum err, void *t)
+  { reinterpret_cast<opengl_tessellator *> (t)->error (err); }
+
+  //--------
+
+  GLUtesselator *m_glu_tess;
+  bool m_fill;
+};
+
+class vertex_data
+{
+public:
+
+  class vertex_data_rep
   {
-#if defined (HAVE_OPENGL)
-
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
-
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
-    m_glfcns.glOrtho (0, width, height, 0, 1, -1);
-
-    m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
-    m_glfcns.glDisable (GL_DEPTH_TEST);
-
-    m_glfcns.glBegin (GL_POLYGON);
-    m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2),
-                        overlayalpha);
-    draw_zoom_rect (x1, y1, x2, y2);
-    m_glfcns.glEnd ();
-
-    m_glfcns.glLineWidth (borderwidth);
-    m_glfcns.glBegin (GL_LINE_STRIP);
-    m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2),
-                        borderalpha);
-    draw_zoom_rect (x1, y1, x2, y2);
-    m_glfcns.glEnd ();
-
-    m_glfcns.glPopAttrib ();
-
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPopMatrix ();
-
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPopMatrix ();
-
-#else
-
-    octave_unused_parameter (width);
-    octave_unused_parameter (height);
-    octave_unused_parameter (x1);
-    octave_unused_parameter (x2);
-    octave_unused_parameter (y1);
-    octave_unused_parameter (y2);
-    octave_unused_parameter (overlaycolor);
-    octave_unused_parameter (overlayalpha);
-    octave_unused_parameter (bordercolor);
-    octave_unused_parameter (borderalpha);
-    octave_unused_parameter (borderwidth);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  uint8NDArray
-  opengl_renderer::get_pixels (int width, int height)
-  {
-#if defined (HAVE_OPENGL)
-
-    m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1);
-    uint8NDArray pix(dim_vector (3, width, height), 0);
-
-    m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
-                          pix.fortran_vec ());
-
-    // Permute and flip data
-    Array<octave_idx_type> perm (dim_vector (3, 1));
-    perm(0) = 2;
-    perm(1) = 1;
-    perm(2) = 0;
-
-    Array<idx_vector> idx (dim_vector (3, 1));
-    idx(0) = idx_vector::make_range (height - 1, -1, height);
-    idx(1) = idx_vector::colon;
-    idx(2) = idx_vector::colon;
-
-    return pix.permute (perm).index (idx);
-
-#else
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    octave_unused_parameter (width);
-    octave_unused_parameter (height);
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::finish (void)
-  {
-#if defined (HAVE_OPENGL)
-
-    m_glfcns.glFinish ();
-
-#else
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::setup_opengl_transformation (const axes::properties& props)
+  public:
+
+    vertex_data_rep (void)
+      : m_coords (), m_color (), m_vertex_normal (), m_face_normal (),
+        m_alpha (), m_ambient (), m_diffuse (), m_specular (),
+        m_specular_exp (), m_specular_color_refl ()
+    { }
+
+    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)
+      : m_coords (c), m_color (col), m_vertex_normal (vn),
+        m_face_normal (fn), m_alpha (a), m_ambient (as), m_diffuse (ds),
+        m_specular (ss), m_specular_exp (se), m_specular_color_refl (scr)
+    { }
+
+    Matrix m_coords;
+    Matrix m_color;
+    Matrix m_vertex_normal;
+    Matrix m_face_normal;
+    double m_alpha;
+    float m_ambient;
+    float m_diffuse;
+    float m_specular;
+    float m_specular_exp;
+    float m_specular_color_refl;
+  };
+
+public:
+
+  // Required to instantiate std::list<vertex_data> objects.
+  vertex_data (void) : m_rep (nil_rep ()) { }
+
+  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)
+    : m_rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr))
+  { }
+
+  vertex_data (const vertex_data&) = default;
+
+  ~vertex_data (void) = default;
+
+  vertex_data& operator = (const vertex_data&) = default;
+
+  vertex_data_rep * get_rep (void) const { return m_rep.get (); }
+
+private:
+
+  static std::shared_ptr<vertex_data_rep> nil_rep (void)
   {
-#if defined (HAVE_OPENGL)
-
-    // setup OpenGL transformation
-
-    Matrix x_zlim = props.get_transform_zlim ();
-
-    // Expand the distance between the clipping planes symmetrically by
-    // an arbitrary factor (see bug #54551).
-    const double expansion_fac = 100.0;
-    // Also make sure that the distance between the clipping planes
-    // differs in single precision (see bug #58956).  This factor is also
-    // arbitrary.  Different values (>2) might also work.
-    const double single_prec_fac = 10.0;
-
-    double avgZ = x_zlim(0) / 2.0 + x_zlim(1) / 2.0;
-    double span
-      = std::max (expansion_fac * (x_zlim(1)-x_zlim(0)),
-                  single_prec_fac * std::abs (avgZ)
-                  * std::numeric_limits<float>::epsilon ());
-    m_xZ1 = avgZ - span;
-    m_xZ2 = avgZ + span;
-
-    Matrix x_mat1 = props.get_opengl_matrix_1 ();
-    Matrix x_mat2 = props.get_opengl_matrix_2 ();
-
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glLoadIdentity ();
-    m_glfcns.glScaled (1, 1, -1);
-    m_glfcns.glMultMatrixd (x_mat1.data ());
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glLoadIdentity ();
-
-    Matrix vp = get_viewport_scaled ();
-    m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2);
-    m_glfcns.glMultMatrixd (x_mat2.data ());
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-
-    m_glfcns.glClear (GL_DEPTH_BUFFER_BIT);
-
-    // store axes transformation data
-
-    m_xform = props.get_transform ();
-
-#else
-
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
+    static std::shared_ptr<vertex_data_rep> nr (new vertex_data_rep ());
+
+    return nr;
   }
 
-  void
-  opengl_renderer::draw_axes_planes (const axes::properties& props)
+  std::shared_ptr<vertex_data_rep> m_rep;
+};
+
+class
+opengl_renderer::patch_tessellator : public opengl_tessellator
+{
+public:
+  patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl,
+                     float idx = 0.0)
+    : opengl_tessellator (), m_renderer (r),
+      m_color_mode (cmode), m_light_mode (lmode), m_face_lighting (fl),
+      m_index (idx), m_first (true), m_tmp_vdata ()
+  { }
+
+protected:
+  void begin (GLenum type)
   {
-#if defined (HAVE_OPENGL)
-
-    Matrix axe_color = props.get_color_rgb ();
-    if (axe_color.isempty () || ! props.is_visible ())
-      return;
-
-    double xPlane = props.get_xPlane ();
-    double yPlane = props.get_yPlane ();
-    double zPlane = props.get_zPlane ();
-    double xPlaneN = props.get_xPlaneN ();
-    double yPlaneN = props.get_yPlaneN ();
-    double zPlaneN = props.get_zPlaneN ();
-    bool is2D = props.get_is2D ();
-
-    // Axes planes
-    set_color (axe_color);
-    set_polygon_offset (true, 9.0);
-
-    m_glfcns.glBegin (GL_QUADS);
-
-    if (! is2D)
-      {
-        // X plane
-        m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
-        m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
-        m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
-        m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
-
-        // Y plane
-        m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
-        m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
-        m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
-        m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
-      }
-
-    // Z plane
-    m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
-    m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
-    m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
-    m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
-
-    m_glfcns.glEnd ();
-
-    set_polygon_offset (false);
-
-#else
-
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
+    opengl_functions& glfcns = m_renderer->get_opengl_functions ();
+
+    //printf ("patch_tessellator::begin (%d)\n", type);
+    m_first = true;
+
+    if (m_color_mode == INTERP || m_light_mode == GOURAUD)
+      glfcns.glShadeModel (GL_SMOOTH);
+    else
+      glfcns.glShadeModel (GL_FLAT);
+
+    if (is_filled ())
+      m_renderer->set_polygon_offset (true, m_index);
+
+    glfcns.glBegin (type);
   }
 
-  void
-  opengl_renderer::draw_axes_boxes (const axes::properties& props)
+  void end (void)
   {
-#if defined (HAVE_OPENGL)
-
-    if (! props.is_visible ())
-      return;
-
-    bool xySym = props.get_xySym ();
-    bool layer2Dtop = props.get_layer2Dtop ();
-    bool is2D = props.get_is2D ();
-    bool isXOrigin = props.xaxislocation_is ("origin")
-                     && ! props.yscale_is ("log");
-    bool isYOrigin = props.yaxislocation_is ("origin")
-                     && ! props.xscale_is ("log");
-    bool boxFull = (props.get_boxstyle () == "full");
-    double linewidth = props.get_linewidth ();
-    double xPlane = props.get_xPlane ();
-    double yPlane = props.get_yPlane ();
-    double zPlane = props.get_zPlane ();
-    double xPlaneN = props.get_xPlaneN ();
-    double yPlaneN = props.get_yPlaneN ();
-    double zPlaneN = props.get_zPlaneN ();
-    double xpTick = props.get_xpTick ();
-    double ypTick = props.get_ypTick ();
-    double zpTick = props.get_zpTick ();
-    double xpTickN = props.get_xpTickN ();
-    double ypTickN = props.get_ypTickN ();
-    double zpTickN = props.get_zpTickN ();
-
-    bool plotyy = (props.has_property ("__plotyy_axes__"));
-
-    // Axes box
-
-    set_linecap ("square");
-    set_linestyle ("-", true, linewidth);
-
-    m_glfcns.glBegin (GL_LINES);
-
-    if (layer2Dtop)
-      std::swap (zpTick, zpTickN);
-
-    // X box
-    Matrix color = props.get_xcolor_rgb ();
-
-    if (! color.isempty ())
+    opengl_functions& glfcns = m_renderer->get_opengl_functions ();
+
+    //printf ("patch_tessellator::end\n");
+    glfcns.glEnd ();
+    m_renderer->set_polygon_offset (false);
+  }
+
+  void vertex (void *data)
+  {
+    opengl_functions& glfcns = m_renderer->get_opengl_functions ();
+
+    vertex_data::vertex_data_rep *v
+      = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
+    //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->m_coords(0), v->m_coords(1), v->m_coords(2));
+
+    // NOTE: OpenGL can re-order vertices.  For "flat" coloring of FaceColor
+    // the first vertex must be identified in the draw_patch routine.
+
+    if (m_color_mode == INTERP || (m_color_mode == FLAT && ! is_filled ()))
       {
-        set_color (color);
-
-        if (! isXOrigin || props.is_box() || ! is2D)
+        Matrix col = v->m_color;
+
+        if (col.numel () == 3)
           {
-            m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick);
-            m_glfcns.glVertex3d (xPlane, ypTick, zpTick);
-          }
-
-        if (props.is_box ())
-          {
-            m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick);
-            m_glfcns.glVertex3d (xPlane, ypTickN, zpTick);
-            if (! is2D)
+            glfcns.glColor4d (col(0), col(1), col(2), v->m_alpha);
+            if (m_light_mode > 0)
               {
-                m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN);
-                m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN);
-                if (boxFull)
-                  {
-                    m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN);
-                    m_glfcns.glVertex3d (xPlane, ypTick, zpTickN);
-                  }
-              }
-          }
-      }
-
-    // Y box
-    color = props.get_ycolor_rgb ();
-
-    if (! color.isempty ())
-      {
-        set_color (color);
-        if (! isYOrigin || props.is_box() || ! is2D)
-          {
-            m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick);
-            m_glfcns.glVertex3d (xpTick, yPlane, zpTick);
-          }
-
-        if (props.is_box () && ! plotyy)
-          {
-            m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick);
-            m_glfcns.glVertex3d (xpTickN, yPlane, zpTick);
-
-            if (! is2D)
-              {
-                m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN);
-                m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN);
-                if (boxFull)
-                  {
-                    m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN);
-                    m_glfcns.glVertex3d (xpTick, yPlane, zpTickN);
-                  }
-              }
-          }
-      }
-
-    // Z box
-    color = props.get_zcolor_rgb ();
-
-    if (! color.isempty () && ! is2D)
-      {
-        set_color (color);
-
-        if (xySym)
-          {
-            m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
-            m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
-          }
-        else
-          {
-            m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
-            m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
-          }
-
-        if (props.is_box ())
-          {
-            m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
-            m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
-
-            if (xySym)
-              {
-                m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
-                m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
-              }
-            else
-              {
-                m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
-                m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
-              }
-
-            if (boxFull)
-              {
-                m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN);
-                m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
+                // edge lighting only uses ambient light
+                float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };;
+
+                if (m_face_lighting)
+                  for (int k = 0; k < 3; k++)
+                    buf[k] = (v->m_specular
+                              * (v->m_specular_color_refl +
+                                 (1 - v->m_specular_color_refl) * col(k)));
+                glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
+
+                if (m_face_lighting)
+                  for (int k = 0; k < 3; k++)
+                    buf[k] = (v->m_diffuse * col(k));
+                glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf);
+
+                for (int k = 0; k < 3; k++)
+                  buf[k] = (v->m_ambient * col(k));
+                glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
               }
           }
       }
 
-    m_glfcns.glEnd ();
-
-    set_linestyle ("-");  // Disable LineStipple
+    if (m_light_mode == FLAT && m_first)
+      glfcns.glNormal3dv (v->m_face_normal.data ());
+    else if (m_light_mode == GOURAUD)
+      glfcns.glNormal3dv (v->m_vertex_normal.data ());
+
+    glfcns.glVertex3dv (v->m_coords.data ());
+
+    m_first = false;
+  }
+
+  void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data)
+  {
+    vertex_data::vertex_data_rep *v[4];
+    int vmax = 4;
+
+    for (int i = 0; i < 4; i++)
+      {
+        v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]);
+
+        if (vmax == 4 && ! v[i])
+          vmax = i;
+      }
+
+    Matrix vv (1, 3, 0.0);
+    Matrix cc;
+    Matrix vnn (1, 3, 0.0);
+    Matrix fnn (1, 3, 0.0);
+    double aa = 0.0;
+
+    vv(0) = xyz[0];
+    vv(1) = xyz[1];
+    vv(2) = xyz[2];
+
+    if (v[0]->m_color.numel ())
+      {
+        cc.resize (1, 3, 0.0);
+        for (int ic = 0; ic < 3; ic++)
+          for (int iv = 0; iv < vmax; iv++)
+            cc(ic) += (w[iv] * v[iv]->m_color (ic));
+      }
+
+    if (v[0]->m_vertex_normal.numel () > 0)
+      {
+        for (int in = 0; in < 3; in++)
+          for (int iv = 0; iv < vmax; iv++)
+            vnn(in) += (w[iv] * v[iv]->m_vertex_normal (in));
+      }
+
+    if (v[0]->m_face_normal.numel () > 0)
+      {
+        for (int in = 0; in < 3; in++)
+          for (int iv = 0; iv < vmax; iv++)
+            fnn(in) += (w[iv] * v[iv]->m_face_normal (in));
+      }
+
+    for (int iv = 0; iv < vmax; iv++)
+      aa += (w[iv] * v[iv]->m_alpha);
+
+    vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->m_ambient, v[0]->m_diffuse,
+                       v[0]->m_specular, v[0]->m_specular_exp,
+                       v[0]->m_specular_color_refl);
+    m_tmp_vdata.push_back (new_v);
+
+    *out_data = new_v.get_rep ();
+  }
+
+private:
+
+  // No copying!
+
+  patch_tessellator (const patch_tessellator&) = delete;
+
+  patch_tessellator& operator = (const patch_tessellator&) = delete;
+
+  opengl_renderer *m_renderer;
+  int m_color_mode;
+  int m_light_mode;
+  bool m_face_lighting;
+  int m_index;
+  bool m_first;
+  std::list<vertex_data> m_tmp_vdata;
+};
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+class
+opengl_renderer::patch_tessellator
+{
+  // Dummy class.
+};
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_axes_x_grid (const axes::properties& props)
-  {
+
+opengl_renderer::opengl_renderer (opengl_functions& glfcns)
+  : m_glfcns (glfcns), m_xmin (), m_xmax (), m_ymin (), m_ymax (),
+    m_zmin (), m_zmax (), m_devpixratio (1.0), m_xform (), m_toolkit (),
+    m_xZ1 (), m_xZ2 (), m_marker_id (), m_filled_marker_id (),
+    m_camera_pos (), m_camera_dir (), m_view_vector (),
+    m_interpreter ("none"), m_txt_renderer (), m_current_light (0),
+    m_max_lights (0), m_selecting (false), m_printing (false)
+{
+  // This constructor will fail if we don't have OpenGL or if the data
+  // types we assumed in our public interface aren't compatible with the
+  // OpenGL types.
+
 #if defined (HAVE_OPENGL)
 
-    gh_manager& gh_mgr = __get_gh_manager__ ();
-
-    int xstate = props.get_xstate ();
-
-    if (xstate != AXE_DEPTH_DIR
-        && (props.is_visible ()
-            || (m_selecting && props.pickableparts_is ("all"))))
-      {
-        int zstate = props.get_zstate ();
-        bool x2Dtop = props.get_x2Dtop ();
-        bool layer2Dtop = props.get_layer2Dtop ();
-        bool xyzSym = props.get_xyzSym ();
-        bool nearhoriz = props.get_nearhoriz ();
-        double xticklen = props.get_xticklen ();
-        double xtickoffset = props.get_xtickoffset ();
-        double fy = props.get_fy ();
-        double fz = props.get_fz ();
-        double x_min = props.get_x_min ();
-        double x_max = props.get_x_max ();
-        double y_min = props.get_y_min ();
-        double y_max = props.get_y_max ();
-        double yPlane = props.get_yPlane ();
-        double yPlaneN = props.get_yPlaneN ();
-        double ypTick = props.get_ypTick ();
-        double ypTickN = props.get_ypTickN ();
-        double zPlane = props.get_zPlane ();
-        double zPlaneN = props.get_zPlaneN ();
-        double zpTick = props.get_zpTick ();
-        double zpTickN = props.get_zpTickN ();
-
-        // X ticks and grid properties
-        Matrix xticks = m_xform.xscale (props.get_xtick ().matrix_value ());
-        Matrix xmticks = m_xform.xscale (props.get_xminortickvalues ().matrix_value ());
-        bool do_xminortick = props.is_xminortick () && ! xticks.isempty ();
-        string_vector xticklabels = props.get_xticklabel ().string_vector_value ();
-        int wmax = 0;
-        int hmax = 0;
-        bool tick_along_z = nearhoriz || math::isinf (fy);
-        double linewidth = props.get_linewidth ();
-        std::string gridstyle = props.get_gridlinestyle ();
-        std::string minorgridstyle = props.get_minorgridlinestyle ();
-        Matrix gridcolor = props.get_gridcolor_rgb ();
-        Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
-        double gridalpha = props.get_gridalpha ();
-        double minorgridalpha = props.get_minorgridalpha ();
-        bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
-        bool do_xminorgrid = (props.is_xminorgrid ()
-                              && (minorgridstyle != "none")
-                              && ! xticks.isempty ());
-        bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D ()
-                         && ! props.yscale_is ("log");
-        bool is_origin_low = is_origin && (y_min + y_max) < 0;
-        bool mirror = props.is_box () && xstate != AXE_ANY_DIR;
-
-        // X grid
-
-        // possibly use axis color for gridcolor & minorgridcolor
-        if (props.gridcolormode_is ("auto"))
-          if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none"))
-            gridcolor = props.get_xcolor_rgb ();
-
-        if (props.minorgridcolormode_is ("auto"))
-          if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none"))
-            minorgridcolor = props.get_xcolor_rgb ();
-
-        if (gridcolor.isempty ())
-          do_xgrid = false;
-
-        if (minorgridcolor.isempty ())
-          do_xminorgrid = false;
-
-        // set styles when drawing only minor grid
-        if (do_xminorgrid && ! do_xgrid)
-          {
-            gridstyle = minorgridstyle;
-            gridcolor = minorgridcolor;
-            gridalpha = minorgridalpha;
-            do_xgrid = true;
-          }
-
-        // minor grid lines
-        if (do_xminorgrid)
-          render_grid (linewidth,
-                       minorgridstyle, minorgridcolor, minorgridalpha,
-                       xmticks, x_min, x_max,
-                       yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
-                       0, (zstate != AXE_DEPTH_DIR));
-
-        // grid lines
-        if (do_xgrid)
-          render_grid (linewidth,
-                       gridstyle, gridcolor, gridalpha,
-                       xticks, x_min, x_max,
-                       yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
-                       0, (zstate != AXE_DEPTH_DIR));
-
-        // Skip drawing axis, ticks, and ticklabels when color is "none"
-        if (props.xcolor_is ("none"))
-          return;
-
-        set_color (props.get_xcolor_rgb ());
-
-        // axis line
-        double y_axis_pos = 0.;
-        if (is_origin)
-          {
-            y_axis_pos = math::max (math::min (0., y_max), y_min);
-            m_glfcns.glBegin (GL_LINES);
-            set_color (props.get_xcolor_rgb ());
-            m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick);
-            m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick);
-            m_glfcns.glEnd ();
-          }
-
-        // minor tick marks
-        if (do_xminortick)
-          {
-            if (tick_along_z)
-              render_tickmarks (xmticks, x_min, x_max,
-                                is_origin ? y_axis_pos : ypTick, ypTick,
-                                zpTick, zpTickN, 0., 0.,
-                                (is_origin_low ? -1. : 1.) *
-                                math::signum (zpTick-zpTickN)*fz*xticklen/2,
-                                0, ! is_origin && mirror);
-            else
-              render_tickmarks (xmticks, x_min, x_max,
-                                is_origin ? y_axis_pos : ypTick, ypTickN,
-                                zpTick, zpTick, 0.,
-                                (is_origin_low ? -1. : 1.) *
-                                math::signum (ypTick-ypTickN)*fy*xticklen/2,
-                                0., 0, ! is_origin && mirror);
-          }
-
-        // tick marks
-        if (tick_along_z)
-          render_tickmarks (xticks, x_min, x_max,
-                            is_origin ? y_axis_pos : ypTick, ypTick,
-                            zpTick, zpTickN, 0., 0.,
-                            (is_origin_low ? -1. : 1.) *
-                            math::signum (zpTick-zpTickN)*fz*xticklen,
-                            0, ! is_origin && mirror);
-        else
-          render_tickmarks (xticks, x_min, x_max,
-                            is_origin ? y_axis_pos : ypTick, ypTickN,
-                            zpTick, zpTick, 0.,
-                            (is_origin_low ? -1. : 1.) *
-                            math::signum (ypTick-ypTickN)*fy*xticklen,
-                            0., 0, ! is_origin && mirror);
-
-        // tick texts
-        if (xticklabels.numel () > 0)
-          {
-            int halign = (xstate == AXE_HORZ_DIR
-                          ? 1
-                          : (xyzSym || is_origin_low ? 0 : 2));
-            int valign = (xstate == AXE_VERT_DIR
-                          ? 1
-                          : (x2Dtop || is_origin_low ? 0 : 2));
-
-            if (tick_along_z)
-              render_ticktexts (xticks, xticklabels, x_min, x_max,
-                                is_origin ? y_axis_pos : ypTick,
-                                zpTick +
-                                (is_origin_low ? -1. : 1.) *
-                                math::signum (zpTick-zpTickN)*fz*xtickoffset,
-                                0, halign, valign, wmax, hmax);
-            else
-              render_ticktexts (xticks, xticklabels, x_min, x_max,
-                                (is_origin ? y_axis_pos : ypTick) +
-                                (is_origin_low ?  -1. : 1.) *
-                                math::signum (ypTick-ypTickN)*fy*xtickoffset,
-                                zpTick, 0, halign, valign, wmax, hmax);
-          }
-
-        gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on");
-      }
-    else
-      gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off");
+  // Ensure that we can't request an image larger than OpenGL can handle.
+  // FIXME: should we check signed vs. unsigned?
+
+  static bool ok = (sizeof (int) <= sizeof (GLsizei));
+
+  if (! ok)
+    error ("the size of GLsizei is smaller than the size of int");
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  err_disabled_feature ("opengl_renderer", "OpenGL");
+
+#endif
+}
+
+void
+opengl_renderer::draw (const graphics_object& go, bool toplevel)
+{
+  if (! go.valid_object ())
+    return;
+
+  const base_properties& props = go.get_properties ();
+
+  if (! m_toolkit)
+    m_toolkit = props.get_toolkit ();
+
+  if (go.isa ("figure"))
+    draw_figure (dynamic_cast<const figure::properties&> (props));
+  else if (go.isa ("axes"))
+    draw_axes (dynamic_cast<const axes::properties&> (props));
+  else if (go.isa ("line"))
+    draw_line (dynamic_cast<const line::properties&> (props));
+  else if (go.isa ("surface"))
+    draw_surface (dynamic_cast<const surface::properties&> (props));
+  else if (go.isa ("patch"))
+    draw_patch (dynamic_cast<const patch::properties&> (props));
+  else if (go.isa ("scatter"))
+    draw_scatter (dynamic_cast<const scatter::properties&> (props));
+  else if (go.isa ("light"))
+    draw_light (dynamic_cast<const light::properties&> (props));
+  else if (go.isa ("hggroup"))
+    draw_hggroup (dynamic_cast<const hggroup::properties&> (props));
+  else if (go.isa ("text"))
+    draw_text (dynamic_cast<const text::properties&> (props));
+  else if (go.isa ("image"))
+    draw_image (dynamic_cast<const image::properties&> (props));
+  else if (go.isa ("uimenu") || go.isa ("uicontrol")
+           || go.isa ("uicontextmenu") || go.isa ("uitoolbar")
+           || go.isa ("uipushtool") || go.isa ("uitoggletool")
+           || go.isa ("uitable"))
+    ; // SKIP
+  else if (go.isa ("uipanel"))
+    {
+      if (toplevel)
+        draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go);
+    }
+  else if (go.isa ("uibuttongroup"))
+    {
+      if (toplevel)
+        draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go);
+    }
+  else
+    {
+      warning ("opengl_renderer: cannot render object of type '%s'",
+               props.graphics_object_name ().c_str ());
+    }
+
+#if defined (HAVE_OPENGL)
+
+  GLenum gl_error = m_glfcns.glGetError ();
+  if (gl_error)
+    warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object",
+             gluErrorString (gl_error), gl_error,
+             props.graphics_object_name ().c_str ());
+
+#endif
+}
+
+void
+opengl_renderer::draw_figure (const figure::properties& props)
+{
+  m_printing = props.is___printing__ ();
+
+  // Initialize OpenGL context
+  init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ());
+
+#if defined (HAVE_OPENGL)
+
+  props.set___gl_extensions__ (get_string (GL_EXTENSIONS));
+  props.set___gl_renderer__ (get_string (GL_RENDERER));
+  props.set___gl_vendor__ (get_string (GL_VENDOR));
+  props.set___gl_version__ (get_string (GL_VERSION));
+
+#endif
+
+  // Draw children
+
+  draw (props.get_all_children (), false);
+}
+
+void
+opengl_renderer::draw_uipanel (const uipanel::properties& props,
+                               const graphics_object& go)
+{
+  graphics_object fig = go.get_ancestor ("figure");
+  const figure::properties& figProps
+    = dynamic_cast<const figure::properties&> (fig.get_properties ());
+
+  // Initialize OpenGL context
+
+  init_gl_context (figProps.is_graphicssmoothing (),
+                   props.get_backgroundcolor_rgb ());
+
+  // Draw children
+
+  draw (props.get_all_children (), false);
+}
+
+void
+opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props,
+                                     const graphics_object& go)
+{
+  graphics_object fig = go.get_ancestor ("figure");
+  const figure::properties& figProps
+    = dynamic_cast<const figure::properties&> (fig.get_properties ());
+
+  // Initialize OpenGL context
+
+  init_gl_context (figProps.is_graphicssmoothing (),
+                   props.get_backgroundcolor_rgb ());
+
+  // Draw children
+
+  draw (props.get_all_children (), false);
+}
+
+void
+opengl_renderer::init_gl_context (bool enhanced, const Matrix& c)
+{
+#if defined (HAVE_OPENGL)
+
+  // Initialize OpenGL context
+
+  m_glfcns.glEnable (GL_DEPTH_TEST);
+  m_glfcns.glDepthFunc (GL_LEQUAL);
+  m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  m_glfcns.glAlphaFunc (GL_GREATER, 0.0f);
+  m_glfcns.glEnable (GL_NORMALIZE);
+  m_glfcns.glEnable (GL_BLEND);
+
+  if (enhanced)
+    {
+      m_glfcns.glEnable (GL_MULTISAMPLE);
+      bool has_multisample = false;
+      if (! m_glfcns.glGetError ())
+        {
+          GLint iMultiSample, iNumSamples;
+          m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample);
+          m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples);
+          if (iMultiSample == GL_TRUE && iNumSamples > 0)
+            has_multisample = true;
+        }
+
+      if (! has_multisample)
+        {
+          // MultiSample not implemented.  Use old-style anti-aliasing
+          m_glfcns.glDisable (GL_MULTISAMPLE);
+          // Disabling GL_MULTISAMPLE will raise a gl error if it is not
+          // implemented.  Thus, call glGetError to reset the error state.
+          m_glfcns.glGetError ();
+
+          m_glfcns.glEnable (GL_LINE_SMOOTH);
+          m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+        }
+    }
+  else
+    {
+      m_glfcns.glDisable (GL_LINE_SMOOTH);
+    }
+
+  // Clear background
+
+  if (c.numel () >= 3)
+    {
+      m_glfcns.glClearColor (c(0), c(1), c(2), 1);
+      m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    }
+
+  GLenum gl_error = m_glfcns.glGetError ();
+  if (gl_error)
+    warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context",
+             gluErrorString (gl_error), gl_error);
+
+#else
+
+  octave_unused_parameter (enhanced);
+  octave_unused_parameter (c);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_axes_y_grid (const axes::properties& props)
-  {
+}
+
+void
+opengl_renderer::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)
+{
 #if defined (HAVE_OPENGL)
 
-    gh_manager& gh_mgr = __get_gh_manager__ ();
-
-    int ystate = props.get_ystate ();
-
-    if (ystate != AXE_DEPTH_DIR && props.is_visible ()
-        && (props.is_visible ()
-            || (m_selecting && props.pickableparts_is ("all"))))
-      {
-        int zstate = props.get_zstate ();
-        bool y2Dright = props.get_y2Dright ();
-        bool layer2Dtop = props.get_layer2Dtop ();
-        bool xyzSym = props.get_xyzSym ();
-        bool nearhoriz = props.get_nearhoriz ();
-        double yticklen = props.get_yticklen ();
-        double ytickoffset = props.get_ytickoffset ();
-        double fx = props.get_fx ();
-        double fz = props.get_fz ();
-        double xPlane = props.get_xPlane ();
-        double xPlaneN = props.get_xPlaneN ();
-        double xpTick = props.get_xpTick ();
-        double xpTickN = props.get_xpTickN ();
-        double y_min = props.get_y_min ();
-        double y_max = props.get_y_max ();
-        double x_min = props.get_x_min ();
-        double x_max = props.get_x_max ();
-        double zPlane = props.get_zPlane ();
-        double zPlaneN = props.get_zPlaneN ();
-        double zpTick = props.get_zpTick ();
-        double zpTickN = props.get_zpTickN ();
-
-        // Y ticks and grid properties
-        Matrix yticks = m_xform.yscale (props.get_ytick ().matrix_value ());
-        Matrix ymticks = m_xform.yscale (props.get_yminortickvalues ().matrix_value ());
-        bool do_yminortick = props.is_yminortick () && ! yticks.isempty ();
-        string_vector yticklabels = props.get_yticklabel ().string_vector_value ();
-        int wmax = 0;
-        int hmax = 0;
-        bool tick_along_z = nearhoriz || math::isinf (fx);
-        double linewidth = props.get_linewidth ();
-        std::string gridstyle = props.get_gridlinestyle ();
-        std::string minorgridstyle = props.get_minorgridlinestyle ();
-        Matrix gridcolor = props.get_gridcolor_rgb ();
-        Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
-        double gridalpha = props.get_gridalpha ();
-        double minorgridalpha = props.get_minorgridalpha ();
-        bool do_ygrid = (props.is_ygrid () && (gridstyle != "none"));
-        bool do_yminorgrid = (props.is_yminorgrid ()
-                              && (minorgridstyle != "none")
-                              && ! yticks.isempty ());
-        bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D ()
-                         && ! props.xscale_is ("log");
-        bool is_origin_low = is_origin && (x_min + x_max) < 0;
-        bool mirror = props.is_box () && ystate != AXE_ANY_DIR
-                      && (! props.has_property ("__plotyy_axes__"));
-
-        // Y grid
-
-        // possibly use axis color for gridcolor & minorgridcolor
-        if (props.gridcolormode_is ("auto"))
-          if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none"))
-            gridcolor = props.get_ycolor_rgb ();
-
-        if (props.minorgridcolormode_is ("auto"))
-          if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none"))
-            minorgridcolor = props.get_ycolor_rgb ();
-
-        if (gridcolor.isempty ())
-          do_ygrid = false;
-
-        if (minorgridcolor.isempty ())
-          do_yminorgrid = false;
-
-        // set styles when drawing only minor grid
-        if (do_yminorgrid && ! do_ygrid)
-          {
-            gridstyle = minorgridstyle;
-            gridcolor = minorgridcolor;
-            gridalpha = minorgridalpha;
-            do_ygrid = true;
-          }
-
-        // minor grid lines
-        if (do_yminorgrid)
-          render_grid (linewidth,
-                       minorgridstyle, minorgridcolor, minorgridalpha,
-                       ymticks, y_min, y_max,
-                       xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
-                       1, (zstate != AXE_DEPTH_DIR));
-
-        // grid lines
-        if (do_ygrid)
-          render_grid (linewidth,
-                       gridstyle, gridcolor, gridalpha,
-                       yticks, y_min, y_max,
-                       xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
-                       1, (zstate != AXE_DEPTH_DIR));
-
-        // Skip drawing axis, ticks, and ticklabels when color is "none"
-        if (props.ycolor_is ("none"))
-          return;
-
-        set_color (props.get_ycolor_rgb ());
-
-        // axis line
-        double x_axis_pos = 0.;
-        if (is_origin)
-          {
-            x_axis_pos = math::max (math::min (0., x_max), x_min);
-            m_glfcns.glBegin (GL_LINES);
-            set_color (props.get_ycolor_rgb ());
-            m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick);
-            m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick);
-            m_glfcns.glEnd ();
-          }
-
-        // minor tick marks
-        if (do_yminortick)
-          {
-            if (tick_along_z)
-              render_tickmarks (ymticks, y_min, y_max,
-                                is_origin ? x_axis_pos : xpTick, xpTick,
-                                zpTick, zpTickN, 0., 0.,
-                                (is_origin_low ? -1. : 1.) *
-                                math::signum (zpTick-zpTickN)*fz*yticklen/2,
-                                1, ! is_origin && mirror);
-            else
-              render_tickmarks (ymticks, y_min, y_max,
-                                is_origin ? x_axis_pos : xpTick, xpTickN,
-                                zpTick, zpTick,
-                                (is_origin_low ? -1. : 1.) *
-                                math::signum (xpTick-xpTickN)*fx*yticklen/2,
-                                0., 0., 1, ! is_origin && mirror);
-          }
-
-        // tick marks
-        if (tick_along_z)
-          render_tickmarks (yticks, y_min, y_max,
-                            is_origin ? x_axis_pos : xpTick, xpTick,
-                            zpTick, zpTickN, 0., 0.,
-                            (is_origin_low ? -1. : 1.) *
-                            math::signum (zpTick-zpTickN)*fz*yticklen,
-                            1, ! is_origin && mirror);
-        else
-          render_tickmarks (yticks, y_min, y_max,
-                            is_origin ? x_axis_pos : xpTick, xpTickN,
-                            zpTick, zpTick,
-                            (is_origin_low ? -1. : 1.) *
-                            math::signum (xPlaneN-xPlane)*fx*yticklen,
-                            0., 0., 1, ! is_origin && mirror);
-
-        // tick texts
-        if (yticklabels.numel () > 0)
-          {
-            int halign = (ystate == AXE_HORZ_DIR
-                          ? 1
-                          : (! xyzSym || y2Dright || is_origin_low ? 0 : 2));
-            int valign = (ystate == AXE_VERT_DIR
-                          ? 1
-                          : (is_origin_low ? 0 : 2));
-
-            if (tick_along_z)
-              render_ticktexts (yticks, yticklabels, y_min, y_max,
-                                is_origin ? x_axis_pos : xpTick,
-                                zpTick +
-                                (is_origin_low ? -1. : 1.) *
-                                math::signum (zpTick-zpTickN)*fz*ytickoffset,
-                                1, halign, valign, wmax, hmax);
-            else
-              render_ticktexts (yticks, yticklabels, y_min, y_max,
-                                (is_origin ? x_axis_pos : xpTick) +
-                                (is_origin_low ?  -1. : 1.) *
-                                math::signum (xpTick-xpTickN)*fx*ytickoffset,
-                                zpTick, 1, halign, valign, wmax, hmax);
-          }
-
-        gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on");
-      }
-    else
-      gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off");
+  m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha);
+  set_linestyle (gridstyle, true, linewidth);
+  m_glfcns.glBegin (GL_LINES);
+  for (int i = 0; i < ticks.numel (); i++)
+    {
+      double val = ticks(i);
+      if (lim1 <= val && val <= lim2)
+        {
+          if (xyz == X_AXIS)
+            {
+              m_glfcns.glVertex3d (val, p1N, p2);
+              m_glfcns.glVertex3d (val, p1, p2);
+              if (is_3D)
+                {
+                  m_glfcns.glVertex3d (val, p1, p2N);
+                  m_glfcns.glVertex3d (val, p1, p2);
+                }
+            }
+          else if (xyz == Y_AXIS)
+            {
+              m_glfcns.glVertex3d (p1N, val, p2);
+              m_glfcns.glVertex3d (p1, val, p2);
+              if (is_3D)
+                {
+                  m_glfcns.glVertex3d (p1, val, p2N);
+                  m_glfcns.glVertex3d (p1, val, p2);
+                }
+            }
+          else if (xyz == Z_AXIS)
+            {
+              m_glfcns.glVertex3d (p1N, p2, val);
+              m_glfcns.glVertex3d (p1, p2, val);
+              m_glfcns.glVertex3d (p1, p2N, val);
+              m_glfcns.glVertex3d (p1, p2, val);
+            }
+        }
+    }
+  m_glfcns.glEnd ();
+  set_linestyle ("-");  // Disable LineStipple
+  double black[3] = {0, 0, 0};
+  m_glfcns.glColor3dv (black);
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (linewidth);
+  octave_unused_parameter (gridstyle);
+  octave_unused_parameter (gridcolor);
+  octave_unused_parameter (gridalpha);
+  octave_unused_parameter (ticks);
+  octave_unused_parameter (lim1);
+  octave_unused_parameter (lim2);
+  octave_unused_parameter (p1);
+  octave_unused_parameter (p1N);
+  octave_unused_parameter (p2);
+  octave_unused_parameter (p2N);
+  octave_unused_parameter (xyz);
+  octave_unused_parameter (is_3D);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::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)
+{
+#if defined (HAVE_OPENGL)
+
+  m_glfcns.glBegin (GL_LINES);
+
+  for (int i = 0; i < ticks.numel (); i++)
+    {
+      double val = ticks(i);
+
+      if (lim1 <= val && val <= lim2)
+        {
+          if (xyz == X_AXIS)
+            {
+              m_glfcns.glVertex3d (val, p1, p2);
+              m_glfcns.glVertex3d (val, p1+dy, p2+dz);
+              if (mirror)
+                {
+                  m_glfcns.glVertex3d (val, p1N, p2N);
+                  m_glfcns.glVertex3d (val, p1N-dy, p2N-dz);
+                }
+            }
+          else if (xyz == Y_AXIS)
+            {
+              m_glfcns.glVertex3d (p1, val, p2);
+              m_glfcns.glVertex3d (p1+dx, val, p2+dz);
+              if (mirror)
+                {
+                  m_glfcns.glVertex3d (p1N, val, p2N);
+                  m_glfcns.glVertex3d (p1N-dx, val, p2N-dz);
+                }
+            }
+          else if (xyz == Z_AXIS)
+            {
+              m_glfcns.glVertex3d (p1, p2, val);
+              m_glfcns.glVertex3d (p1+dx, p2+dy, val);
+              if (mirror)
+                {
+                  m_glfcns.glVertex3d (p1N, p2N, val);
+                  m_glfcns.glVertex3d (p1N-dx, p2N-dy, val);
+                }
+            }
+        }
+    }
+
+  m_glfcns.glEnd ();
+
+#else
+
+  octave_unused_parameter (ticks);
+  octave_unused_parameter (lim1);
+  octave_unused_parameter (lim2);
+  octave_unused_parameter (p1);
+  octave_unused_parameter (p1N);
+  octave_unused_parameter (p2);
+  octave_unused_parameter (p2N);
+  octave_unused_parameter (dx);
+  octave_unused_parameter (dy);
+  octave_unused_parameter (dz);
+  octave_unused_parameter (xyz);
+  octave_unused_parameter (mirror);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::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)
+{
+#if defined (HAVE_OPENGL)
+
+  int nticks  = ticks.numel ();
+  int nlabels = ticklabels.numel ();
+
+  if (nlabels == 0)
+    return;
+
+  for (int i = 0; i < nticks; i++)
+    {
+      double val = ticks(i);
+
+      if (lim1 <= val && val <= lim2)
+        {
+          Matrix b;
+
+          std::string label (ticklabels(i % nlabels));
+          label.erase (0, label.find_first_not_of (' '));
+          label = label.substr (0, label.find_last_not_of (' ')+1);
+
+          // FIXME: As tick text is transparent, shouldn't it be
+          //        drawn after axes object, for correct rendering?
+          if (xyz == X_AXIS)
+            {
+              b = render_text (label, val, p1, p2, ha, va);
+            }
+          else if (xyz == Y_AXIS)
+            {
+              b = render_text (label, p1, val, p2, ha, va);
+            }
+          else if (xyz == Z_AXIS)
+            {
+              b = render_text (label, p1, p2, val, ha, va);
+            }
+
+          wmax = std::max (wmax, static_cast<int> (b(2)));
+          hmax = std::max (hmax, static_cast<int> (b(3)));
+        }
+    }
+
+#else
+
+  octave_unused_parameter (ticks);
+  octave_unused_parameter (ticklabels);
+  octave_unused_parameter (lim1);
+  octave_unused_parameter (lim2);
+  octave_unused_parameter (p1);
+  octave_unused_parameter (p2);
+  octave_unused_parameter (xyz);
+  octave_unused_parameter (ha);
+  octave_unused_parameter (va);
+  octave_unused_parameter (wmax);
+  octave_unused_parameter (hmax);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2)
+{
+#if defined (HAVE_OPENGL)
+
+  m_glfcns.glVertex2d (x1, y1);
+  m_glfcns.glVertex2d (x2, y1);
+  m_glfcns.glVertex2d (x2, y2);
+  m_glfcns.glVertex2d (x1, y2);
+  m_glfcns.glVertex2d (x1, y1);
+
+#else
+
+  octave_unused_parameter (x1);
+  octave_unused_parameter (x2);
+  octave_unused_parameter (y1);
+  octave_unused_parameter (y2);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_axes_z_grid (const axes::properties& props)
-  {
-    gh_manager& gh_mgr = __get_gh_manager__ ();
-
-    int zstate = props.get_zstate ();
-
-    if (zstate != AXE_DEPTH_DIR && props.is_visible ()
-        && (props.is_visible ()
-            || (m_selecting && props.pickableparts_is ("all"))))
-      {
-        bool xySym = props.get_xySym ();
-        bool zSign = props.get_zSign ();
-        double zticklen = props.get_zticklen ();
-        double ztickoffset = props.get_ztickoffset ();
-        double fx = props.get_fx ();
-        double fy = props.get_fy ();
-        double xPlane = props.get_xPlane ();
-        double xPlaneN = props.get_xPlaneN ();
-        double yPlane = props.get_yPlane ();
-        double yPlaneN = props.get_yPlaneN ();
-        double z_min = props.get_z_min ();
-        double z_max = props.get_z_max ();
-
-        // Z ticks and grid properties
-        Matrix zticks = m_xform.zscale (props.get_ztick ().matrix_value ());
-        Matrix zmticks = m_xform.zscale (props.get_zminortickvalues ().matrix_value ());
-        bool do_zminortick = props.is_zminortick () && ! zticks.isempty ();
-        string_vector zticklabels = props.get_zticklabel ().string_vector_value ();
-        int wmax = 0;
-        int hmax = 0;
-        double linewidth = props.get_linewidth ();
-        std::string gridstyle = props.get_gridlinestyle ();
-        std::string minorgridstyle = props.get_minorgridlinestyle ();
-        Matrix gridcolor = props.get_gridcolor_rgb ();
-        Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
-        double gridalpha = props.get_gridalpha ();
-        double minorgridalpha = props.get_minorgridalpha ();
-        bool do_zgrid = (props.is_zgrid () && (gridstyle != "none"));
-        bool do_zminorgrid = (props.is_zminorgrid ()
-                              && (minorgridstyle != "none")
-                              && ! zticks.isempty ());
-        bool mirror = props.is_box () && zstate != AXE_ANY_DIR;
-
-        // Z grid
-
-        // possibly use axis color for gridcolor & minorgridcolor
-        if (props.gridcolormode_is ("auto"))
-          if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none"))
-            gridcolor = props.get_zcolor_rgb ();
-
-        if (props.minorgridcolormode_is ("auto"))
-          if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none"))
-            minorgridcolor = props.get_zcolor_rgb ();
-
-        if (gridcolor.isempty ())
-          do_zgrid = false;
-
-        if (minorgridcolor.isempty ())
-          do_zminorgrid = false;
-
-        // set styles when drawing only minor grid
-        if (do_zminorgrid && ! do_zgrid)
-          {
-            gridstyle = minorgridstyle;
-            gridcolor = minorgridcolor;
-            gridalpha = minorgridalpha;
-            do_zgrid = true;
-          }
-
-        // minor grid lines
-        if (do_zminorgrid)
-          render_grid (linewidth,
-                       minorgridstyle, minorgridcolor, minorgridalpha,
-                       zmticks, z_min, z_max,
-                       xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
-
-        // grid lines
-        if (do_zgrid)
-          render_grid (linewidth,
-                       gridstyle, gridcolor, gridalpha,
-                       zticks, z_min, z_max,
-                       xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
-
-        // Skip drawing axis, ticks, and ticklabels when color is "none"
-        if (props.zcolor_is ("none"))
-          return;
-
-        set_color (props.get_zcolor_rgb ());
-
-        // minor tick marks
-        if (do_zminortick)
-          {
-            if (xySym)
-              {
-                if (math::isinf (fy))
-                  render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane,
-                                    yPlane, yPlane,
-                                    math::signum (xPlaneN-xPlane)*fx*zticklen/2,
-                                    0., 0., 2, mirror);
-                else
-                  render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN,
-                                    yPlane, yPlane, 0.,
-                                    math::signum (yPlane-yPlaneN)*fy*zticklen/2,
-                                    0., 2, false);
-              }
-            else
-              {
-                if (math::isinf (fx))
-                  render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
-                                    yPlaneN, yPlane, 0.,
-                                    math::signum (yPlaneN-yPlane)*fy*zticklen/2,
-                                    0., 2, mirror);
-                else
-                  render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
-                                    yPlaneN, yPlaneN,
-                                    math::signum (xPlane-xPlaneN)*fx*zticklen/2,
-                                    0., 0., 2, false);
-              }
-          }
-
-        // tick marks
-        if (xySym)
-          {
-            if (math::isinf (fy))
-              render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
-                                yPlane, yPlane,
-                                math::signum (xPlaneN-xPlane)*fx*zticklen,
-                                0., 0., 2, mirror);
-            else
-              render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN,
-                                yPlane, yPlane, 0.,
-                                math::signum (yPlane-yPlaneN)*fy*zticklen,
-                                0., 2, false);
-          }
-        else
-          {
-            if (math::isinf (fx))
-              render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
-                                yPlaneN, yPlane, 0.,
-                                math::signum (yPlaneN-yPlane)*fy*zticklen,
-                                0., 2, mirror);
-            else
-              render_tickmarks (zticks, z_min, z_max, xPlane, xPlane,
-                                yPlaneN, yPlane,
-                                math::signum (xPlane-xPlaneN)*fx*zticklen,
-                                0., 0., 2, false);
-          }
-
-        // tick texts
-        if (zticklabels.numel () > 0)
-          {
-            int halign = 2;
-            int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2));
-
-            if (xySym)
-              {
-                if (math::isinf (fy))
-                  render_ticktexts (zticks, zticklabels, z_min, z_max,
-                                    xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset,
-                                    yPlane, 2, halign, valign, wmax, hmax);
-                else
-                  render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN,
-                                    yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset,
-                                    2, halign, valign, wmax, hmax);
-              }
-            else
-              {
-                if (math::isinf (fx))
-                  render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane,
-                                    yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset,
-                                    2, halign, valign, wmax, hmax);
-                else
-                  render_ticktexts (zticks, zticklabels, z_min, z_max,
-                                    xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset,
-                                    yPlaneN, 2, halign, valign, wmax, hmax);
-              }
-          }
-
-        gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on");
-      }
-    else
-      gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off");
-  }
-
-  void
-  opengl_renderer::draw_axes_grids (const axes::properties& props)
-  {
+}
+
+void
+opengl_renderer::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)
+{
 #if defined (HAVE_OPENGL)
-    // Disable line smoothing for axes
-    GLboolean antialias;
-
-    m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias);
-
-    if (antialias == GL_TRUE)
-      m_glfcns.glDisable (GL_LINE_SMOOTH);
-
-    set_linecap ("butt");
-    set_linewidth (props.get_linewidth ());
-    set_font (props);
-    set_interpreter (props.get_ticklabelinterpreter ());
-
-    draw_axes_x_grid (props);
-    draw_axes_y_grid (props);
-    draw_axes_z_grid (props);
-
-    if (antialias == GL_TRUE)
-      m_glfcns.glEnable (GL_LINE_SMOOTH);
+
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glPushMatrix ();
+  m_glfcns.glLoadIdentity ();
+
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glPushMatrix ();
+  m_glfcns.glLoadIdentity ();
+  m_glfcns.glOrtho (0, width, height, 0, 1, -1);
+
+  m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
+  m_glfcns.glDisable (GL_DEPTH_TEST);
+
+  m_glfcns.glBegin (GL_POLYGON);
+  m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2),
+                      overlayalpha);
+  draw_zoom_rect (x1, y1, x2, y2);
+  m_glfcns.glEnd ();
+
+  m_glfcns.glLineWidth (borderwidth);
+  m_glfcns.glBegin (GL_LINE_STRIP);
+  m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2),
+                      borderalpha);
+  draw_zoom_rect (x1, y1, x2, y2);
+  m_glfcns.glEnd ();
+
+  m_glfcns.glPopAttrib ();
+
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glPopMatrix ();
+
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glPopMatrix ();
+
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (width);
+  octave_unused_parameter (height);
+  octave_unused_parameter (x1);
+  octave_unused_parameter (x2);
+  octave_unused_parameter (y1);
+  octave_unused_parameter (y2);
+  octave_unused_parameter (overlaycolor);
+  octave_unused_parameter (overlayalpha);
+  octave_unused_parameter (bordercolor);
+  octave_unused_parameter (borderalpha);
+  octave_unused_parameter (borderwidth);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+uint8NDArray
+opengl_renderer::get_pixels (int width, int height)
+{
+#if defined (HAVE_OPENGL)
+
+  m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1);
+  uint8NDArray pix(dim_vector (3, width, height), 0);
+
+  m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
+                        pix.fortran_vec ());
+
+  // Permute and flip data
+  Array<octave_idx_type> perm (dim_vector (3, 1));
+  perm(0) = 2;
+  perm(1) = 1;
+  perm(2) = 0;
+
+  Array<idx_vector> idx (dim_vector (3, 1));
+  idx(0) = idx_vector::make_range (height - 1, -1, height);
+  idx(1) = idx_vector::colon;
+  idx(2) = idx_vector::colon;
+
+  return pix.permute (perm).index (idx);
+
+#else
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  octave_unused_parameter (width);
+  octave_unused_parameter (height);
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_all_lights (const base_properties& props,
-                                    std::list<graphics_object>& obj_list)
-  {
+}
+
+void
+opengl_renderer::finish (void)
+{
+#if defined (HAVE_OPENGL)
+
+  m_glfcns.glFinish ();
+
+#else
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::setup_opengl_transformation (const axes::properties& props)
+{
 #if defined (HAVE_OPENGL)
-    gh_manager& gh_mgr = __get_gh_manager__ ();
-
-    Matrix children = props.get_all_children ();
-
-    for (octave_idx_type i = children.numel () - 1; i >= 0; i--)
-      {
-        graphics_object go = gh_mgr.get_object (children(i));
-
-        base_properties p = go.get_properties ();
-
-        if (p.is_visible ()
-            || (m_selecting && p.pickableparts_is ("all")))
-          {
-            if (go.isa ("light") && ! m_selecting)
-              {
-                if (m_current_light-GL_LIGHT0 < m_max_lights)
-                  {
-                    set_clipping (p.is_clipping ());
-                    draw (go);
-                    m_current_light++;
-                  }
-              }
-            else if (go.isa ("hggroup")
-                     && ! (m_selecting && p.pickableparts_is ("none")))
-              draw_all_lights (go.get_properties (), obj_list);
-            else if (! (m_selecting && p.pickableparts_is ("none")))
-              obj_list.push_back (go);
-          }
-      }
+
+  // setup OpenGL transformation
+
+  Matrix x_zlim = props.get_transform_zlim ();
+
+  // Expand the distance between the clipping planes symmetrically by
+  // an arbitrary factor (see bug #54551).
+  const double expansion_fac = 100.0;
+  // Also make sure that the distance between the clipping planes
+  // differs in single precision (see bug #58956).  This factor is also
+  // arbitrary.  Different values (>2) might also work.
+  const double single_prec_fac = 10.0;
+
+  double avgZ = x_zlim(0) / 2.0 + x_zlim(1) / 2.0;
+  double span
+    = std::max (expansion_fac * (x_zlim(1)-x_zlim(0)),
+                single_prec_fac * std::abs (avgZ)
+                * std::numeric_limits<float>::epsilon ());
+  m_xZ1 = avgZ - span;
+  m_xZ2 = avgZ + span;
+
+  Matrix x_mat1 = props.get_opengl_matrix_1 ();
+  Matrix x_mat2 = props.get_opengl_matrix_2 ();
+
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glLoadIdentity ();
+  m_glfcns.glScaled (1, 1, -1);
+  m_glfcns.glMultMatrixd (x_mat1.data ());
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glLoadIdentity ();
+
+  Matrix vp = get_viewport_scaled ();
+  m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2);
+  m_glfcns.glMultMatrixd (x_mat2.data ());
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+
+  m_glfcns.glClear (GL_DEPTH_BUFFER_BIT);
+
+  // store axes transformation data
+
+  m_xform = props.get_transform ();
+
 #else
 
-    octave_unused_parameter (props);
-    octave_unused_parameter (obj_list);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_axes_planes (const axes::properties& props)
+{
+#if defined (HAVE_OPENGL)
+
+  Matrix axe_color = props.get_color_rgb ();
+  if (axe_color.isempty () || ! props.is_visible ())
+    return;
+
+  double xPlane = props.get_xPlane ();
+  double yPlane = props.get_yPlane ();
+  double zPlane = props.get_zPlane ();
+  double xPlaneN = props.get_xPlaneN ();
+  double yPlaneN = props.get_yPlaneN ();
+  double zPlaneN = props.get_zPlaneN ();
+  bool is2D = props.get_is2D ();
+
+  // Axes planes
+  set_color (axe_color);
+  set_polygon_offset (true, 9.0);
+
+  m_glfcns.glBegin (GL_QUADS);
+
+  if (! is2D)
+    {
+      // X plane
+      m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
+      m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
+      m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+      m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
+
+      // Y plane
+      m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
+      m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
+      m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+      m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
+    }
+
+  // Z plane
+  m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
+  m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
+  m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+  m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
+
+  m_glfcns.glEnd ();
+
+  set_polygon_offset (false);
+
+#else
+
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_axes_children (const axes::properties& props)
-  {
+}
+
+void
+opengl_renderer::draw_axes_boxes (const axes::properties& props)
+{
 #if defined (HAVE_OPENGL)
-    // list for non-light child objects
-    std::list<graphics_object> obj_list;
-    std::list<graphics_object>::iterator it;
-
-    // 1st pass: draw light objects
-
-    // FIXME: max_lights only needs to be set once.
-    // It would be better if this could be in the constructor for gl_renderer
-    // but this seems to lead to calls of OpenGL functions before the context
-    // is actually initialized.  See bug #48669.
-    // Check actual maximum number of lights possible
-    init_maxlights ();
-
-    // Start with the last element of the array of child objects to
-    // display them in the order they were added to the array.
-
-    if (props.get_num_lights () > m_max_lights)
-      warning_with_id ("Octave:max-lights-exceeded",
-                       "light: Maximum number of lights (%d) in these axes is "
-                       "exceeded.", m_max_lights);
-
-    m_current_light = GL_LIGHT0;
-    draw_all_lights (props, obj_list);
-
-    // disable other OpenGL lights
-    for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++)
-      m_glfcns.glDisable (GL_LIGHT0 + i);
-
-    // save camera position and set ambient light color before drawing
-    // other objects
-    m_view_vector = props.get_cameraposition ().matrix_value ();
-
-    float cb[4] = { 1.0, 1.0, 1.0, 1.0 };
-    ColumnVector ambient_color = props.get_ambientlightcolor_rgb ();
-    for (int i = 0; i < 3; i++)
-      cb[i] = ambient_color(i);
-    m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb);
-
-    // 2nd pass: draw other objects (with units set to "data")
-
-    it = obj_list.begin ();
-    while (it != obj_list.end ())
-      {
-        graphics_object go = (*it);
-
-        // FIXME: check whether object has "units" property and it is set
-        // to "data"
-        if (! go.isa ("text") || go.get ("units").string_value () == "data")
-          {
-            set_clipping (go.get_properties ().is_clipping ());
-            draw (go);
-
-            it = obj_list.erase (it);
-          }
-        else
-          it++;
-      }
-
-    // 3rd pass: draw remaining objects
-
-    m_glfcns.glDisable (GL_DEPTH_TEST);
-
-    for (it = obj_list.begin (); it != obj_list.end (); it++)
-      {
-        graphics_object go = (*it);
-
-        set_clipping (go.get_properties ().is_clipping ());
-        draw (go);
-      }
-
-    set_clipping (false);
-
-    // FIXME: finalize rendering (transparency processing)
-    // FIXME: draw zoom box, if needed
-
-#else
-
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::draw_axes (const axes::properties& props)
-  {
-#if defined (HAVE_OPENGL)
-
-    // Legends are not drawn when "visible" is "off".
-    if (! props.is_visible () && props.get_tag () == "legend")
-      return;
-
-    // Don't draw the axes and its children if we are in selection and
-    // pickable parts is "none".
-    if (m_selecting && props.pickableparts_is ("none"))
-      return;
-
-    static double floatmax = std::numeric_limits<float>::max ();
-
-    double x_min = props.get_x_min ();
-    double x_max = props.get_x_max ();
-    double y_min = props.get_y_min ();
-    double y_max = props.get_y_max ();
-    double z_min = props.get_z_min ();
-    double z_max = props.get_z_max ();
-
-    if (x_max > floatmax || y_max > floatmax || z_max > floatmax
-        || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax)
-      {
-        warning ("opengl_renderer: data values greater than float capacity.  (1) Scale data, or (2) Use gnuplot");
-        return;
-      }
-
-    setup_opengl_transformation (props);
-
-    // For 2D axes with only 2D primitives, draw from back to front without
-    // depth sorting
-    bool is2D = props.get_is2D (true);
-    if (is2D)
-      m_glfcns.glDisable (GL_DEPTH_TEST);
-    else
-      m_glfcns.glEnable (GL_DEPTH_TEST);
-
-    draw_axes_planes (props);
-
-    if (! is2D || props.layer_is ("bottom"))
-      {
-        draw_axes_grids (props);
-        if (props.get_tag () != "legend" || props.get_box () != "off")
-          draw_axes_boxes (props);
-      }
-
-    set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max);
-
-    draw_axes_children (props);
-
-    if (is2D && props.layer_is ("top"))
-      {
-        draw_axes_grids (props);
-        if (props.get_tag () != "legend" || props.get_box () != "off")
-          draw_axes_boxes (props);
-      }
+
+  if (! props.is_visible ())
+    return;
+
+  bool xySym = props.get_xySym ();
+  bool layer2Dtop = props.get_layer2Dtop ();
+  bool is2D = props.get_is2D ();
+  bool isXOrigin = props.xaxislocation_is ("origin")
+                   && ! props.yscale_is ("log");
+  bool isYOrigin = props.yaxislocation_is ("origin")
+                   && ! props.xscale_is ("log");
+  bool boxFull = (props.get_boxstyle () == "full");
+  double linewidth = props.get_linewidth ();
+  double xPlane = props.get_xPlane ();
+  double yPlane = props.get_yPlane ();
+  double zPlane = props.get_zPlane ();
+  double xPlaneN = props.get_xPlaneN ();
+  double yPlaneN = props.get_yPlaneN ();
+  double zPlaneN = props.get_zPlaneN ();
+  double xpTick = props.get_xpTick ();
+  double ypTick = props.get_ypTick ();
+  double zpTick = props.get_zpTick ();
+  double xpTickN = props.get_xpTickN ();
+  double ypTickN = props.get_ypTickN ();
+  double zpTickN = props.get_zpTickN ();
+
+  bool plotyy = (props.has_property ("__plotyy_axes__"));
+
+  // Axes box
+
+  set_linecap ("square");
+  set_linestyle ("-", true, linewidth);
+
+  m_glfcns.glBegin (GL_LINES);
+
+  if (layer2Dtop)
+    std::swap (zpTick, zpTickN);
+
+  // X box
+  Matrix color = props.get_xcolor_rgb ();
+
+  if (! color.isempty ())
+    {
+      set_color (color);
+
+      if (! isXOrigin || props.is_box() || ! is2D)
+        {
+          m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick);
+          m_glfcns.glVertex3d (xPlane, ypTick, zpTick);
+        }
+
+      if (props.is_box ())
+        {
+          m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick);
+          m_glfcns.glVertex3d (xPlane, ypTickN, zpTick);
+          if (! is2D)
+            {
+              m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN);
+              m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN);
+              if (boxFull)
+                {
+                  m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN);
+                  m_glfcns.glVertex3d (xPlane, ypTick, zpTickN);
+                }
+            }
+        }
+    }
+
+  // Y box
+  color = props.get_ycolor_rgb ();
+
+  if (! color.isempty ())
+    {
+      set_color (color);
+      if (! isYOrigin || props.is_box() || ! is2D)
+        {
+          m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick);
+          m_glfcns.glVertex3d (xpTick, yPlane, zpTick);
+        }
+
+      if (props.is_box () && ! plotyy)
+        {
+          m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick);
+          m_glfcns.glVertex3d (xpTickN, yPlane, zpTick);
+
+          if (! is2D)
+            {
+              m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN);
+              m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN);
+              if (boxFull)
+                {
+                  m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN);
+                  m_glfcns.glVertex3d (xpTick, yPlane, zpTickN);
+                }
+            }
+        }
+    }
+
+  // Z box
+  color = props.get_zcolor_rgb ();
+
+  if (! color.isempty () && ! is2D)
+    {
+      set_color (color);
+
+      if (xySym)
+        {
+          m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
+          m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
+        }
+      else
+        {
+          m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
+          m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
+        }
+
+      if (props.is_box ())
+        {
+          m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
+          m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+
+          if (xySym)
+            {
+              m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
+              m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
+            }
+          else
+            {
+              m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
+              m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
+            }
+
+          if (boxFull)
+            {
+              m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN);
+              m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
+            }
+        }
+    }
+
+  m_glfcns.glEnd ();
+
+  set_linestyle ("-");  // Disable LineStipple
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_line (const line::properties& props)
-  {
+}
+
+void
+opengl_renderer::draw_axes_x_grid (const axes::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    bool draw_all = m_selecting && props.pickableparts_is ("all");
-
-    Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ());
-    Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ());
-    Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ());
-
-    bool has_z = (z.numel () > 0);
-    int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()),
-                                        (has_z ? z.numel ()
-                                         : std::numeric_limits<int>::max ())));
-    uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
-    uint8_t clip_ok = 0x40;
-
-    std::vector<uint8_t> clip (n);
-
-    if (has_z)
-      for (int i = 0; i < n; i++)
-        clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask);
-    else
-      {
-        double z_mid = (m_zmin+m_zmax)/2;
-
-        for (int i = 0; i < n; i++)
-          clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask);
-      }
-
-    if (! props.linestyle_is ("none") && ! props.color_is ("none"))
-      {
-        set_color (props.get_color_rgb ());
-        set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
-        set_linewidth (props.get_linewidth ());
-        set_linecap ("butt");
-        set_linejoin (props.get_linejoin ());
-
-        if (has_z)
-          {
-            bool flag = false;
-
-            for (int i = 1; i < n; i++)
-              {
-                if ((clip[i-1] & clip[i]) == clip_ok)
-                  {
-                    if (! flag)
-                      {
-                        flag = true;
-                        m_glfcns.glBegin (GL_LINE_STRIP);
-                        m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1));
-                      }
-                    m_glfcns.glVertex3d (x(i), y(i), z(i));
-                  }
-                else if (flag)
-                  {
-                    flag = false;
-                    m_glfcns.glEnd ();
-                  }
-              }
-
-            if (flag)
-              m_glfcns.glEnd ();
-          }
-        else
-          {
-            bool flag = false;
-
-            for (int i = 1; i < n; i++)
-              {
-                if ((clip[i-1] & clip[i]) == clip_ok)
-                  {
-                    if (! flag)
-                      {
-                        flag = true;
-                        m_glfcns.glBegin (GL_LINE_STRIP);
-                        m_glfcns.glVertex2d (x(i-1), y(i-1));
-                      }
-                    m_glfcns.glVertex2d (x(i), y(i));
-                  }
-                else if (flag)
-                  {
-                    flag = false;
-                    m_glfcns.glEnd ();
-                  }
-              }
-
-            if (flag)
-              m_glfcns.glEnd ();
-          }
-
-        set_linewidth (0.5f);
-        set_linestyle ("-");
-      }
-
-    set_clipping (false);
-
-    if (! props.marker_is ("none")
-        && ! (props.markeredgecolor_is ("none")
-              && props.markerfacecolor_is ("none")))
-      {
-        Matrix lc, fc;
-
-        if (draw_all)
-          lc = Matrix (1, 3, 0.0);
-        else if (props.markeredgecolor_is ("auto"))
-          lc = props.get_color_rgb ();
-        else if (! props.markeredgecolor_is ("none"))
-          lc = props.get_markeredgecolor_rgb ();
-
-        if (draw_all)
-          fc = Matrix (1, 3, 0.0);
-        if (props.markerfacecolor_is ("auto"))
-          fc = props.get_color_rgb ();
-        else if (! props.markerfacecolor_is ("none"))
-          fc = props.get_markerfacecolor_rgb ();
-
-        init_marker (props.get_marker (), props.get_markersize (),
-                     props.get_linewidth ());
-
-        for (int i = 0; i < n; i++)
-          {
-            if (clip[i] == clip_ok)
-              draw_marker (x(i), y(i),
-                           has_z ? z(i) : 0.0,
-                           lc, fc);
-          }
-
-        end_marker ();
-      }
-
-    set_clipping (props.is_clipping ());
+  gh_manager& gh_mgr = __get_gh_manager__ ();
+
+  int xstate = props.get_xstate ();
+
+  if (xstate != AXE_DEPTH_DIR
+      && (props.is_visible ()
+          || (m_selecting && props.pickableparts_is ("all"))))
+    {
+      int zstate = props.get_zstate ();
+      bool x2Dtop = props.get_x2Dtop ();
+      bool layer2Dtop = props.get_layer2Dtop ();
+      bool xyzSym = props.get_xyzSym ();
+      bool nearhoriz = props.get_nearhoriz ();
+      double xticklen = props.get_xticklen ();
+      double xtickoffset = props.get_xtickoffset ();
+      double fy = props.get_fy ();
+      double fz = props.get_fz ();
+      double x_min = props.get_x_min ();
+      double x_max = props.get_x_max ();
+      double y_min = props.get_y_min ();
+      double y_max = props.get_y_max ();
+      double yPlane = props.get_yPlane ();
+      double yPlaneN = props.get_yPlaneN ();
+      double ypTick = props.get_ypTick ();
+      double ypTickN = props.get_ypTickN ();
+      double zPlane = props.get_zPlane ();
+      double zPlaneN = props.get_zPlaneN ();
+      double zpTick = props.get_zpTick ();
+      double zpTickN = props.get_zpTickN ();
+
+      // X ticks and grid properties
+      Matrix xticks = m_xform.xscale (props.get_xtick ().matrix_value ());
+      Matrix xmticks = m_xform.xscale (props.get_xminortickvalues ().matrix_value ());
+      bool do_xminortick = props.is_xminortick () && ! xticks.isempty ();
+      string_vector xticklabels = props.get_xticklabel ().string_vector_value ();
+      int wmax = 0;
+      int hmax = 0;
+      bool tick_along_z = nearhoriz || math::isinf (fy);
+      double linewidth = props.get_linewidth ();
+      std::string gridstyle = props.get_gridlinestyle ();
+      std::string minorgridstyle = props.get_minorgridlinestyle ();
+      Matrix gridcolor = props.get_gridcolor_rgb ();
+      Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
+      double gridalpha = props.get_gridalpha ();
+      double minorgridalpha = props.get_minorgridalpha ();
+      bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
+      bool do_xminorgrid = (props.is_xminorgrid ()
+                            && (minorgridstyle != "none")
+                            && ! xticks.isempty ());
+      bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D ()
+                       && ! props.yscale_is ("log");
+      bool is_origin_low = is_origin && (y_min + y_max) < 0;
+      bool mirror = props.is_box () && xstate != AXE_ANY_DIR;
+
+      // X grid
+
+      // possibly use axis color for gridcolor & minorgridcolor
+      if (props.gridcolormode_is ("auto"))
+        if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none"))
+          gridcolor = props.get_xcolor_rgb ();
+
+      if (props.minorgridcolormode_is ("auto"))
+        if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none"))
+          minorgridcolor = props.get_xcolor_rgb ();
+
+      if (gridcolor.isempty ())
+        do_xgrid = false;
+
+      if (minorgridcolor.isempty ())
+        do_xminorgrid = false;
+
+      // set styles when drawing only minor grid
+      if (do_xminorgrid && ! do_xgrid)
+        {
+          gridstyle = minorgridstyle;
+          gridcolor = minorgridcolor;
+          gridalpha = minorgridalpha;
+          do_xgrid = true;
+        }
+
+      // minor grid lines
+      if (do_xminorgrid)
+        render_grid (linewidth,
+                     minorgridstyle, minorgridcolor, minorgridalpha,
+                     xmticks, x_min, x_max,
+                     yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+                     0, (zstate != AXE_DEPTH_DIR));
+
+      // grid lines
+      if (do_xgrid)
+        render_grid (linewidth,
+                     gridstyle, gridcolor, gridalpha,
+                     xticks, x_min, x_max,
+                     yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+                     0, (zstate != AXE_DEPTH_DIR));
+
+      // Skip drawing axis, ticks, and ticklabels when color is "none"
+      if (props.xcolor_is ("none"))
+        return;
+
+      set_color (props.get_xcolor_rgb ());
+
+      // axis line
+      double y_axis_pos = 0.;
+      if (is_origin)
+        {
+          y_axis_pos = math::max (math::min (0., y_max), y_min);
+          m_glfcns.glBegin (GL_LINES);
+          set_color (props.get_xcolor_rgb ());
+          m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick);
+          m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick);
+          m_glfcns.glEnd ();
+        }
+
+      // minor tick marks
+      if (do_xminortick)
+        {
+          if (tick_along_z)
+            render_tickmarks (xmticks, x_min, x_max,
+                              is_origin ? y_axis_pos : ypTick, ypTick,
+                              zpTick, zpTickN, 0., 0.,
+                              (is_origin_low ? -1. : 1.) *
+                              math::signum (zpTick-zpTickN)*fz*xticklen/2,
+                              0, ! is_origin && mirror);
+          else
+            render_tickmarks (xmticks, x_min, x_max,
+                              is_origin ? y_axis_pos : ypTick, ypTickN,
+                              zpTick, zpTick, 0.,
+                              (is_origin_low ? -1. : 1.) *
+                              math::signum (ypTick-ypTickN)*fy*xticklen/2,
+                              0., 0, ! is_origin && mirror);
+        }
+
+      // tick marks
+      if (tick_along_z)
+        render_tickmarks (xticks, x_min, x_max,
+                          is_origin ? y_axis_pos : ypTick, ypTick,
+                          zpTick, zpTickN, 0., 0.,
+                          (is_origin_low ? -1. : 1.) *
+                          math::signum (zpTick-zpTickN)*fz*xticklen,
+                          0, ! is_origin && mirror);
+      else
+        render_tickmarks (xticks, x_min, x_max,
+                          is_origin ? y_axis_pos : ypTick, ypTickN,
+                          zpTick, zpTick, 0.,
+                          (is_origin_low ? -1. : 1.) *
+                          math::signum (ypTick-ypTickN)*fy*xticklen,
+                          0., 0, ! is_origin && mirror);
+
+      // tick texts
+      if (xticklabels.numel () > 0)
+        {
+          int halign = (xstate == AXE_HORZ_DIR
+                        ? 1
+                        : (xyzSym || is_origin_low ? 0 : 2));
+          int valign = (xstate == AXE_VERT_DIR
+                        ? 1
+                        : (x2Dtop || is_origin_low ? 0 : 2));
+
+          if (tick_along_z)
+            render_ticktexts (xticks, xticklabels, x_min, x_max,
+                              is_origin ? y_axis_pos : ypTick,
+                              zpTick +
+                              (is_origin_low ? -1. : 1.) *
+                              math::signum (zpTick-zpTickN)*fz*xtickoffset,
+                              0, halign, valign, wmax, hmax);
+          else
+            render_ticktexts (xticks, xticklabels, x_min, x_max,
+                              (is_origin ? y_axis_pos : ypTick) +
+                              (is_origin_low ?  -1. : 1.) *
+                              math::signum (ypTick-ypTickN)*fy*xtickoffset,
+                              zpTick, 0, halign, valign, wmax, hmax);
+        }
+
+      gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on");
+    }
+  else
+    gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off");
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_surface (const surface::properties& props)
-  {
+}
+
+void
+opengl_renderer::draw_axes_y_grid (const axes::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    bool draw_all = m_selecting && props.pickableparts_is ("all");
-
-    const Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ());
-    const Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ());
-    const Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ());
-
-    int zr = z.rows ();
-    int zc = z.columns ();
-
-    NDArray c;
-    const NDArray vn = props.get_vertexnormals ().array_value ();
-    dim_vector vn_dims = vn.dims ();
-    bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc
-                               && vn_dims(2) == 3);
-    const NDArray fn = props.get_facenormals ().array_value ();
-    dim_vector fn_dims = fn.dims ();
-    bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1
-                             && fn_dims(2) == 3);
-
-    // FIXME: handle transparency
-    Matrix a;
-
-    int fc_mode = (props.facecolor_is_rgb () ? 0 :
-                   (props.facecolor_is ("flat") ? 1 :
-                    (props.facecolor_is ("interp") ? 2 :
-                     (props.facecolor_is ("texturemap") ? 3 : -1))));
-    int fl_mode = (props.facelighting_is ("none") ? 0 :
-                   (props.facelighting_is ("flat") ?
-                    (has_face_normals ? 1 : 0) :
-                    (has_vertex_normals ? 2 : 0)));
-    int fa_mode = (props.facealpha_is_double () ? 0 :
-                   (props.facealpha_is ("flat") ? 1 : 2));
-    int ec_mode = (props.edgecolor_is_rgb () ? 0 :
-                   (props.edgecolor_is ("flat") ? 1 :
-                    (props.edgecolor_is ("interp") ? 2 : -1)));
-    int el_mode = (props.edgelighting_is ("none") ? 0 :
-                   (props.edgelighting_is ("flat") ?
-                    (has_face_normals ? 1 : 0) :
-                    (has_vertex_normals ? 2 : 0)));
-    int ea_mode = (props.edgealpha_is_double () ? 0 :
-                   (props.edgealpha_is ("flat") ? 1 : 2));
-    int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
-                    (props.backfacelighting_is ("reverselit") ? 1 : 2));
-    bool do_lighting = props.get_do_lighting ();
-
-    Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0)
-                                        : props.get_facecolor_rgb ());
-    Matrix ecolor = props.get_edgecolor_rgb ();
-    double fa = 1.0;
-
-    float as = props.get_ambientstrength ();
-    float ds = props.get_diffusestrength ();
-    float ss = props.get_specularstrength ();
-    float se = props.get_specularexponent () * 5; // to fit Matlab
-    float scr = props.get_specularcolorreflectance ();
-    float cb[4] = { 0.0, 0.0, 0.0, 1.0 };
-
-    opengl_texture tex (m_glfcns);
-
-    int i1, i2, j1, j2;
-    bool x_mat = (x.rows () == z.rows ());
-    bool y_mat = (y.columns () == z.columns ());
-
-    i1 = i2 = j1 = j2 = 0;
-
-    if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0)
-      c = props.get_color_data ().array_value ();
-
-    boolMatrix clip (z.dims (), false);
-
-    for (int i = 0; i < zr; i++)
-      {
-        if (x_mat)
-          i1 = i;
-
-        for (int j = 0; j < zc; j++)
-          {
-            if (y_mat)
-              j1 = j;
-
-            clip(i,j) = is_nan_or_inf (x(i1,j), y(i,j1), z(i,j));
-          }
-      }
-
-    if (fa_mode > 0 || ea_mode > 0)
-      {
-        // FIXME: implement alphadata conversion
-        //a = props.get_alpha_data ();
-      }
-
-    if (fl_mode > 0 || el_mode > 0)
-      m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
-
-    // FIXME: good candidate for caching,
-    //        transferring pixel data to OpenGL is time consuming.
-    if (fc_mode == TEXTURE)
-      tex = opengl_texture::create (m_glfcns, props.get_color_data ());
-
-    if (draw_all || ! props.facecolor_is ("none"))
-      {
-        if (fa_mode == 0)
-          {
-            fa = props.get_facealpha_double ();
-            cb[3] = fa;
-            if (fc_mode == UNIFORM || fc_mode == TEXTURE)
-              {
-                m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
-                if (fl_mode > 0)
-                  {
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = as * fcolor(i);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = ds * fcolor(i);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = ss * (scr + (1-scr) * fcolor(i));
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                  }
-              }
-
-            if ((fl_mode > 0) && do_lighting)
-              m_glfcns.glEnable (GL_LIGHTING);
-            m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD)
-                                   ? GL_SMOOTH : GL_FLAT);
-            set_polygon_offset (true, 1.0);
-            if (fc_mode == TEXTURE)
-              m_glfcns.glEnable (GL_TEXTURE_2D);
-
-            for (int i = 1; i < zc; i++)
-              {
-                if (y_mat)
-                  {
-                    i1 = i-1;
-                    i2 = i;
-                  }
-
-                for (int j = 1; j < zr; j++)
-                  {
-
-                    if (clip(j-1, i-1) || clip(j, i-1)
-                        || clip(j-1, i) || clip(j, i))
-                      continue;
-
-                    if (fc_mode == FLAT)
-                      {
-                        // "flat" only needs color at lower-left vertex
-                        if (! math::isfinite (c(j-1,i-1)))
-                          continue;
-                      }
-                    else if (fc_mode == INTERP)
-                      {
-                        // "interp" needs valid color at all 4 vertices
-                        if (! (math::isfinite (c(j-1, i-1))
-                               && math::isfinite (c(j, i-1))
-                               && math::isfinite (c(j-1, i))
-                               && math::isfinite (c(j, i))))
-                          continue;
-                      }
-
-                    if (x_mat)
-                      {
-                        j1 = j-1;
-                        j2 = j;
-                      }
-
-                    m_glfcns.glBegin (GL_QUADS);
-
-                    // Vertex 1
-                    if (fc_mode == TEXTURE)
-                      tex.tex_coord (double (i-1) / (zc-1),
-                                     double (j-1) / (zr-1));
-                    else if (fc_mode > 0)
-                      {
-                        // FIXME: is there a smarter way to do this?
-                        for (int k = 0; k < 3; k++)
-                          cb[k] = c(j-1, i-1, k);
-                        m_glfcns.glColor4fv (cb);
-
-                        if (fl_mode > 0)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] *= as;
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ds * c(j-1, i-1, k);
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k));
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                          }
-                      }
-                    if (fl_mode > 0)
-                      set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn),
-                                  j-1, i-1);
-
-                    m_glfcns.glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
-
-                    // Vertex 2
-                    if (fc_mode == TEXTURE)
-                      tex.tex_coord (double (i) / (zc-1),
-                                     double (j-1) / (zr-1));
-                    else if (fc_mode == INTERP)
-                      {
-                        for (int k = 0; k < 3; k++)
-                          cb[k] = c(j-1, i, k);
-                        m_glfcns.glColor4fv (cb);
-
-                        if (fl_mode > 0)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] *= as;
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ds * c(j-1, i, k);
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                          }
-                      }
-
-                    if (fl_mode == GOURAUD)
-                      set_normal (bfl_mode, vn, j-1, i);
-
-                    m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
-
-                    // Vertex 3
-                    if (fc_mode == TEXTURE)
-                      tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1));
-                    else if (fc_mode == INTERP)
-                      {
-                        for (int k = 0; k < 3; k++)
-                          cb[k] = c(j, i, k);
-                        m_glfcns.glColor4fv (cb);
-
-                        if (fl_mode > 0)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] *= as;
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ds * c(j, i, k);
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ss * (scr + (1-scr) * c(j, i, k));
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                          }
-                      }
-                    if (fl_mode == GOURAUD)
-                      set_normal (bfl_mode, vn, j, i);
-
-                    m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
-
-                    // Vertex 4
-                    if (fc_mode == TEXTURE)
-                      tex.tex_coord (double (i-1) / (zc-1),
-                                     double (j) / (zr-1));
-                    else if (fc_mode == INTERP)
-                      {
-                        for (int k = 0; k < 3; k++)
-                          cb[k] = c(j, i-1, k);
-                        m_glfcns.glColor4fv (cb);
-
-                        if (fl_mode > 0)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] *= as;
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ds * c(j, i-1, k);
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
-                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                          }
-                      }
-                    if (fl_mode == GOURAUD)
-                      set_normal (bfl_mode, vn, j, i-1);
-
-                    m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
-
-                    m_glfcns.glEnd ();
-                  }
-              }
-
-            set_polygon_offset (false);
-            if (fc_mode == TEXTURE)
-              m_glfcns.glDisable (GL_TEXTURE_2D);
-
-            if ((fl_mode > 0) && do_lighting)
-              m_glfcns.glDisable (GL_LIGHTING);
-          }
-        else
-          {
-            // FIXME: implement flat, interp and texturemap transparency
-          }
-      }
-
-    if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))
-      {
-        if (props.get_edgealpha_double () == 1)
-          {
-            cb[3] = 1.0; // edgealpha isn't implemented yet
-            if (ec_mode == UNIFORM)
-              {
-                m_glfcns.glColor3dv (ecolor.data ());
-                if (el_mode > 0)
-                  {
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = as * ecolor(i);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = ds * ecolor(i);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = ss * (scr + (1-scr) * ecolor(i));
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                  }
-              }
-
-            if ((el_mode > 0) && do_lighting)
-              m_glfcns.glEnable (GL_LIGHTING);
-            m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
-                                   ? GL_SMOOTH : GL_FLAT);
-
-            set_linestyle (props.get_linestyle (), false,
-                           props.get_linewidth ());
-            set_linewidth (props.get_linewidth ());
-            set_linecap ("butt");
-            set_linejoin ("miter");
-
-            // Mesh along Y-axis
-
-            if (props.meshstyle_is ("both") || props.meshstyle_is ("column"))
-              {
-                for (int i = 0; i < zc; i++)
-                  {
-                    if (y_mat)
-                      {
-                        i1 = i-1;
-                        i2 = i;
-                      }
-
-                    for (int j = 1; j < zr; j++)
-                      {
-                        if (clip(j-1,i) || clip(j,i))
-                          continue;
-
-                        if (ec_mode == FLAT)
-                          {
-                            // "flat" only needs color at lower-left vertex
-                            if (! math::isfinite (c(j-1,i)))
-                              continue;
-                          }
-                        else if (ec_mode == INTERP)
-                          {
-                            // "interp" needs valid color at both vertices
-                            if (! (math::isfinite (c(j-1, i))
-                                   && math::isfinite (c(j, i))))
-                              continue;
-                          }
-
-                        if (x_mat)
-                          {
-                            j1 = j-1;
-                            j2 = j;
-                          }
-
-                        m_glfcns.glBegin (GL_LINES);
-
-                        // Vertex 1
-                        if (ec_mode > 0)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = c(j-1, i, k);
-                            m_glfcns.glColor3fv (cb);
-
-                            if (el_mode > 0)
-                              {
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] *= as;
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ds * c(j-1, i, k);
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
-                                                       cb);
-                              }
-                          }
-                        if (el_mode > 0)
-                          {
-                            if (el_mode == GOURAUD)
-                              set_normal (bfl_mode, vn, j-1, i);
-                            else
-                              set_normal (bfl_mode, fn, j-1, std::min (i, zc-2));
-                          }
-
-                        m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
-
-                        // Vertex 2
-                        if (ec_mode == INTERP)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = c(j, i, k);
-                            m_glfcns.glColor3fv (cb);
-
-                            if (el_mode > 0)
-                              {
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] *= as;
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ds * c(j, i, k);
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ss * (scr + (1-scr) * c(j, i, k));
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
-                                                       cb);
-                              }
-                          }
-                        if (el_mode == GOURAUD)
-                          set_normal (bfl_mode, vn, j, i);
-
-                        m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
-
-                        m_glfcns.glEnd ();
-                      }
-                  }
-              }
-
-            // Mesh along X-axis
-
-            if (props.meshstyle_is ("both") || props.meshstyle_is ("row"))
-              {
-                for (int j = 0; j < zr; j++)
-                  {
-                    if (x_mat)
-                      {
-                        j1 = j-1;
-                        j2 = j;
-                      }
-
-                    for (int i = 1; i < zc; i++)
-                      {
-                        if (clip(j,i-1) || clip(j,i))
-                          continue;
-
-                        if (ec_mode == FLAT)
-                          {
-                            // "flat" only needs color at lower-left vertex
-                            if (! math::isfinite (c(j,i-1)))
-                              continue;
-                          }
-                        else if (ec_mode == INTERP)
-                          {
-                            // "interp" needs valid color at both vertices
-                            if (! (math::isfinite (c(j, i-1))
-                                   && math::isfinite (c(j, i))))
-                              continue;
-                          }
-
-                        if (y_mat)
-                          {
-                            i1 = i-1;
-                            i2 = i;
-                          }
-
-                        m_glfcns.glBegin (GL_LINES);
-
-                        // Vertex 1
-                        if (ec_mode > 0)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = c(j, i-1, k);
-                            m_glfcns.glColor3fv (cb);
-
-                            if (el_mode > 0)
-                              {
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] *= as;
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ds * c(j, i-1, k);
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
-                                                       cb);
-                              }
-                          }
-                        if (el_mode > 0)
-                          {
-                            if (el_mode == GOURAUD)
-                              set_normal (bfl_mode, vn, j, i-1);
-                            else
-                              set_normal (bfl_mode, fn, std::min (j, zr-2), i-1);
-                          }
-
-                        m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
-
-                        // Vertex 2
-                        if (ec_mode == INTERP)
-                          {
-                            for (int k = 0; k < 3; k++)
-                              cb[k] = c(j, i, k);
-                            m_glfcns.glColor3fv (cb);
-
-                            if (el_mode > 0)
-                              {
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] *= as;
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ds * c(j, i, k);
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
-                                                       cb);
-
-                                for (int k = 0; k < 3; k++)
-                                  cb[k] = ss * (scr + (1-scr) * c(j, i, k));
-                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
-                                                       cb);
-                              }
-                          }
-                        if (el_mode == GOURAUD)
-                          set_normal (bfl_mode, vn, j, i);
-
-                        m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
-
-                        m_glfcns.glEnd ();
-                      }
-                  }
-              }
-
-            set_linestyle ("-");  // Disable LineStipple
-            set_linewidth (0.5f);
-
-            if ((el_mode > 0) && do_lighting)
-              m_glfcns.glDisable (GL_LIGHTING);
-          }
-        else
-          {
-            // FIXME: implement transparency
-          }
-      }
-
-    if (! props.marker_is ("none")
-        && ! (props.markeredgecolor_is ("none")
-              && props.markerfacecolor_is ("none")))
-      {
-        // FIXME: check how transparency should be handled in markers
-        // FIXME: check what to do with marker facecolor set to auto
-        //        and facecolor set to none.
-
-        bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
-        bool do_face = draw_all || ! props.markerfacecolor_is ("none");
-
-        Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
-                          props.get_markeredgecolor_rgb ());
-        Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
-                          props.get_markerfacecolor_rgb ());
-        Matrix cc (1, 3, 0.0);
-
-        if (mecolor.isempty () && props.markeredgecolor_is ("auto"))
-          {
-            mecolor = props.get_edgecolor_rgb ();
-            do_edge = ! props.edgecolor_is ("none");
-          }
-
-        if (mfcolor.isempty () && props.markerfacecolor_is ("auto"))
-          {
-            mfcolor = props.get_facecolor_rgb ();
-            do_face = ! props.facecolor_is ("none");
-          }
-
-        if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ())
-          c = props.get_color_data ().array_value ();
-
-        init_marker (props.get_marker (), props.get_markersize (),
-                     props.get_linewidth ());
-
-        uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
-        uint8_t clip_ok = 0x40;
-
-        for (int i = 0; i < zc; i++)
-          {
-            if (y_mat)
-              i1 = i;
-
-            for (int j = 0; j < zr; j++)
-              {
-                if (x_mat)
-                  j1 = j;
-
-                if ((clip_code (x(j1,i), y(j,i1), z(j,i)) & clip_mask)
-                    != clip_ok)
-                  continue;
-
-                if ((do_edge && mecolor.isempty ())
-                    || (do_face && mfcolor.isempty ()))
-                  {
-                    if (! math::isfinite (c(j,i)))
-                      continue;  // Skip NaNs in color data
-
-                    for (int k = 0; k < 3; k++)
-                      cc(k) = c(j,i,k);
-                  }
-
-                Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor)
-                                     : Matrix ());
-                Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor)
-                                     : Matrix ());
-
-                draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc);
-              }
-          }
-
-        end_marker ();
-      }
+  gh_manager& gh_mgr = __get_gh_manager__ ();
+
+  int ystate = props.get_ystate ();
+
+  if (ystate != AXE_DEPTH_DIR && props.is_visible ()
+      && (props.is_visible ()
+          || (m_selecting && props.pickableparts_is ("all"))))
+    {
+      int zstate = props.get_zstate ();
+      bool y2Dright = props.get_y2Dright ();
+      bool layer2Dtop = props.get_layer2Dtop ();
+      bool xyzSym = props.get_xyzSym ();
+      bool nearhoriz = props.get_nearhoriz ();
+      double yticklen = props.get_yticklen ();
+      double ytickoffset = props.get_ytickoffset ();
+      double fx = props.get_fx ();
+      double fz = props.get_fz ();
+      double xPlane = props.get_xPlane ();
+      double xPlaneN = props.get_xPlaneN ();
+      double xpTick = props.get_xpTick ();
+      double xpTickN = props.get_xpTickN ();
+      double y_min = props.get_y_min ();
+      double y_max = props.get_y_max ();
+      double x_min = props.get_x_min ();
+      double x_max = props.get_x_max ();
+      double zPlane = props.get_zPlane ();
+      double zPlaneN = props.get_zPlaneN ();
+      double zpTick = props.get_zpTick ();
+      double zpTickN = props.get_zpTickN ();
+
+      // Y ticks and grid properties
+      Matrix yticks = m_xform.yscale (props.get_ytick ().matrix_value ());
+      Matrix ymticks = m_xform.yscale (props.get_yminortickvalues ().matrix_value ());
+      bool do_yminortick = props.is_yminortick () && ! yticks.isempty ();
+      string_vector yticklabels = props.get_yticklabel ().string_vector_value ();
+      int wmax = 0;
+      int hmax = 0;
+      bool tick_along_z = nearhoriz || math::isinf (fx);
+      double linewidth = props.get_linewidth ();
+      std::string gridstyle = props.get_gridlinestyle ();
+      std::string minorgridstyle = props.get_minorgridlinestyle ();
+      Matrix gridcolor = props.get_gridcolor_rgb ();
+      Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
+      double gridalpha = props.get_gridalpha ();
+      double minorgridalpha = props.get_minorgridalpha ();
+      bool do_ygrid = (props.is_ygrid () && (gridstyle != "none"));
+      bool do_yminorgrid = (props.is_yminorgrid ()
+                            && (minorgridstyle != "none")
+                            && ! yticks.isempty ());
+      bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D ()
+                       && ! props.xscale_is ("log");
+      bool is_origin_low = is_origin && (x_min + x_max) < 0;
+      bool mirror = props.is_box () && ystate != AXE_ANY_DIR
+                    && (! props.has_property ("__plotyy_axes__"));
+
+      // Y grid
+
+      // possibly use axis color for gridcolor & minorgridcolor
+      if (props.gridcolormode_is ("auto"))
+        if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none"))
+          gridcolor = props.get_ycolor_rgb ();
+
+      if (props.minorgridcolormode_is ("auto"))
+        if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none"))
+          minorgridcolor = props.get_ycolor_rgb ();
+
+      if (gridcolor.isempty ())
+        do_ygrid = false;
+
+      if (minorgridcolor.isempty ())
+        do_yminorgrid = false;
+
+      // set styles when drawing only minor grid
+      if (do_yminorgrid && ! do_ygrid)
+        {
+          gridstyle = minorgridstyle;
+          gridcolor = minorgridcolor;
+          gridalpha = minorgridalpha;
+          do_ygrid = true;
+        }
+
+      // minor grid lines
+      if (do_yminorgrid)
+        render_grid (linewidth,
+                     minorgridstyle, minorgridcolor, minorgridalpha,
+                     ymticks, y_min, y_max,
+                     xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+                     1, (zstate != AXE_DEPTH_DIR));
+
+      // grid lines
+      if (do_ygrid)
+        render_grid (linewidth,
+                     gridstyle, gridcolor, gridalpha,
+                     yticks, y_min, y_max,
+                     xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+                     1, (zstate != AXE_DEPTH_DIR));
+
+      // Skip drawing axis, ticks, and ticklabels when color is "none"
+      if (props.ycolor_is ("none"))
+        return;
+
+      set_color (props.get_ycolor_rgb ());
+
+      // axis line
+      double x_axis_pos = 0.;
+      if (is_origin)
+        {
+          x_axis_pos = math::max (math::min (0., x_max), x_min);
+          m_glfcns.glBegin (GL_LINES);
+          set_color (props.get_ycolor_rgb ());
+          m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick);
+          m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick);
+          m_glfcns.glEnd ();
+        }
+
+      // minor tick marks
+      if (do_yminortick)
+        {
+          if (tick_along_z)
+            render_tickmarks (ymticks, y_min, y_max,
+                              is_origin ? x_axis_pos : xpTick, xpTick,
+                              zpTick, zpTickN, 0., 0.,
+                              (is_origin_low ? -1. : 1.) *
+                              math::signum (zpTick-zpTickN)*fz*yticklen/2,
+                              1, ! is_origin && mirror);
+          else
+            render_tickmarks (ymticks, y_min, y_max,
+                              is_origin ? x_axis_pos : xpTick, xpTickN,
+                              zpTick, zpTick,
+                              (is_origin_low ? -1. : 1.) *
+                              math::signum (xpTick-xpTickN)*fx*yticklen/2,
+                              0., 0., 1, ! is_origin && mirror);
+        }
+
+      // tick marks
+      if (tick_along_z)
+        render_tickmarks (yticks, y_min, y_max,
+                          is_origin ? x_axis_pos : xpTick, xpTick,
+                          zpTick, zpTickN, 0., 0.,
+                          (is_origin_low ? -1. : 1.) *
+                          math::signum (zpTick-zpTickN)*fz*yticklen,
+                          1, ! is_origin && mirror);
+      else
+        render_tickmarks (yticks, y_min, y_max,
+                          is_origin ? x_axis_pos : xpTick, xpTickN,
+                          zpTick, zpTick,
+                          (is_origin_low ? -1. : 1.) *
+                          math::signum (xPlaneN-xPlane)*fx*yticklen,
+                          0., 0., 1, ! is_origin && mirror);
+
+      // tick texts
+      if (yticklabels.numel () > 0)
+        {
+          int halign = (ystate == AXE_HORZ_DIR
+                        ? 1
+                        : (! xyzSym || y2Dright || is_origin_low ? 0 : 2));
+          int valign = (ystate == AXE_VERT_DIR
+                        ? 1
+                        : (is_origin_low ? 0 : 2));
+
+          if (tick_along_z)
+            render_ticktexts (yticks, yticklabels, y_min, y_max,
+                              is_origin ? x_axis_pos : xpTick,
+                              zpTick +
+                              (is_origin_low ? -1. : 1.) *
+                              math::signum (zpTick-zpTickN)*fz*ytickoffset,
+                              1, halign, valign, wmax, hmax);
+          else
+            render_ticktexts (yticks, yticklabels, y_min, y_max,
+                              (is_origin ? x_axis_pos : xpTick) +
+                              (is_origin_low ?  -1. : 1.) *
+                              math::signum (xpTick-xpTickN)*fx*ytickoffset,
+                              zpTick, 1, halign, valign, wmax, hmax);
+        }
+
+      gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on");
+    }
+  else
+    gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off");
+
+#else
+
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_axes_z_grid (const axes::properties& props)
+{
+  gh_manager& gh_mgr = __get_gh_manager__ ();
+
+  int zstate = props.get_zstate ();
+
+  if (zstate != AXE_DEPTH_DIR && props.is_visible ()
+      && (props.is_visible ()
+          || (m_selecting && props.pickableparts_is ("all"))))
+    {
+      bool xySym = props.get_xySym ();
+      bool zSign = props.get_zSign ();
+      double zticklen = props.get_zticklen ();
+      double ztickoffset = props.get_ztickoffset ();
+      double fx = props.get_fx ();
+      double fy = props.get_fy ();
+      double xPlane = props.get_xPlane ();
+      double xPlaneN = props.get_xPlaneN ();
+      double yPlane = props.get_yPlane ();
+      double yPlaneN = props.get_yPlaneN ();
+      double z_min = props.get_z_min ();
+      double z_max = props.get_z_max ();
+
+      // Z ticks and grid properties
+      Matrix zticks = m_xform.zscale (props.get_ztick ().matrix_value ());
+      Matrix zmticks = m_xform.zscale (props.get_zminortickvalues ().matrix_value ());
+      bool do_zminortick = props.is_zminortick () && ! zticks.isempty ();
+      string_vector zticklabels = props.get_zticklabel ().string_vector_value ();
+      int wmax = 0;
+      int hmax = 0;
+      double linewidth = props.get_linewidth ();
+      std::string gridstyle = props.get_gridlinestyle ();
+      std::string minorgridstyle = props.get_minorgridlinestyle ();
+      Matrix gridcolor = props.get_gridcolor_rgb ();
+      Matrix minorgridcolor = props.get_minorgridcolor_rgb ();
+      double gridalpha = props.get_gridalpha ();
+      double minorgridalpha = props.get_minorgridalpha ();
+      bool do_zgrid = (props.is_zgrid () && (gridstyle != "none"));
+      bool do_zminorgrid = (props.is_zminorgrid ()
+                            && (minorgridstyle != "none")
+                            && ! zticks.isempty ());
+      bool mirror = props.is_box () && zstate != AXE_ANY_DIR;
+
+      // Z grid
+
+      // possibly use axis color for gridcolor & minorgridcolor
+      if (props.gridcolormode_is ("auto"))
+        if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none"))
+          gridcolor = props.get_zcolor_rgb ();
+
+      if (props.minorgridcolormode_is ("auto"))
+        if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none"))
+          minorgridcolor = props.get_zcolor_rgb ();
+
+      if (gridcolor.isempty ())
+        do_zgrid = false;
+
+      if (minorgridcolor.isempty ())
+        do_zminorgrid = false;
+
+      // set styles when drawing only minor grid
+      if (do_zminorgrid && ! do_zgrid)
+        {
+          gridstyle = minorgridstyle;
+          gridcolor = minorgridcolor;
+          gridalpha = minorgridalpha;
+          do_zgrid = true;
+        }
+
+      // minor grid lines
+      if (do_zminorgrid)
+        render_grid (linewidth,
+                     minorgridstyle, minorgridcolor, minorgridalpha,
+                     zmticks, z_min, z_max,
+                     xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
+
+      // grid lines
+      if (do_zgrid)
+        render_grid (linewidth,
+                     gridstyle, gridcolor, gridalpha,
+                     zticks, z_min, z_max,
+                     xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
+
+      // Skip drawing axis, ticks, and ticklabels when color is "none"
+      if (props.zcolor_is ("none"))
+        return;
+
+      set_color (props.get_zcolor_rgb ());
+
+      // minor tick marks
+      if (do_zminortick)
+        {
+          if (xySym)
+            {
+              if (math::isinf (fy))
+                render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane,
+                                  yPlane, yPlane,
+                                  math::signum (xPlaneN-xPlane)*fx*zticklen/2,
+                                  0., 0., 2, mirror);
+              else
+                render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN,
+                                  yPlane, yPlane, 0.,
+                                  math::signum (yPlane-yPlaneN)*fy*zticklen/2,
+                                  0., 2, false);
+            }
+          else
+            {
+              if (math::isinf (fx))
+                render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
+                                  yPlaneN, yPlane, 0.,
+                                  math::signum (yPlaneN-yPlane)*fy*zticklen/2,
+                                  0., 2, mirror);
+              else
+                render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane,
+                                  yPlaneN, yPlaneN,
+                                  math::signum (xPlane-xPlaneN)*fx*zticklen/2,
+                                  0., 0., 2, false);
+            }
+        }
+
+      // tick marks
+      if (xySym)
+        {
+          if (math::isinf (fy))
+            render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
+                              yPlane, yPlane,
+                              math::signum (xPlaneN-xPlane)*fx*zticklen,
+                              0., 0., 2, mirror);
+          else
+            render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN,
+                              yPlane, yPlane, 0.,
+                              math::signum (yPlane-yPlaneN)*fy*zticklen,
+                              0., 2, false);
+        }
+      else
+        {
+          if (math::isinf (fx))
+            render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane,
+                              yPlaneN, yPlane, 0.,
+                              math::signum (yPlaneN-yPlane)*fy*zticklen,
+                              0., 2, mirror);
+          else
+            render_tickmarks (zticks, z_min, z_max, xPlane, xPlane,
+                              yPlaneN, yPlane,
+                              math::signum (xPlane-xPlaneN)*fx*zticklen,
+                              0., 0., 2, false);
+        }
+
+      // tick texts
+      if (zticklabels.numel () > 0)
+        {
+          int halign = 2;
+          int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2));
+
+          if (xySym)
+            {
+              if (math::isinf (fy))
+                render_ticktexts (zticks, zticklabels, z_min, z_max,
+                                  xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset,
+                                  yPlane, 2, halign, valign, wmax, hmax);
+              else
+                render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN,
+                                  yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset,
+                                  2, halign, valign, wmax, hmax);
+            }
+          else
+            {
+              if (math::isinf (fx))
+                render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane,
+                                  yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset,
+                                  2, halign, valign, wmax, hmax);
+              else
+                render_ticktexts (zticks, zticklabels, z_min, z_max,
+                                  xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset,
+                                  yPlaneN, 2, halign, valign, wmax, hmax);
+            }
+        }
+
+      gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on");
+    }
+  else
+    gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off");
+}
+
+void
+opengl_renderer::draw_axes_grids (const axes::properties& props)
+{
+#if defined (HAVE_OPENGL)
+  // Disable line smoothing for axes
+  GLboolean antialias;
+
+  m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias);
+
+  if (antialias == GL_TRUE)
+    m_glfcns.glDisable (GL_LINE_SMOOTH);
+
+  set_linecap ("butt");
+  set_linewidth (props.get_linewidth ());
+  set_font (props);
+  set_interpreter (props.get_ticklabelinterpreter ());
+
+  draw_axes_x_grid (props);
+  draw_axes_y_grid (props);
+  draw_axes_z_grid (props);
+
+  if (antialias == GL_TRUE)
+    m_glfcns.glEnable (GL_LINE_SMOOTH);
+#else
+
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_all_lights (const base_properties& props,
+                                  std::list<graphics_object>& obj_list)
+{
+#if defined (HAVE_OPENGL)
+  gh_manager& gh_mgr = __get_gh_manager__ ();
+
+  Matrix children = props.get_all_children ();
+
+  for (octave_idx_type i = children.numel () - 1; i >= 0; i--)
+    {
+      graphics_object go = gh_mgr.get_object (children(i));
+
+      base_properties p = go.get_properties ();
+
+      if (p.is_visible ()
+          || (m_selecting && p.pickableparts_is ("all")))
+        {
+          if (go.isa ("light") && ! m_selecting)
+            {
+              if (m_current_light-GL_LIGHT0 < m_max_lights)
+                {
+                  set_clipping (p.is_clipping ());
+                  draw (go);
+                  m_current_light++;
+                }
+            }
+          else if (go.isa ("hggroup")
+                   && ! (m_selecting && p.pickableparts_is ("none")))
+            draw_all_lights (go.get_properties (), obj_list);
+          else if (! (m_selecting && p.pickableparts_is ("none")))
+            obj_list.push_back (go);
+        }
+    }
+#else
+
+  octave_unused_parameter (props);
+  octave_unused_parameter (obj_list);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_axes_children (const axes::properties& props)
+{
+#if defined (HAVE_OPENGL)
+  // list for non-light child objects
+  std::list<graphics_object> obj_list;
+  std::list<graphics_object>::iterator it;
+
+  // 1st pass: draw light objects
+
+  // FIXME: max_lights only needs to be set once.
+  // It would be better if this could be in the constructor for gl_renderer
+  // but this seems to lead to calls of OpenGL functions before the context
+  // is actually initialized.  See bug #48669.
+  // Check actual maximum number of lights possible
+  init_maxlights ();
+
+  // Start with the last element of the array of child objects to
+  // display them in the order they were added to the array.
+
+  if (props.get_num_lights () > m_max_lights)
+    warning_with_id ("Octave:max-lights-exceeded",
+                     "light: Maximum number of lights (%d) in these axes is "
+                     "exceeded.", m_max_lights);
+
+  m_current_light = GL_LIGHT0;
+  draw_all_lights (props, obj_list);
+
+  // disable other OpenGL lights
+  for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++)
+    m_glfcns.glDisable (GL_LIGHT0 + i);
+
+  // save camera position and set ambient light color before drawing
+  // other objects
+  m_view_vector = props.get_cameraposition ().matrix_value ();
+
+  float cb[4] = { 1.0, 1.0, 1.0, 1.0 };
+  ColumnVector ambient_color = props.get_ambientlightcolor_rgb ();
+  for (int i = 0; i < 3; i++)
+    cb[i] = ambient_color(i);
+  m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb);
+
+  // 2nd pass: draw other objects (with units set to "data")
+
+  it = obj_list.begin ();
+  while (it != obj_list.end ())
+    {
+      graphics_object go = (*it);
+
+      // FIXME: check whether object has "units" property and it is set
+      // to "data"
+      if (! go.isa ("text") || go.get ("units").string_value () == "data")
+        {
+          set_clipping (go.get_properties ().is_clipping ());
+          draw (go);
+
+          it = obj_list.erase (it);
+        }
+      else
+        it++;
+    }
+
+  // 3rd pass: draw remaining objects
+
+  m_glfcns.glDisable (GL_DEPTH_TEST);
+
+  for (it = obj_list.begin (); it != obj_list.end (); it++)
+    {
+      graphics_object go = (*it);
+
+      set_clipping (go.get_properties ().is_clipping ());
+      draw (go);
+    }
+
+  set_clipping (false);
+
+  // FIXME: finalize rendering (transparency processing)
+  // FIXME: draw zoom box, if needed
+
+#else
+
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_axes (const axes::properties& props)
+{
+#if defined (HAVE_OPENGL)
+
+  // Legends are not drawn when "visible" is "off".
+  if (! props.is_visible () && props.get_tag () == "legend")
+    return;
+
+  // Don't draw the axes and its children if we are in selection and
+  // pickable parts is "none".
+  if (m_selecting && props.pickableparts_is ("none"))
+    return;
+
+  static double floatmax = std::numeric_limits<float>::max ();
+
+  double x_min = props.get_x_min ();
+  double x_max = props.get_x_max ();
+  double y_min = props.get_y_min ();
+  double y_max = props.get_y_max ();
+  double z_min = props.get_z_min ();
+  double z_max = props.get_z_max ();
+
+  if (x_max > floatmax || y_max > floatmax || z_max > floatmax
+      || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax)
+    {
+      warning ("opengl_renderer: data values greater than float capacity.  (1) Scale data, or (2) Use gnuplot");
+      return;
+    }
+
+  setup_opengl_transformation (props);
+
+  // For 2D axes with only 2D primitives, draw from back to front without
+  // depth sorting
+  bool is2D = props.get_is2D (true);
+  if (is2D)
+    m_glfcns.glDisable (GL_DEPTH_TEST);
+  else
+    m_glfcns.glEnable (GL_DEPTH_TEST);
+
+  draw_axes_planes (props);
+
+  if (! is2D || props.layer_is ("bottom"))
+    {
+      draw_axes_grids (props);
+      if (props.get_tag () != "legend" || props.get_box () != "off")
+        draw_axes_boxes (props);
+    }
+
+  set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max);
+
+  draw_axes_children (props);
+
+  if (is2D && props.layer_is ("top"))
+    {
+      draw_axes_grids (props);
+      if (props.get_tag () != "legend" || props.get_box () != "off")
+        draw_axes_boxes (props);
+    }
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::draw_line (const line::properties& props)
+{
+#if defined (HAVE_OPENGL)
+
+  bool draw_all = m_selecting && props.pickableparts_is ("all");
+
+  Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ());
+  Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ());
+  Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ());
+
+  bool has_z = (z.numel () > 0);
+  int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()),
+                                      (has_z ? z.numel ()
+                                       : std::numeric_limits<int>::max ())));
+  uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
+  uint8_t clip_ok = 0x40;
+
+  std::vector<uint8_t> clip (n);
+
+  if (has_z)
+    for (int i = 0; i < n; i++)
+      clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask);
+  else
+    {
+      double z_mid = (m_zmin+m_zmax)/2;
+
+      for (int i = 0; i < n; i++)
+        clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask);
+    }
+
+  if (! props.linestyle_is ("none") && ! props.color_is ("none"))
+    {
+      set_color (props.get_color_rgb ());
+      set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
+      set_linewidth (props.get_linewidth ());
+      set_linecap ("butt");
+      set_linejoin (props.get_linejoin ());
+
+      if (has_z)
+        {
+          bool flag = false;
+
+          for (int i = 1; i < n; i++)
+            {
+              if ((clip[i-1] & clip[i]) == clip_ok)
+                {
+                  if (! flag)
+                    {
+                      flag = true;
+                      m_glfcns.glBegin (GL_LINE_STRIP);
+                      m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1));
+                    }
+                  m_glfcns.glVertex3d (x(i), y(i), z(i));
+                }
+              else if (flag)
+                {
+                  flag = false;
+                  m_glfcns.glEnd ();
+                }
+            }
+
+          if (flag)
+            m_glfcns.glEnd ();
+        }
+      else
+        {
+          bool flag = false;
+
+          for (int i = 1; i < n; i++)
+            {
+              if ((clip[i-1] & clip[i]) == clip_ok)
+                {
+                  if (! flag)
+                    {
+                      flag = true;
+                      m_glfcns.glBegin (GL_LINE_STRIP);
+                      m_glfcns.glVertex2d (x(i-1), y(i-1));
+                    }
+                  m_glfcns.glVertex2d (x(i), y(i));
+                }
+              else if (flag)
+                {
+                  flag = false;
+                  m_glfcns.glEnd ();
+                }
+            }
+
+          if (flag)
+            m_glfcns.glEnd ();
+        }
+
+      set_linewidth (0.5f);
+      set_linestyle ("-");
+    }
+
+  set_clipping (false);
+
+  if (! props.marker_is ("none")
+      && ! (props.markeredgecolor_is ("none")
+            && props.markerfacecolor_is ("none")))
+    {
+      Matrix lc, fc;
+
+      if (draw_all)
+        lc = Matrix (1, 3, 0.0);
+      else if (props.markeredgecolor_is ("auto"))
+        lc = props.get_color_rgb ();
+      else if (! props.markeredgecolor_is ("none"))
+        lc = props.get_markeredgecolor_rgb ();
+
+      if (draw_all)
+        fc = Matrix (1, 3, 0.0);
+      if (props.markerfacecolor_is ("auto"))
+        fc = props.get_color_rgb ();
+      else if (! props.markerfacecolor_is ("none"))
+        fc = props.get_markerfacecolor_rgb ();
+
+      init_marker (props.get_marker (), props.get_markersize (),
+                   props.get_linewidth ());
+
+      for (int i = 0; i < n; i++)
+        {
+          if (clip[i] == clip_ok)
+            draw_marker (x(i), y(i),
+                         has_z ? z(i) : 0.0,
+                         lc, fc);
+        }
+
+      end_marker ();
+    }
+
+  set_clipping (props.is_clipping ());
+
+#else
+
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  // FIXME: global optimization (rendering, data structures...),
-  // there is probably a smarter/faster/less-memory-consuming way to do this.
-  void
-  opengl_renderer::draw_patch (const patch::properties& props)
-  {
+}
+
+void
+opengl_renderer::draw_surface (const surface::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    // Do not render if the patch has incoherent data
-    std::string msg;
-    if (props.has_bad_data (msg))
-      {
-        warning ("opengl_renderer: %s.  Not rendering.", msg.c_str ());
-        return;
-      }
-
-    bool draw_all = m_selecting && props.pickableparts_is ("all");
-    const Matrix f = props.get_faces ().matrix_value ();
-    const Matrix v = m_xform.scale (props.get_vertices ().matrix_value ());
-    Matrix c;
-    Matrix a;
-    double fa = 1.0;
-
-    int nv = v.rows ();
-    int nf = f.rows ();
-    int fcmax = f.columns ();
-
-    bool has_z = (v.columns () > 2);
-    bool has_facecolor = false;
-    bool has_facealpha = false;
-
-    int fc_mode = ((props.facecolor_is ("none")
-                    || props.facecolor_is_rgb () || draw_all) ? 0 :
-                   (props.facecolor_is ("flat") ? 1 : 2));
-    int fl_mode = (props.facelighting_is ("none") ? 0 :
-                   (props.facelighting_is ("flat") ? 1 : 2));
-    int fa_mode = (props.facealpha_is_double () ? 0 :
-                   (props.facealpha_is ("flat") ? 1 : 2));
-    int ec_mode = ((props.edgecolor_is ("none")
-                    || props.edgecolor_is_rgb ()) ? 0 :
-                   (props.edgecolor_is ("flat") ? 1 : 2));
-    int el_mode = (props.edgelighting_is ("none") ? 0 :
-                   (props.edgelighting_is ("flat") ? 1 : 2));
-    int ea_mode = (props.edgealpha_is_double () ? 0 :
-                   (props.edgealpha_is ("flat") ? 1 : 2));
-    int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
-                    (props.backfacelighting_is ("reverselit") ? 1 : 2));
-    bool do_lighting = props.get_do_lighting ();
-
-    Matrix fcolor = props.get_facecolor_rgb ();
-    Matrix ecolor = props.get_edgecolor_rgb ();
-
-    float as = props.get_ambientstrength ();
-    float ds = props.get_diffusestrength ();
-    float ss = props.get_specularstrength ();
-    float se = props.get_specularexponent () * 5; // to fit Matlab
-    float scr = props.get_specularcolorreflectance ();
-
-    const Matrix vn = props.get_vertexnormals ().matrix_value ();
-    bool has_vertex_normals = (vn.rows () == nv);
-    const Matrix fn = props.get_facenormals ().matrix_value ();
-    bool has_face_normals = (fn.rows () == nf);
-
-    boolMatrix clip (1, nv, false);
-
-    if (has_z)
-      for (int i = 0; i < nv; i++)
-        clip(i) = is_nan_or_inf (v(i,0), v(i,1), v(i,2));
-    else
-      for (int i = 0; i < nv; i++)
-        clip(i) = is_nan_or_inf (v(i,0), v(i,1), 0);
-
-    boolMatrix clip_f (1, nf, false);
-    Array<int> count_f (dim_vector (nf, 1), 0);
-
-    for (int i = 0; i < nf; i++)
-      {
-        bool fclip = false;
-        int count = 0;
-
-        for (int j = 0; j < fcmax && ! math::isnan (f(i,j)); j++, count++)
-          fclip = (fclip || clip(int (f(i,j) - 1)));
-
-        clip_f(i) = fclip;
-        count_f(i) = count;
-      }
-
-    if (draw_all || fc_mode > 0 || ec_mode > 0)
-      {
-        if (draw_all)
-          c = Matrix (1, 3, 0.0);
-        else
-          c = props.get_color_data ().matrix_value ();
-
-        if (c.rows () == 1)
-          {
-            // Single color specifications, we can simplify a little bit
-
-            if (draw_all || fc_mode > 0)
-              {
-                fcolor = c;
-                fc_mode = UNIFORM;
-              }
-
-            if (draw_all || ec_mode > 0)
-              {
-                ecolor = c;
-                ec_mode = UNIFORM;
-              }
-
-            c = Matrix ();
-          }
-        else
-          has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ()));
-      }
-
-    if (fa_mode > 0 || ea_mode > 0)
-      {
-        // FIXME: retrieve alpha data from patch object
-        //a = props.get_alpha_data ();
-        has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ()));
-      }
-
-    if (fa_mode == 0)
-      fa = props.get_facealpha_double ();
-
-    octave_idx_type fr = f.rows ();
-    std::vector<vertex_data> vdata (f.numel ());
-
-    for (int i = 0; i < nf; i++)
-      for (int j = 0; j < count_f(i); j++)
+  bool draw_all = m_selecting && props.pickableparts_is ("all");
+
+  const Matrix x = m_xform.xscale (props.get_xdata ().matrix_value ());
+  const Matrix y = m_xform.yscale (props.get_ydata ().matrix_value ());
+  const Matrix z = m_xform.zscale (props.get_zdata ().matrix_value ());
+
+  int zr = z.rows ();
+  int zc = z.columns ();
+
+  NDArray c;
+  const NDArray vn = props.get_vertexnormals ().array_value ();
+  dim_vector vn_dims = vn.dims ();
+  bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc
+                             && vn_dims(2) == 3);
+  const NDArray fn = props.get_facenormals ().array_value ();
+  dim_vector fn_dims = fn.dims ();
+  bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1
+                           && fn_dims(2) == 3);
+
+  // FIXME: handle transparency
+  Matrix a;
+
+  int fc_mode = (props.facecolor_is_rgb () ? 0 :
+                 (props.facecolor_is ("flat") ? 1 :
+                  (props.facecolor_is ("interp") ? 2 :
+                   (props.facecolor_is ("texturemap") ? 3 : -1))));
+  int fl_mode = (props.facelighting_is ("none") ? 0 :
+                 (props.facelighting_is ("flat") ?
+                  (has_face_normals ? 1 : 0) :
+                  (has_vertex_normals ? 2 : 0)));
+  int fa_mode = (props.facealpha_is_double () ? 0 :
+                 (props.facealpha_is ("flat") ? 1 : 2));
+  int ec_mode = (props.edgecolor_is_rgb () ? 0 :
+                 (props.edgecolor_is ("flat") ? 1 :
+                  (props.edgecolor_is ("interp") ? 2 : -1)));
+  int el_mode = (props.edgelighting_is ("none") ? 0 :
+                 (props.edgelighting_is ("flat") ?
+                  (has_face_normals ? 1 : 0) :
+                  (has_vertex_normals ? 2 : 0)));
+  int ea_mode = (props.edgealpha_is_double () ? 0 :
+                 (props.edgealpha_is ("flat") ? 1 : 2));
+  int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
+                  (props.backfacelighting_is ("reverselit") ? 1 : 2));
+  bool do_lighting = props.get_do_lighting ();
+
+  Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0)
+                   : props.get_facecolor_rgb ());
+  Matrix ecolor = props.get_edgecolor_rgb ();
+  double fa = 1.0;
+
+  float as = props.get_ambientstrength ();
+  float ds = props.get_diffusestrength ();
+  float ss = props.get_specularstrength ();
+  float se = props.get_specularexponent () * 5; // to fit Matlab
+  float scr = props.get_specularcolorreflectance ();
+  float cb[4] = { 0.0, 0.0, 0.0, 1.0 };
+
+  opengl_texture tex (m_glfcns);
+
+  int i1, i2, j1, j2;
+  bool x_mat = (x.rows () == z.rows ());
+  bool y_mat = (y.columns () == z.columns ());
+
+  i1 = i2 = j1 = j2 = 0;
+
+  if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0)
+    c = props.get_color_data ().array_value ();
+
+  boolMatrix clip (z.dims (), false);
+
+  for (int i = 0; i < zr; i++)
+    {
+      if (x_mat)
+        i1 = i;
+
+      for (int j = 0; j < zc; j++)
+        {
+          if (y_mat)
+            j1 = j;
+
+          clip(i, j) = is_nan_or_inf (x(i1, j), y(i, j1), z(i, j));
+        }
+    }
+
+  if (fa_mode > 0 || ea_mode > 0)
+    {
+      // FIXME: implement alphadata conversion
+      //a = props.get_alpha_data ();
+    }
+
+  if (fl_mode > 0 || el_mode > 0)
+    m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
+
+  // FIXME: good candidate for caching,
+  //        transferring pixel data to OpenGL is time consuming.
+  if (fc_mode == TEXTURE)
+    tex = opengl_texture::create (m_glfcns, props.get_color_data ());
+
+  if (draw_all || ! props.facecolor_is ("none"))
+    {
+      if (fa_mode == 0)
         {
-          int idx = int (f(i,j) - 1);
-
-          Matrix vv (1, 3, 0.0);
-          Matrix cc;
-          Matrix vnn (1, 3, 0.0);
-          Matrix fnn (1, 3, 0.0);
-          double aa = 1.0;
-
-          vv(0) = v(idx,0); vv(1) = v(idx,1);
-          if (has_z)
-            vv(2) = v(idx,2);
-          if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals)
+          fa = props.get_facealpha_double ();
+          cb[3] = fa;
+          if (fc_mode == UNIFORM || fc_mode == TEXTURE)
             {
-              double dir = 1.0;
-              if (bfl_mode > 0)
-                dir = ((fn(i,0) * m_view_vector(0)
-                        + fn(i,1) * m_view_vector(1)
-                        + fn(i,2) * m_view_vector(2) < 0)
-                       ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
-              fnn(0) = dir * fn(i,0);
-              fnn(1) = dir * fn(i,1);
-              fnn(2) = dir * fn(i,2);
+              m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
+              if (fl_mode > 0)
+                {
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = as * fcolor(i);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = ds * fcolor(i);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = ss * (scr + (1-scr) * fcolor(i));
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                }
             }
-          if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals)
+
+          if ((fl_mode > 0) && do_lighting)
+            m_glfcns.glEnable (GL_LIGHTING);
+          m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD)
+                                 ? GL_SMOOTH : GL_FLAT);
+          set_polygon_offset (true, 1.0);
+          if (fc_mode == TEXTURE)
+            m_glfcns.glEnable (GL_TEXTURE_2D);
+
+          for (int i = 1; i < zc; i++)
             {
-              double dir = 1.0;
-              if (bfl_mode > 0)
-                dir = ((vn(idx,0) * m_view_vector(0)
-                        + vn(idx,1) * m_view_vector(1)
-                        + vn(idx,2) * m_view_vector(2) < 0)
-                       ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
-              vnn(0) = dir * vn(idx,0);
-              vnn(1) = dir * vn(idx,1);
-              vnn(2) = dir * vn(idx,2);
+              if (y_mat)
+                {
+                  i1 = i-1;
+                  i2 = i;
+                }
+
+              for (int j = 1; j < zr; j++)
+                {
+
+                  if (clip(j-1, i-1) || clip(j, i-1)
+                      || clip(j-1, i) || clip(j, i))
+                    continue;
+
+                  if (fc_mode == FLAT)
+                    {
+                      // "flat" only needs color at lower-left vertex
+                      if (! math::isfinite (c(j-1, i-1)))
+                        continue;
+                    }
+                  else if (fc_mode == INTERP)
+                    {
+                      // "interp" needs valid color at all 4 vertices
+                      if (! (math::isfinite (c(j-1, i-1))
+                             && math::isfinite (c(j, i-1))
+                             && math::isfinite (c(j-1, i))
+                             && math::isfinite (c(j, i))))
+                        continue;
+                    }
+
+                  if (x_mat)
+                    {
+                      j1 = j-1;
+                      j2 = j;
+                    }
+
+                  m_glfcns.glBegin (GL_QUADS);
+
+                  // Vertex 1
+                  if (fc_mode == TEXTURE)
+                    tex.tex_coord (double (i-1) / (zc-1),
+                                   double (j-1) / (zr-1));
+                  else if (fc_mode > 0)
+                    {
+                      // FIXME: is there a smarter way to do this?
+                      for (int k = 0; k < 3; k++)
+                        cb[k] = c(j-1, i-1, k);
+                      m_glfcns.glColor4fv (cb);
+
+                      if (fl_mode > 0)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] *= as;
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ds * c(j-1, i-1, k);
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k));
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                        }
+                    }
+                  if (fl_mode > 0)
+                    set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn),
+                                j-1, i-1);
+
+                  m_glfcns.glVertex3d (x(j1, i-1), y(j-1, i1), z(j-1, i-1));
+
+                  // Vertex 2
+                  if (fc_mode == TEXTURE)
+                    tex.tex_coord (double (i) / (zc-1),
+                                   double (j-1) / (zr-1));
+                  else if (fc_mode == INTERP)
+                    {
+                      for (int k = 0; k < 3; k++)
+                        cb[k] = c(j-1, i, k);
+                      m_glfcns.glColor4fv (cb);
+
+                      if (fl_mode > 0)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] *= as;
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ds * c(j-1, i, k);
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                        }
+                    }
+
+                  if (fl_mode == GOURAUD)
+                    set_normal (bfl_mode, vn, j-1, i);
+
+                  m_glfcns.glVertex3d (x(j1, i), y(j-1, i2), z(j-1, i));
+
+                  // Vertex 3
+                  if (fc_mode == TEXTURE)
+                    tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1));
+                  else if (fc_mode == INTERP)
+                    {
+                      for (int k = 0; k < 3; k++)
+                        cb[k] = c(j, i, k);
+                      m_glfcns.glColor4fv (cb);
+
+                      if (fl_mode > 0)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] *= as;
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ds * c(j, i, k);
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ss * (scr + (1-scr) * c(j, i, k));
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                        }
+                    }
+                  if (fl_mode == GOURAUD)
+                    set_normal (bfl_mode, vn, j, i);
+
+                  m_glfcns.glVertex3d (x(j2, i), y(j, i2), z(j, i));
+
+                  // Vertex 4
+                  if (fc_mode == TEXTURE)
+                    tex.tex_coord (double (i-1) / (zc-1),
+                                   double (j) / (zr-1));
+                  else if (fc_mode == INTERP)
+                    {
+                      for (int k = 0; k < 3; k++)
+                        cb[k] = c(j, i-1, k);
+                      m_glfcns.glColor4fv (cb);
+
+                      if (fl_mode > 0)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] *= as;
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ds * c(j, i-1, k);
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
+                          m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                        }
+                    }
+                  if (fl_mode == GOURAUD)
+                    set_normal (bfl_mode, vn, j, i-1);
+
+                  m_glfcns.glVertex3d (x(j2, i-1), y(j, i1), z(j, i-1));
+
+                  m_glfcns.glEnd ();
+                }
             }
-          if (c.numel () > 0)
-            {
-              cc.resize (1, 3);
-              if (has_facecolor)
-                cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2);
-              else
-                cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2);
-            }
-          if (fa_mode == 0)
-            aa = fa;
-          else if (a.numel () > 0)
-            {
-              if (has_facealpha)
-                aa = a(i);
-              else
-                aa = a(idx);
-            }
-
-          vdata[i+j*fr]
-            = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr);
+
+          set_polygon_offset (false);
+          if (fc_mode == TEXTURE)
+            m_glfcns.glDisable (GL_TEXTURE_2D);
+
+          if ((fl_mode > 0) && do_lighting)
+            m_glfcns.glDisable (GL_LIGHTING);
+        }
+      else
+        {
+          // FIXME: implement flat, interp and texturemap transparency
         }
-
-    if (fl_mode > 0 || el_mode > 0)
-      m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
-
-    if (draw_all || ! props.facecolor_is ("none"))
-      {
-        // FIXME: adapt to double-radio property
-        if (fa_mode == 0)
-          {
-            if (fc_mode == UNIFORM)
-              {
-                m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
-                if (fl_mode > 0)
-                  {
-                    float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };;
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = as * fcolor(i);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = ds * fcolor(i);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = ss * (scr + (1-scr) * fcolor(i));
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                  }
-              }
-
-            if ((fl_mode > 0) && do_lighting)
-              m_glfcns.glEnable (GL_LIGHTING);
-
-            // NOTE: Push filled part of patch backwards to avoid Z-fighting
-            // with tessellator outline.  A value of 1.0 seems to work fine.
-            // Value can't be too large or the patch will be pushed below the
-            // axes planes at +2.5.
-            patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0);
-
-            std::vector<octave_idx_type>::const_iterator it;
-            octave_idx_type i_start, i_end;
-
-            for (int i = 0; i < nf; i++)
-              {
-                if (clip_f(i))
-                  continue;
-
-                bool is_non_planar = false;
-                if (props.m_coplanar_last_idx.size () > 0
-                    && props.m_coplanar_last_idx[i].size () > 1)
-                  {
-                    is_non_planar = true;
-                    it = props.m_coplanar_last_idx[i].end ();
-                    it--;
-                  }
-
-                // loop over planar subsets of face
-                do
-                  {
-                    if (is_non_planar)
-                      {
-                        i_end = *it;
-                        if (it == props.m_coplanar_last_idx[i].begin ())
-                          i_start = 0;
-                        else
-                          {
-                            it--;
-                            i_start = *it - 1;
-                          }
-                      }
-                    else
-                      {
-                        i_end = count_f(i) - 1;
-                        i_start = 0;
-                      }
-
-                    tess.begin_polygon (true);
-                    tess.begin_contour ();
-
-                    // Add vertices in reverse order for Matlab compatibility
-                    for (int j = i_end; j > i_start; j--)
-                      {
-                        vertex_data::vertex_data_rep *vv
-                          = vdata[i+j*fr].get_rep ();
-
-                        tess.add_vertex (vv->m_coords.fortran_vec (), vv);
-                      }
-
-                    if (count_f(i) > 0)
-                      {
-                        vertex_data::vertex_data_rep *vv = vdata[i].get_rep ();
-
-                        if (fc_mode == FLAT)
-                          {
-                            // For "flat" shading, use color of 1st vertex.
-                            Matrix col = vv->m_color;
-
-                            if (col.numel () == 3)
-                              {
-                                m_glfcns.glColor4d (col(0), col(1), col(2), fa);
-                                if (fl_mode > 0)
-                                  {
-                                    float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
-
-                                    for (int k = 0; k < 3; k++)
-                                      cb[k] = (vv->m_ambient * col(k));
-                                    m_glfcns.glMaterialfv (LIGHT_MODE,
-                                                           GL_AMBIENT, cb);
-
-                                    for (int k = 0; k < 3; k++)
-                                      cb[k] = (vv->m_diffuse * col(k));
-                                    m_glfcns.glMaterialfv (LIGHT_MODE,
-                                                           GL_DIFFUSE, cb);
-
-                                    for (int k = 0; k < 3; k++)
-                                      cb[k] = vv->m_specular *
-                                              (vv->m_specular_color_refl
-                                               + (1-vv->m_specular_color_refl) *
-                                              col(k));
-                                    m_glfcns.glMaterialfv (LIGHT_MODE,
-                                                           GL_SPECULAR, cb);
-                                  }
-                              }
-                          }
-
-                        tess.add_vertex (vv->m_coords.fortran_vec (), vv);
-                      }
-
-                    tess.end_contour ();
-                    tess.end_polygon ();
-                  } while (i_start > 0);
-              }
-
-            if ((fl_mode > 0) && do_lighting)
-              m_glfcns.glDisable (GL_LIGHTING);
-          }
-        else
-          {
-            // FIXME: implement flat and interp transparency
-          }
-      }
-
-    if (draw_all
-        || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")))
-      {
-        // FIXME: adapt to double-radio property
-        if (props.get_edgealpha_double () == 1)
-          {
-            if (ec_mode == UNIFORM)
-              {
-                m_glfcns.glColor3dv (ecolor.data ());
-                if (el_mode > 0)
-                  {
-                    // edge lighting only uses ambient light
-                    float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-
-                    for (int i = 0; i < 3; i++)
-                      cb[i] = (as * ecolor(i));
-                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-                  }
-              }
-
-            if ((el_mode > 0) && do_lighting)
-              m_glfcns.glEnable (GL_LIGHTING);
-
-            double linewidth = props.get_linewidth ();
-            set_linestyle (props.get_linestyle (), false, linewidth);
-            set_linewidth (linewidth);
-            set_linecap ("butt");
-            set_linejoin ("miter");
-
-            // NOTE: patch contour cannot be offset.  Offset must occur with
-            // the filled portion of the patch above.  The tessellator uses
-            // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL
-            // automatically sets the glType to GL_LINE_LOOP.  This primitive is
-            // not supported by glPolygonOffset which is used to do Z offsets.
-            patch_tessellator tess (this, ec_mode, el_mode, false);
-
-            for (int i = 0; i < nf; i++)
-              {
-                bool is_non_planar = false;
-                if (props.m_coplanar_last_idx.size () > 0
-                    && props.m_coplanar_last_idx[i].size () > 1)
-                  is_non_planar = true;
-                if (clip_f(i) || is_non_planar)
-                  {
-                    // This is an unclosed contour or a non-planar face.
-                    // Draw it as a line.
-                    bool flag = false;
-
-                    m_glfcns.glShadeModel ((ec_mode == INTERP
-                                            || el_mode == GOURAUD)
-                                           ? GL_SMOOTH : GL_FLAT);
-
-                    // Add vertices in reverse order for Matlab compatibility
-                    for (int j = count_f(i)-1; j >= 0; j--)
-                      {
-                        if (! clip(int (f(i,j) - 1)))
-                          {
-                            vertex_data::vertex_data_rep *vv
-                              = vdata[i+j*fr].get_rep ();
-                            const Matrix m = vv->m_coords;
-                            if (! flag)
-                              {
-                                flag = true;
-                                m_glfcns.glBegin (GL_LINE_STRIP);
-                              }
-                            if (ec_mode != UNIFORM)
-                              {
-                                Matrix col = vv->m_color;
-
-                                if (col.numel () == 3)
-                                  m_glfcns.glColor3dv (col.data ());
-                              }
-                            m_glfcns.glVertex3d (m(0), m(1), m(2));
-                          }
-                        else if (flag)
-                          {
-                            flag = false;
-                            m_glfcns.glEnd ();
-                          }
-                      }
-                    // Do loop body with vertex N to "close" GL_LINE_STRIP
-                    // from vertex 0 to vertex N.
-                    int j = count_f(i)-1;
-                    if (flag && ! clip(int (f(i,j) - 1)))
-                      {
-                        vertex_data::vertex_data_rep *vv
-                          = vdata[i+j*fr].get_rep ();
-                        const Matrix m = vv->m_coords;
-                        if (ec_mode != UNIFORM)
-                          {
-                            Matrix col = vv->m_color;
-
-                            if (col.numel () == 3)
-                              m_glfcns.glColor3dv (col.data ());
-                          }
-                        m_glfcns.glVertex3d (m(0), m(1), m(2));
-                      }
-
-                    if (flag)
+    }
+
+  if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))
+    {
+      if (props.get_edgealpha_double () == 1)
+        {
+          cb[3] = 1.0; // edgealpha isn't implemented yet
+          if (ec_mode == UNIFORM)
+            {
+              m_glfcns.glColor3dv (ecolor.data ());
+              if (el_mode > 0)
+                {
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = as * ecolor(i);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = ds * ecolor(i);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = ss * (scr + (1-scr) * ecolor(i));
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                }
+            }
+
+          if ((el_mode > 0) && do_lighting)
+            m_glfcns.glEnable (GL_LIGHTING);
+          m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
+                                 ? GL_SMOOTH : GL_FLAT);
+
+          set_linestyle (props.get_linestyle (), false,
+                         props.get_linewidth ());
+          set_linewidth (props.get_linewidth ());
+          set_linecap ("butt");
+          set_linejoin ("miter");
+
+          // Mesh along Y-axis
+
+          if (props.meshstyle_is ("both") || props.meshstyle_is ("column"))
+            {
+              for (int i = 0; i < zc; i++)
+                {
+                  if (y_mat)
+                    {
+                      i1 = i-1;
+                      i2 = i;
+                    }
+
+                  for (int j = 1; j < zr; j++)
+                    {
+                      if (clip(j-1, i) || clip(j, i))
+                        continue;
+
+                      if (ec_mode == FLAT)
+                        {
+                          // "flat" only needs color at lower-left vertex
+                          if (! math::isfinite (c(j-1, i)))
+                            continue;
+                        }
+                      else if (ec_mode == INTERP)
+                        {
+                          // "interp" needs valid color at both vertices
+                          if (! (math::isfinite (c(j-1, i))
+                                 && math::isfinite (c(j, i))))
+                            continue;
+                        }
+
+                      if (x_mat)
+                        {
+                          j1 = j-1;
+                          j2 = j;
+                        }
+
+                      m_glfcns.glBegin (GL_LINES);
+
+                      // Vertex 1
+                      if (ec_mode > 0)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = c(j-1, i, k);
+                          m_glfcns.glColor3fv (cb);
+
+                          if (el_mode > 0)
+                            {
+                              for (int k = 0; k < 3; k++)
+                                cb[k] *= as;
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ds * c(j-1, i, k);
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
+                                                     cb);
+                            }
+                        }
+                      if (el_mode > 0)
+                        {
+                          if (el_mode == GOURAUD)
+                            set_normal (bfl_mode, vn, j-1, i);
+                          else
+                            set_normal (bfl_mode, fn, j-1, std::min (i, zc-2));
+                        }
+
+                      m_glfcns.glVertex3d (x(j1, i), y(j-1, i2), z(j-1, i));
+
+                      // Vertex 2
+                      if (ec_mode == INTERP)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = c(j, i, k);
+                          m_glfcns.glColor3fv (cb);
+
+                          if (el_mode > 0)
+                            {
+                              for (int k = 0; k < 3; k++)
+                                cb[k] *= as;
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ds * c(j, i, k);
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ss * (scr + (1-scr) * c(j, i, k));
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
+                                                     cb);
+                            }
+                        }
+                      if (el_mode == GOURAUD)
+                        set_normal (bfl_mode, vn, j, i);
+
+                      m_glfcns.glVertex3d (x(j2, i), y(j, i2), z(j, i));
+
                       m_glfcns.glEnd ();
-                  }
-                else  // Normal edge contour drawn with tessellator
-                  {
-                    tess.begin_polygon (false);
-                    tess.begin_contour ();
-
-                    for (int j = count_f(i)-1; j >= 0; j--)
-                      {
-                        vertex_data::vertex_data_rep *vv
-                          = vdata[i+j*fr].get_rep ();
-                        tess.add_vertex (vv->m_coords.fortran_vec (), vv);
-                      }
-
-                    tess.end_contour ();
-                    tess.end_polygon ();
-                  }
-              }
-
-            set_linestyle ("-");  // Disable LineStipple
-            set_linewidth (0.5f);
-
-            if ((el_mode > 0) && do_lighting)
-              m_glfcns.glDisable (GL_LIGHTING);
-          }
-        else
-          {
-            // FIXME: implement transparency
-          }
-      }
-
-    if (! props.marker_is ("none")
-        && ! (props.markeredgecolor_is ("none")
-              && props.markerfacecolor_is ("none")))
-      {
-        bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
-        bool do_face = draw_all || ! props.markerfacecolor_is ("none");
-
-        Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
-                          props.get_markeredgecolor_rgb ());
-        Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
-                          props.get_markerfacecolor_rgb ());
-
-        bool has_markerfacecolor = draw_all || false;
-
-        if ((mecolor.isempty () && ! props.markeredgecolor_is ("none"))
-            || (mfcolor.isempty () && ! props.markerfacecolor_is ("none")))
-          {
-            Matrix mc = props.get_color_data ().matrix_value ();
-
-            if (mc.rows () == 1)
-              {
-                // Single color specifications, we can simplify a little bit
-                if (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))
-                  mfcolor = mc;
-
-                if (mecolor.isempty () && ! props.markeredgecolor_is ("none"))
-                  mecolor = mc;
-              }
-            else
-              {
-                if (c.isempty ())
-                  c = props.get_color_data ().matrix_value ();
-                has_markerfacecolor = ((c.numel () > 0)
-                                       && (c.rows () == f.rows ()));
-              }
-          }
-
-        init_marker (props.get_marker (), props.get_markersize (),
-                     props.get_linewidth ());
-
-        uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
-        uint8_t clip_ok = 0x40;
-
-        for (int i = 0; i < nf; i++)
-          for (int j = 0; j < count_f(i); j++)
+                    }
+                }
+            }
+
+          // Mesh along X-axis
+
+          if (props.meshstyle_is ("both") || props.meshstyle_is ("row"))
             {
-              int idx = int (f(i,j) - 1);
-
-              if ((clip_code (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0))
-                   & clip_mask) != clip_ok)
+              for (int j = 0; j < zr; j++)
+                {
+                  if (x_mat)
+                    {
+                      j1 = j-1;
+                      j2 = j;
+                    }
+
+                  for (int i = 1; i < zc; i++)
+                    {
+                      if (clip(j, i-1) || clip(j, i))
+                        continue;
+
+                      if (ec_mode == FLAT)
+                        {
+                          // "flat" only needs color at lower-left vertex
+                          if (! math::isfinite (c(j, i-1)))
+                            continue;
+                        }
+                      else if (ec_mode == INTERP)
+                        {
+                          // "interp" needs valid color at both vertices
+                          if (! (math::isfinite (c(j, i-1))
+                                 && math::isfinite (c(j, i))))
+                            continue;
+                        }
+
+                      if (y_mat)
+                        {
+                          i1 = i-1;
+                          i2 = i;
+                        }
+
+                      m_glfcns.glBegin (GL_LINES);
+
+                      // Vertex 1
+                      if (ec_mode > 0)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = c(j, i-1, k);
+                          m_glfcns.glColor3fv (cb);
+
+                          if (el_mode > 0)
+                            {
+                              for (int k = 0; k < 3; k++)
+                                cb[k] *= as;
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ds * c(j, i-1, k);
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
+                                                     cb);
+                            }
+                        }
+                      if (el_mode > 0)
+                        {
+                          if (el_mode == GOURAUD)
+                            set_normal (bfl_mode, vn, j, i-1);
+                          else
+                            set_normal (bfl_mode, fn, std::min (j, zr-2), i-1);
+                        }
+
+                      m_glfcns.glVertex3d (x(j2, i-1), y(j, i1), z(j, i-1));
+
+                      // Vertex 2
+                      if (ec_mode == INTERP)
+                        {
+                          for (int k = 0; k < 3; k++)
+                            cb[k] = c(j, i, k);
+                          m_glfcns.glColor3fv (cb);
+
+                          if (el_mode > 0)
+                            {
+                              for (int k = 0; k < 3; k++)
+                                cb[k] *= as;
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ds * c(j, i, k);
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE,
+                                                     cb);
+
+                              for (int k = 0; k < 3; k++)
+                                cb[k] = ss * (scr + (1-scr) * c(j, i, k));
+                              m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR,
+                                                     cb);
+                            }
+                        }
+                      if (el_mode == GOURAUD)
+                        set_normal (bfl_mode, vn, j, i);
+
+                      m_glfcns.glVertex3d (x(j2, i), y(j, i2), z(j, i));
+
+                      m_glfcns.glEnd ();
+                    }
+                }
+            }
+
+          set_linestyle ("-");  // Disable LineStipple
+          set_linewidth (0.5f);
+
+          if ((el_mode > 0) && do_lighting)
+            m_glfcns.glDisable (GL_LIGHTING);
+        }
+      else
+        {
+          // FIXME: implement transparency
+        }
+    }
+
+  if (! props.marker_is ("none")
+      && ! (props.markeredgecolor_is ("none")
+            && props.markerfacecolor_is ("none")))
+    {
+      // FIXME: check how transparency should be handled in markers
+      // FIXME: check what to do with marker facecolor set to auto
+      //        and facecolor set to none.
+
+      bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
+      bool do_face = draw_all || ! props.markerfacecolor_is ("none");
+
+      Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
+                        props.get_markeredgecolor_rgb ());
+      Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
+                        props.get_markerfacecolor_rgb ());
+      Matrix cc (1, 3, 0.0);
+
+      if (mecolor.isempty () && props.markeredgecolor_is ("auto"))
+        {
+          mecolor = props.get_edgecolor_rgb ();
+          do_edge = ! props.edgecolor_is ("none");
+        }
+
+      if (mfcolor.isempty () && props.markerfacecolor_is ("auto"))
+        {
+          mfcolor = props.get_facecolor_rgb ();
+          do_face = ! props.facecolor_is ("none");
+        }
+
+      if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ())
+        c = props.get_color_data ().array_value ();
+
+      init_marker (props.get_marker (), props.get_markersize (),
+                   props.get_linewidth ());
+
+      uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
+      uint8_t clip_ok = 0x40;
+
+      for (int i = 0; i < zc; i++)
+        {
+          if (y_mat)
+            i1 = i;
+
+          for (int j = 0; j < zr; j++)
+            {
+              if (x_mat)
+                j1 = j;
+
+              if ((clip_code (x(j1, i), y(j, i1), z(j, i)) & clip_mask)
+                  != clip_ok)
                 continue;
 
-              Matrix cc;
-              if (c.numel () > 0)
+              if ((do_edge && mecolor.isempty ())
+                  || (do_face && mfcolor.isempty ()))
                 {
-                  cc.resize (1, 3);
-                  if (has_markerfacecolor)
-                    cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2);
-                  else
-                    cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2);
+                  if (! math::isfinite (c(j, i)))
+                    continue;  // Skip NaNs in color data
+
+                  for (int k = 0; k < 3; k++)
+                    cc(k) = c(j, i, k);
                 }
 
               Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor)
-                                   : Matrix ());
+                           : Matrix ());
               Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor)
-                                   : Matrix ());
-
-              draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc);
+                           : Matrix ());
+
+              draw_marker (x(j1, i), y(j, i1), z(j, i), lc, fc);
             }
-
-        end_marker ();
-      }
+        }
+
+      end_marker ();
+    }
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_scatter (const scatter::properties& props)
-  {
+}
+
+// FIXME: global optimization (rendering, data structures...),
+// there is probably a smarter/faster/less-memory-consuming way to do this.
+void
+opengl_renderer::draw_patch (const patch::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    // Do not render if the scatter object has incoherent data
-    std::string msg;
-    if (props.has_bad_data (msg))
-      {
-        warning ("opengl_renderer: %s.  Not rendering.", msg.c_str ());
-        return;
-      }
-
-    bool draw_all = m_selecting;
-
-    if (draw_all || (! props.marker_is ("none")
-                     && ! (props.markeredgecolor_is ("none")
-                           && props.markerfacecolor_is ("none"))))
+  // Do not render if the patch has incoherent data
+  std::string msg;
+  if (props.has_bad_data (msg))
+    {
+      warning ("opengl_renderer: %s.  Not rendering.", msg.c_str ());
+      return;
+    }
+
+  bool draw_all = m_selecting && props.pickableparts_is ("all");
+  const Matrix f = props.get_faces ().matrix_value ();
+  const Matrix v = m_xform.scale (props.get_vertices ().matrix_value ());
+  Matrix c;
+  Matrix a;
+  double fa = 1.0;
+
+  int nv = v.rows ();
+  int nf = f.rows ();
+  int fcmax = f.columns ();
+
+  bool has_z = (v.columns () > 2);
+  bool has_facecolor = false;
+  bool has_facealpha = false;
+
+  int fc_mode = ((props.facecolor_is ("none")
+                  || props.facecolor_is_rgb () || draw_all) ? 0 :
+                 (props.facecolor_is ("flat") ? 1 : 2));
+  int fl_mode = (props.facelighting_is ("none") ? 0 :
+                 (props.facelighting_is ("flat") ? 1 : 2));
+  int fa_mode = (props.facealpha_is_double () ? 0 :
+                 (props.facealpha_is ("flat") ? 1 : 2));
+  int ec_mode = ((props.edgecolor_is ("none")
+                  || props.edgecolor_is_rgb ()) ? 0 :
+                 (props.edgecolor_is ("flat") ? 1 : 2));
+  int el_mode = (props.edgelighting_is ("none") ? 0 :
+                 (props.edgelighting_is ("flat") ? 1 : 2));
+  int ea_mode = (props.edgealpha_is_double () ? 0 :
+                 (props.edgealpha_is ("flat") ? 1 : 2));
+  int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
+                  (props.backfacelighting_is ("reverselit") ? 1 : 2));
+  bool do_lighting = props.get_do_lighting ();
+
+  Matrix fcolor = props.get_facecolor_rgb ();
+  Matrix ecolor = props.get_edgecolor_rgb ();
+
+  float as = props.get_ambientstrength ();
+  float ds = props.get_diffusestrength ();
+  float ss = props.get_specularstrength ();
+  float se = props.get_specularexponent () * 5; // to fit Matlab
+  float scr = props.get_specularcolorreflectance ();
+
+  const Matrix vn = props.get_vertexnormals ().matrix_value ();
+  bool has_vertex_normals = (vn.rows () == nv);
+  const Matrix fn = props.get_facenormals ().matrix_value ();
+  bool has_face_normals = (fn.rows () == nf);
+
+  boolMatrix clip (1, nv, false);
+
+  if (has_z)
+    for (int i = 0; i < nv; i++)
+      clip(i) = is_nan_or_inf (v(i, 0), v(i, 1), v(i, 2));
+  else
+    for (int i = 0; i < nv; i++)
+      clip(i) = is_nan_or_inf (v(i, 0), v(i, 1), 0);
+
+  boolMatrix clip_f (1, nf, false);
+  Array<int> count_f (dim_vector (nf, 1), 0);
+
+  for (int i = 0; i < nf; i++)
+    {
+      bool fclip = false;
+      int count = 0;
+
+      for (int j = 0; j < fcmax && ! math::isnan (f(i, j)); j++, count++)
+        fclip = (fclip || clip(int (f(i, j) - 1)));
+
+      clip_f(i) = fclip;
+      count_f(i) = count;
+    }
+
+  if (draw_all || fc_mode > 0 || ec_mode > 0)
+    {
+      if (draw_all)
+        c = Matrix (1, 3, 0.0);
+      else
+        c = props.get_color_data ().matrix_value ();
+
+      if (c.rows () == 1)
+        {
+          // Single color specifications, we can simplify a little bit
+
+          if (draw_all || fc_mode > 0)
+            {
+              fcolor = c;
+              fc_mode = UNIFORM;
+            }
+
+          if (draw_all || ec_mode > 0)
+            {
+              ecolor = c;
+              ec_mode = UNIFORM;
+            }
+
+          c = Matrix ();
+        }
+      else
+        has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ()));
+    }
+
+  if (fa_mode > 0 || ea_mode > 0)
+    {
+      // FIXME: retrieve alpha data from patch object
+      //a = props.get_alpha_data ();
+      has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ()));
+    }
+
+  if (fa_mode == 0)
+    fa = props.get_facealpha_double ();
+
+  octave_idx_type fr = f.rows ();
+  std::vector<vertex_data> vdata (f.numel ());
+
+  for (int i = 0; i < nf; i++)
+    for (int j = 0; j < count_f(i); j++)
       {
-        bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
-        bool do_face = draw_all || ! props.markerfacecolor_is ("none");
-
-        const Matrix x = props.get_xdata ().matrix_value ();
-        const Matrix y = props.get_ydata ().matrix_value ();
-        const Matrix z = props.get_zdata ().matrix_value ();
-        const Matrix c = props.get_color_data ().matrix_value ();
-        const Matrix s = props.get_sizedata ().matrix_value ();
-
-        int np = x.rows ();
-        bool has_z = ! z.isempty ();
-
-        // If markeredgecolor is "flat", mecolor is empty
-        Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
-                          props.get_markeredgecolor_rgb ());
-        Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
-                          props.get_markerfacecolor_rgb ());
-        const double mea = props.get_markeredgealpha ();
-        const double mfa = props.get_markerfacealpha ();
-
-        if (props.markerfacecolor_is ("auto"))
+        int idx = int (f(i, j) - 1);
+
+        Matrix vv (1, 3, 0.0);
+        Matrix cc;
+        Matrix vnn (1, 3, 0.0);
+        Matrix fnn (1, 3, 0.0);
+        double aa = 1.0;
+
+        vv(0) = v(idx, 0); vv(1) = v(idx, 1);
+        if (has_z)
+          vv(2) = v(idx, 2);
+        if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals)
+          {
+            double dir = 1.0;
+            if (bfl_mode > 0)
+              dir = ((fn(i, 0) * m_view_vector(0)
+                      + fn(i, 1) * m_view_vector(1)
+                      + fn(i, 2) * m_view_vector(2) < 0)
+                     ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
+            fnn(0) = dir * fn(i, 0);
+            fnn(1) = dir * fn(i, 1);
+            fnn(2) = dir * fn(i, 2);
+          }
+        if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals)
+          {
+            double dir = 1.0;
+            if (bfl_mode > 0)
+              dir = ((vn(idx, 0) * m_view_vector(0)
+                      + vn(idx, 1) * m_view_vector(1)
+                      + vn(idx, 2) * m_view_vector(2) < 0)
+                     ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
+            vnn(0) = dir * vn(idx, 0);
+            vnn(1) = dir * vn(idx, 1);
+            vnn(2) = dir * vn(idx, 2);
+          }
+        if (c.numel () > 0)
+          {
+            cc.resize (1, 3);
+            if (has_facecolor)
+              cc(0) = c(i, 0), cc(1) = c(i, 1), cc(2) = c(i, 2);
+            else
+              cc(0) = c(idx, 0), cc(1) = c(idx, 1), cc(2) = c(idx, 2);
+          }
+        if (fa_mode == 0)
+          aa = fa;
+        else if (a.numel () > 0)
           {
-            gh_manager& gh_mgr = __get_gh_manager__ ();
-            graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
-            graphics_object ax = go.get_ancestor ("axes");
-            const axes::properties& ax_props
-              = dynamic_cast<const axes::properties&> (ax.get_properties ());
-
-            mfcolor = ax_props.get_color ().matrix_value ();
+            if (has_facealpha)
+              aa = a(i);
+            else
+              aa = a(idx);
           }
 
-        init_marker (props.get_marker (), std::sqrt (s(0)),
-                     props.get_linewidth ());
-
-        uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
-        uint8_t clip_ok = 0x40;
-
-        Matrix cc;
-        if (! c.isempty ())
+        vdata[i+j*fr]
+          = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr);
+      }
+
+  if (fl_mode > 0 || el_mode > 0)
+    m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
+
+  if (draw_all || ! props.facecolor_is ("none"))
+    {
+      // FIXME: adapt to double-radio property
+      if (fa_mode == 0)
+        {
+          if (fc_mode == UNIFORM)
+            {
+              m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
+              if (fl_mode > 0)
+                {
+                  float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };;
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = as * fcolor(i);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = ds * fcolor(i);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = ss * (scr + (1-scr) * fcolor(i));
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                }
+            }
+
+          if ((fl_mode > 0) && do_lighting)
+            m_glfcns.glEnable (GL_LIGHTING);
+
+          // NOTE: Push filled part of patch backwards to avoid Z-fighting
+          // with tessellator outline.  A value of 1.0 seems to work fine.
+          // Value can't be too large or the patch will be pushed below the
+          // axes planes at +2.5.
+          patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0);
+
+          std::vector<octave_idx_type>::const_iterator it;
+          octave_idx_type i_start, i_end;
+
+          for (int i = 0; i < nf; i++)
+            {
+              if (clip_f(i))
+                continue;
+
+              bool is_non_planar = false;
+              if (props.m_coplanar_last_idx.size () > 0
+                  && props.m_coplanar_last_idx[i].size () > 1)
+                {
+                  is_non_planar = true;
+                  it = props.m_coplanar_last_idx[i].end ();
+                  it--;
+                }
+
+              // loop over planar subsets of face
+              do
+                {
+                  if (is_non_planar)
+                    {
+                      i_end = *it;
+                      if (it == props.m_coplanar_last_idx[i].begin ())
+                        i_start = 0;
+                      else
+                        {
+                          it--;
+                          i_start = *it - 1;
+                        }
+                    }
+                  else
+                    {
+                      i_end = count_f(i) - 1;
+                      i_start = 0;
+                    }
+
+                  tess.begin_polygon (true);
+                  tess.begin_contour ();
+
+                  // Add vertices in reverse order for Matlab compatibility
+                  for (int j = i_end; j > i_start; j--)
+                    {
+                      vertex_data::vertex_data_rep *vv
+                        = vdata[i+j*fr].get_rep ();
+
+                      tess.add_vertex (vv->m_coords.fortran_vec (), vv);
+                    }
+
+                  if (count_f(i) > 0)
+                    {
+                      vertex_data::vertex_data_rep *vv = vdata[i].get_rep ();
+
+                      if (fc_mode == FLAT)
+                        {
+                          // For "flat" shading, use color of 1st vertex.
+                          Matrix col = vv->m_color;
+
+                          if (col.numel () == 3)
+                            {
+                              m_glfcns.glColor4d (col(0), col(1), col(2), fa);
+                              if (fl_mode > 0)
+                                {
+                                  float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+
+                                  for (int k = 0; k < 3; k++)
+                                    cb[k] = (vv->m_ambient * col(k));
+                                  m_glfcns.glMaterialfv (LIGHT_MODE,
+                                                         GL_AMBIENT, cb);
+
+                                  for (int k = 0; k < 3; k++)
+                                    cb[k] = (vv->m_diffuse * col(k));
+                                  m_glfcns.glMaterialfv (LIGHT_MODE,
+                                                         GL_DIFFUSE, cb);
+
+                                  for (int k = 0; k < 3; k++)
+                                    cb[k] = vv->m_specular *
+                                            (vv->m_specular_color_refl
+                                             + (1-vv->m_specular_color_refl) *
+                                             col(k));
+                                  m_glfcns.glMaterialfv (LIGHT_MODE,
+                                                         GL_SPECULAR, cb);
+                                }
+                            }
+                        }
+
+                      tess.add_vertex (vv->m_coords.fortran_vec (), vv);
+                    }
+
+                  tess.end_contour ();
+                  tess.end_polygon ();
+                }
+              while (i_start > 0);
+            }
+
+          if ((fl_mode > 0) && do_lighting)
+            m_glfcns.glDisable (GL_LIGHTING);
+        }
+      else
+        {
+          // FIXME: implement flat and interp transparency
+        }
+    }
+
+  if (draw_all
+      || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")))
+    {
+      // FIXME: adapt to double-radio property
+      if (props.get_edgealpha_double () == 1)
+        {
+          if (ec_mode == UNIFORM)
+            {
+              m_glfcns.glColor3dv (ecolor.data ());
+              if (el_mode > 0)
+                {
+                  // edge lighting only uses ambient light
+                  float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+
+                  for (int i = 0; i < 3; i++)
+                    cb[i] = (as * ecolor(i));
+                  m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                }
+            }
+
+          if ((el_mode > 0) && do_lighting)
+            m_glfcns.glEnable (GL_LIGHTING);
+
+          double linewidth = props.get_linewidth ();
+          set_linestyle (props.get_linestyle (), false, linewidth);
+          set_linewidth (linewidth);
+          set_linecap ("butt");
+          set_linejoin ("miter");
+
+          // NOTE: patch contour cannot be offset.  Offset must occur with
+          // the filled portion of the patch above.  The tessellator uses
+          // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL
+          // automatically sets the glType to GL_LINE_LOOP.  This primitive is
+          // not supported by glPolygonOffset which is used to do Z offsets.
+          patch_tessellator tess (this, ec_mode, el_mode, false);
+
+          for (int i = 0; i < nf; i++)
+            {
+              bool is_non_planar = false;
+              if (props.m_coplanar_last_idx.size () > 0
+                  && props.m_coplanar_last_idx[i].size () > 1)
+                is_non_planar = true;
+              if (clip_f(i) || is_non_planar)
+                {
+                  // This is an unclosed contour or a non-planar face.
+                  // Draw it as a line.
+                  bool flag = false;
+
+                  m_glfcns.glShadeModel ((ec_mode == INTERP
+                                          || el_mode == GOURAUD)
+                                         ? GL_SMOOTH : GL_FLAT);
+
+                  // Add vertices in reverse order for Matlab compatibility
+                  for (int j = count_f(i)-1; j >= 0; j--)
+                    {
+                      if (! clip(int (f(i, j) - 1)))
+                        {
+                          vertex_data::vertex_data_rep *vv
+                            = vdata[i+j*fr].get_rep ();
+                          const Matrix m = vv->m_coords;
+                          if (! flag)
+                            {
+                              flag = true;
+                              m_glfcns.glBegin (GL_LINE_STRIP);
+                            }
+                          if (ec_mode != UNIFORM)
+                            {
+                              Matrix col = vv->m_color;
+
+                              if (col.numel () == 3)
+                                m_glfcns.glColor3dv (col.data ());
+                            }
+                          m_glfcns.glVertex3d (m(0), m(1), m(2));
+                        }
+                      else if (flag)
+                        {
+                          flag = false;
+                          m_glfcns.glEnd ();
+                        }
+                    }
+                  // Do loop body with vertex N to "close" GL_LINE_STRIP
+                  // from vertex 0 to vertex N.
+                  int j = count_f(i)-1;
+                  if (flag && ! clip(int (f(i, j) - 1)))
+                    {
+                      vertex_data::vertex_data_rep *vv
+                        = vdata[i+j*fr].get_rep ();
+                      const Matrix m = vv->m_coords;
+                      if (ec_mode != UNIFORM)
+                        {
+                          Matrix col = vv->m_color;
+
+                          if (col.numel () == 3)
+                            m_glfcns.glColor3dv (col.data ());
+                        }
+                      m_glfcns.glVertex3d (m(0), m(1), m(2));
+                    }
+
+                  if (flag)
+                    m_glfcns.glEnd ();
+                }
+              else  // Normal edge contour drawn with tessellator
+                {
+                  tess.begin_polygon (false);
+                  tess.begin_contour ();
+
+                  for (int j = count_f(i)-1; j >= 0; j--)
+                    {
+                      vertex_data::vertex_data_rep *vv
+                        = vdata[i+j*fr].get_rep ();
+                      tess.add_vertex (vv->m_coords.fortran_vec (), vv);
+                    }
+
+                  tess.end_contour ();
+                  tess.end_polygon ();
+                }
+            }
+
+          set_linestyle ("-");  // Disable LineStipple
+          set_linewidth (0.5f);
+
+          if ((el_mode > 0) && do_lighting)
+            m_glfcns.glDisable (GL_LIGHTING);
+        }
+      else
+        {
+          // FIXME: implement transparency
+        }
+    }
+
+  if (! props.marker_is ("none")
+      && ! (props.markeredgecolor_is ("none")
+            && props.markerfacecolor_is ("none")))
+    {
+      bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
+      bool do_face = draw_all || ! props.markerfacecolor_is ("none");
+
+      Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
+                        props.get_markeredgecolor_rgb ());
+      Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
+                        props.get_markerfacecolor_rgb ());
+
+      bool has_markerfacecolor = draw_all || false;
+
+      if ((mecolor.isempty () && ! props.markeredgecolor_is ("none"))
+          || (mfcolor.isempty () && ! props.markerfacecolor_is ("none")))
+        {
+          Matrix mc = props.get_color_data ().matrix_value ();
+
+          if (mc.rows () == 1)
+            {
+              // Single color specifications, we can simplify a little bit
+              if (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))
+                mfcolor = mc;
+
+              if (mecolor.isempty () && ! props.markeredgecolor_is ("none"))
+                mecolor = mc;
+            }
+          else
+            {
+              if (c.isempty ())
+                c = props.get_color_data ().matrix_value ();
+              has_markerfacecolor = ((c.numel () > 0)
+                                     && (c.rows () == f.rows ()));
+            }
+        }
+
+      init_marker (props.get_marker (), props.get_markersize (),
+                   props.get_linewidth ());
+
+      uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
+      uint8_t clip_ok = 0x40;
+
+      for (int i = 0; i < nf; i++)
+        for (int j = 0; j < count_f(i); j++)
           {
-            if (c.rows () == 1)
-              cc = c;
-            else
+            int idx = int (f(i, j) - 1);
+
+            if ((clip_code (v(idx, 0), v(idx, 1), (has_z ? v(idx, 2) : 0))
+                 & clip_mask) != clip_ok)
+              continue;
+
+            Matrix cc;
+            if (c.numel () > 0)
               {
                 cc.resize (1, 3);
-                cc(0) = c(0,0);
-                cc(1) = c(0,1);
-                cc(2) = c(0,2);
-              }
-          }
-
-        for (int i = 0; i < np; i++)
-          {
-            if ((clip_code (x(i), y(i), (has_z ? z(i) : 0.0)) & clip_mask)
-                 != clip_ok)
-              continue;
-
-            if (c.rows () > 1)
-              {
-                cc(0) = c(i,0);
-                cc(1) = c(i,1);
-                cc(2) = c(i,2);
+                if (has_markerfacecolor)
+                  cc(0) = c(i, 0), cc(1) = c(i, 1), cc(2) = c(i, 2);
+                else
+                  cc(0) = c(idx, 0), cc(1) = c(idx, 1), cc(2) = c(idx, 2);
               }
 
             Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor)
-                                 : Matrix ());
+                         : Matrix ());
             Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor)
-                                 : Matrix ());
-
-            if (s.numel () > 1)
-              change_marker (props.get_marker (), std::sqrt (s(i)));
-
-            draw_marker (x(i), y(i), (has_z ? z(i) : 0.0), lc, fc, mea, mfa);
+                         : Matrix ());
+
+            draw_marker (v(idx, 0), v(idx, 1), (has_z ? v(idx, 2) : 0), lc, fc);
           }
 
-        end_marker ();
-      }
+      end_marker ();
+    }
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-
-#endif
-  }
-
-  void
-  opengl_renderer::draw_light (const light::properties& props)
-  {
-#if defined (HAVE_OPENGL)
-
-    // enable light source
-    m_glfcns.glEnable (m_current_light);
-
-    // light position
-    float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local
-    Matrix lpos = props.get_position ().matrix_value ();
-    for (int i = 0; i < 3; i++)
-      pos[i] = lpos(i);
-    if (props.style_is ("local"))
-      pos[3] = 1;
-    m_glfcns.glLightfv (m_current_light, GL_POSITION, pos);
-
-    // light color
-    float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning)
-    Matrix lcolor = props.get_color ().matrix_value ();
-    for (int i = 0; i < 3; i++)
-      col[i] = lcolor(i);
-    m_glfcns.glLightfv (m_current_light, GL_DIFFUSE,  col);
-    m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col);
-
-#else
-
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_hggroup (const hggroup::properties& props)
-  {
-    draw (props.get_children ());
-  }
-
-  void
-  opengl_renderer::set_ortho_coordinates (void)
-  {
+}
+
+void
+opengl_renderer::draw_scatter (const scatter::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
-
-    Matrix vp = get_viewport_scaled ();
-    m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2);
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
+  // Do not render if the scatter object has incoherent data
+  std::string msg;
+  if (props.has_bad_data (msg))
+    {
+      warning ("opengl_renderer: %s.  Not rendering.", msg.c_str ());
+      return;
+    }
+
+  bool draw_all = m_selecting;
+
+  if (draw_all || (! props.marker_is ("none")
+                   && ! (props.markeredgecolor_is ("none")
+                         && props.markerfacecolor_is ("none"))))
+    {
+      bool do_edge = draw_all || ! props.markeredgecolor_is ("none");
+      bool do_face = draw_all || ! props.markerfacecolor_is ("none");
+
+      const Matrix x = props.get_xdata ().matrix_value ();
+      const Matrix y = props.get_ydata ().matrix_value ();
+      const Matrix z = props.get_zdata ().matrix_value ();
+      const Matrix c = props.get_color_data ().matrix_value ();
+      const Matrix s = props.get_sizedata ().matrix_value ();
+
+      int np = x.rows ();
+      bool has_z = ! z.isempty ();
+
+      // If markeredgecolor is "flat", mecolor is empty
+      Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) :
+                        props.get_markeredgecolor_rgb ());
+      Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) :
+                        props.get_markerfacecolor_rgb ());
+      const double mea = props.get_markeredgealpha ();
+      const double mfa = props.get_markerfacealpha ();
+
+      if (props.markerfacecolor_is ("auto"))
+        {
+          gh_manager& gh_mgr = __get_gh_manager__ ();
+          graphics_object go = gh_mgr.get_object (props.get___myhandle__ ());
+          graphics_object ax = go.get_ancestor ("axes");
+          const axes::properties& ax_props
+            = dynamic_cast<const axes::properties&> (ax.get_properties ());
+
+          mfcolor = ax_props.get_color ().matrix_value ();
+        }
+
+      init_marker (props.get_marker (), std::sqrt (s(0)),
+                   props.get_linewidth ());
+
+      uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40);
+      uint8_t clip_ok = 0x40;
+
+      Matrix cc;
+      if (! c.isempty ())
+        {
+          if (c.rows () == 1)
+            cc = c;
+          else
+            {
+              cc.resize (1, 3);
+              cc(0) = c(0, 0);
+              cc(1) = c(0, 1);
+              cc(2) = c(0, 2);
+            }
+        }
+
+      for (int i = 0; i < np; i++)
+        {
+          if ((clip_code (x(i), y(i), (has_z ? z(i) : 0.0)) & clip_mask)
+              != clip_ok)
+            continue;
+
+          if (c.rows () > 1)
+            {
+              cc(0) = c(i, 0);
+              cc(1) = c(i, 1);
+              cc(2) = c(i, 2);
+            }
+
+          Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor)
+                       : Matrix ());
+          Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor)
+                       : Matrix ());
+
+          if (s.numel () > 1)
+            change_marker (props.get_marker (), std::sqrt (s(i)));
+
+          draw_marker (x(i), y(i), (has_z ? z(i) : 0.0), lc, fc, mea, mfa);
+        }
+
+      end_marker ();
+    }
 
 #else
 
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::restore_previous_coordinates (void)
-  {
+}
+
+void
+opengl_renderer::draw_light (const light::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    // Restore previous coordinate system
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPopMatrix();
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPopMatrix();
+  // enable light source
+  m_glfcns.glEnable (m_current_light);
+
+  // light position
+  float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local
+  Matrix lpos = props.get_position ().matrix_value ();
+  for (int i = 0; i < 3; i++)
+    pos[i] = lpos(i);
+  if (props.style_is ("local"))
+    pos[3] = 1;
+  m_glfcns.glLightfv (m_current_light, GL_POSITION, pos);
+
+  // light color
+  float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning)
+  Matrix lcolor = props.get_color ().matrix_value ();
+  for (int i = 0; i < 3; i++)
+    col[i] = lcolor(i);
+  m_glfcns.glLightfv (m_current_light, GL_DIFFUSE,  col);
+  m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col);
 
 #else
 
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_text (const text::properties& props)
-  {
+}
+
+void
+opengl_renderer::draw_hggroup (const hggroup::properties& props)
+{
+  draw (props.get_children ());
+}
+
+void
+opengl_renderer::set_ortho_coordinates (void)
+{
 #if defined (HAVE_OPENGL)
 
-    if (props.get_string ().isempty () || props.color_is ("none"))
-      return;
-
-    Matrix pos = m_xform.scale (props.get_data_position ());
-
-    // Handle clipping manually when drawing text in ortho coordinates
-    if (! props.is_clipping ()
-        || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40))
-      {
-        set_clipping (false);
-
-        draw_text_background (props);
-
-        set_font (props);
-
-        render_text (props.get_pixels (), props.get_extent_matrix (),
-                     pos(0), pos(1), pos(2), props.get_rotation ());
-
-        set_clipping (props.is_clipping ());
-      }
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glPushMatrix ();
+  m_glfcns.glLoadIdentity ();
+
+  Matrix vp = get_viewport_scaled ();
+  m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2);
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glPushMatrix ();
+  m_glfcns.glLoadIdentity ();
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+void
+opengl_renderer::restore_previous_coordinates (void)
+{
+#if defined (HAVE_OPENGL)
+
+  // Restore previous coordinate system
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glPopMatrix();
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glPopMatrix();
+
+#else
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_text_background (const text::properties& props,
-                                         bool /*do_rotate*/)
-  {
+}
+
+void
+opengl_renderer::draw_text (const text::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    Matrix bgcol = props.get_backgroundcolor_rgb ();
-    Matrix ecol = props.get_edgecolor_rgb ();
-
-    if (bgcol.isempty () && ecol.isempty ())
-      return;
-
-    Matrix pos = props.get_data_position ();
-    ColumnVector pixpos = get_transform ().transform (pos(0), pos(1),
-                                                      pos(2), true);
-
-    // Save current transform matrices and set orthogonal window coordinates
-    set_ortho_coordinates ();
-
-    // Translate coordinates so that the text anchor is (0,0)
-    m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
-
-    // FIXME: Only multiples of 90° are handled by the text renderer.
-    //        Handle others here.
-    double rotation = props.get_rotation ();
-
-    m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
-
-    double m = points_to_pixels (props.get_margin ());
-    const Matrix bbox = props.get_extent_matrix ();
-    double x0 = bbox (0) / m_devpixratio - m;
-    double x1 = x0 + bbox(2) / m_devpixratio + 2 * m;
-    double y0 = -(bbox (1) / m_devpixratio - m);
-    double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m);
-
-    if (! bgcol.isempty ())
-      {
-        m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2));
-
-        bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST);
-        if (depth_test)
-          set_polygon_offset (true, 4.0);
-
-        m_glfcns.glBegin (GL_QUADS);
-        m_glfcns.glVertex2d (x0, y0);
-        m_glfcns.glVertex2d (x1, y0);
-        m_glfcns.glVertex2d (x1, y1);
-        m_glfcns.glVertex2d (x0, y1);
-        m_glfcns.glEnd ();
-
-        if (depth_test)
-          set_polygon_offset (false);
-      }
-
-    if (! ecol.isempty ())
-      {
-        m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2));
-
-        set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
-        set_linewidth (props.get_linewidth ());
-
-        m_glfcns.glBegin (GL_LINE_STRIP);
-        m_glfcns.glVertex2d (x0, y0);
-        m_glfcns.glVertex2d (x1, y0);
-        m_glfcns.glVertex2d (x1, y1);
-        m_glfcns.glVertex2d (x0, y1);
-        m_glfcns.glVertex2d (x0, y0);
-        m_glfcns.glEnd ();
-
-        set_linestyle ("-");
-      }
-
-    restore_previous_coordinates ();
+  if (props.get_string ().isempty () || props.color_is ("none"))
+    return;
+
+  Matrix pos = m_xform.scale (props.get_data_position ());
+
+  // Handle clipping manually when drawing text in ortho coordinates
+  if (! props.is_clipping ()
+      || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40))
+    {
+      set_clipping (false);
+
+      draw_text_background (props);
+
+      set_font (props);
+
+      render_text (props.get_pixels (), props.get_extent_matrix (),
+                   pos(0), pos(1), pos(2), props.get_rotation ());
+
+      set_clipping (props.is_clipping ());
+    }
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_image (const image::properties& props)
-  {
+}
+
+void
+opengl_renderer::draw_text_background (const text::properties& props,
+                                       bool /*do_rotate*/)
+{
 #if defined (HAVE_OPENGL)
 
-    octave_value cdata = props.get_color_data ();
-    Matrix x = props.get_xdata ().matrix_value ();
-    Matrix y = props.get_ydata ().matrix_value ();
-
-    draw_texture_image (cdata, x, y);
+  Matrix bgcol = props.get_backgroundcolor_rgb ();
+  Matrix ecol = props.get_edgecolor_rgb ();
+
+  if (bgcol.isempty () && ecol.isempty ())
+    return;
+
+  Matrix pos = props.get_data_position ();
+  ColumnVector pixpos = get_transform ().transform (pos(0), pos(1),
+                        pos(2), true);
+
+  // Save current transform matrices and set orthogonal window coordinates
+  set_ortho_coordinates ();
+
+  // Translate coordinates so that the text anchor is (0,0)
+  m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
+
+  // FIXME: Only multiples of 90° are handled by the text renderer.
+  //        Handle others here.
+  double rotation = props.get_rotation ();
+
+  m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
+
+  double m = points_to_pixels (props.get_margin ());
+  const Matrix bbox = props.get_extent_matrix ();
+  double x0 = bbox (0) / m_devpixratio - m;
+  double x1 = x0 + bbox(2) / m_devpixratio + 2 * m;
+  double y0 = -(bbox (1) / m_devpixratio - m);
+  double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m);
+
+  if (! bgcol.isempty ())
+    {
+      m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2));
+
+      bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST);
+      if (depth_test)
+        set_polygon_offset (true, 4.0);
+
+      m_glfcns.glBegin (GL_QUADS);
+      m_glfcns.glVertex2d (x0, y0);
+      m_glfcns.glVertex2d (x1, y0);
+      m_glfcns.glVertex2d (x1, y1);
+      m_glfcns.glVertex2d (x0, y1);
+      m_glfcns.glEnd ();
+
+      if (depth_test)
+        set_polygon_offset (false);
+    }
+
+  if (! ecol.isempty ())
+    {
+      m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2));
+
+      set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
+      set_linewidth (props.get_linewidth ());
+
+      m_glfcns.glBegin (GL_LINE_STRIP);
+      m_glfcns.glVertex2d (x0, y0);
+      m_glfcns.glVertex2d (x1, y0);
+      m_glfcns.glVertex2d (x1, y1);
+      m_glfcns.glVertex2d (x0, y1);
+      m_glfcns.glVertex2d (x0, y0);
+      m_glfcns.glEnd ();
+
+      set_linestyle ("-");
+    }
+
+  restore_previous_coordinates ();
 
 #else
 
-    octave_unused_parameter (props);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_texture_image (const octave_value cdata, Matrix x,
-                                       Matrix y, bool ortho)
-  {
+}
+
+void
+opengl_renderer::draw_image (const image::properties& props)
+{
 #if defined (HAVE_OPENGL)
 
-    dim_vector dv (cdata.dims ());
-    int h = dv(0);
-    int w = dv(1);
-    double x0, x1, y0, y1;
-
-    double dx = 1.0;
-    if (w > 1)
-      dx = (x(1) - x(0)) / (w - 1);
-
-    x0 = x(0)-dx/2;
-    x1 = x(1)+dx/2;
-
-    double dy = 1.0;
-    if (h > 1)
-      dy = (y(1) - y(0)) / (h - 1);
-
-    y0 = y(0)-dy/2;
-    y1 = y(1)+dy/2;
-
-    // Expect RGB data
-    if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4))
-      {
-        opengl_texture tex  = opengl_texture::create (m_glfcns, cdata);
-        if (tex.is_valid ())
-          {
-            m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0);
-
-            m_glfcns.glEnable (GL_TEXTURE_2D);
-
-            m_glfcns.glBegin (GL_QUADS);
-
-            tex.tex_coord (0.0, 0.0);
-            if (ortho)
-              m_glfcns.glVertex2d (x0, y0);
-            else
-              m_glfcns.glVertex3d (x0, y0, 0.0);
-
-            tex.tex_coord (1.0, 0.0);
-            if (ortho)
-              m_glfcns.glVertex2d (x1, y0);
-            else
-              m_glfcns.glVertex3d (x1, y0, 0.0);
-
-            tex.tex_coord (1.0, 1.0);
-            if (ortho)
-              m_glfcns.glVertex2d (x1, y1);
-            else
-              m_glfcns.glVertex3d (x1, y1, 0.0);
-
-            tex.tex_coord (0.0, 1.0);
-            if (ortho)
-              m_glfcns.glVertex2d (x0, y1);
-            else
-              m_glfcns.glVertex3d (x0, y1, 0.0);
-
-            m_glfcns.glEnd ();
-            m_glfcns.glDisable (GL_TEXTURE_2D);
-          }
-      }
-    else
-      warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)");
+  octave_value cdata = props.get_color_data ();
+  Matrix x = props.get_xdata ().matrix_value ();
+  Matrix y = props.get_ydata ().matrix_value ();
+
+  draw_texture_image (cdata, x, y);
 
 #else
 
-    octave_unused_parameter (cdata);
-    octave_unused_parameter (x);
-    octave_unused_parameter (y);
-    octave_unused_parameter (ortho);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (props);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void opengl_renderer::draw (const Matrix& hlist, bool toplevel)
-  {
-    int len = hlist.numel ();
-
-    gh_manager& gh_mgr = __get_gh_manager__ ();
-
-    for (int i = len-1; i >= 0; i--)
-      {
-        graphics_object obj = gh_mgr.get_object (hlist(i));
-
-        if (obj)
-          draw (obj, toplevel);
-      }
-  }
-
-  void
-  opengl_renderer::set_viewport (int w, int h)
-  {
+}
+
+void
+opengl_renderer::draw_texture_image (const octave_value cdata, Matrix x,
+                                     Matrix y, bool ortho)
+{
 #if defined (HAVE_OPENGL)
 
-    m_glfcns.glViewport (0, 0, w, h);
+  dim_vector dv (cdata.dims ());
+  int h = dv(0);
+  int w = dv(1);
+  double x0, x1, y0, y1;
+
+  double dx = 1.0;
+  if (w > 1)
+    dx = (x(1) - x(0)) / (w - 1);
+
+  x0 = x(0)-dx/2;
+  x1 = x(1)+dx/2;
+
+  double dy = 1.0;
+  if (h > 1)
+    dy = (y(1) - y(0)) / (h - 1);
+
+  y0 = y(0)-dy/2;
+  y1 = y(1)+dy/2;
+
+  // Expect RGB data
+  if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4))
+    {
+      opengl_texture tex  = opengl_texture::create (m_glfcns, cdata);
+      if (tex.is_valid ())
+        {
+          m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0);
+
+          m_glfcns.glEnable (GL_TEXTURE_2D);
+
+          m_glfcns.glBegin (GL_QUADS);
+
+          tex.tex_coord (0.0, 0.0);
+          if (ortho)
+            m_glfcns.glVertex2d (x0, y0);
+          else
+            m_glfcns.glVertex3d (x0, y0, 0.0);
+
+          tex.tex_coord (1.0, 0.0);
+          if (ortho)
+            m_glfcns.glVertex2d (x1, y0);
+          else
+            m_glfcns.glVertex3d (x1, y0, 0.0);
+
+          tex.tex_coord (1.0, 1.0);
+          if (ortho)
+            m_glfcns.glVertex2d (x1, y1);
+          else
+            m_glfcns.glVertex3d (x1, y1, 0.0);
+
+          tex.tex_coord (0.0, 1.0);
+          if (ortho)
+            m_glfcns.glVertex2d (x0, y1);
+          else
+            m_glfcns.glVertex3d (x0, y1, 0.0);
+
+          m_glfcns.glEnd ();
+          m_glfcns.glDisable (GL_TEXTURE_2D);
+        }
+    }
+  else
+    warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)");
 
 #else
 
-    octave_unused_parameter (w);
-    octave_unused_parameter (h);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (cdata);
+  octave_unused_parameter (x);
+  octave_unused_parameter (y);
+  octave_unused_parameter (ortho);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  Matrix
-  opengl_renderer::get_viewport_scaled (void) const
-  {
-    Matrix retval (1, 4, 0.0);
+}
+
+void opengl_renderer::draw (const Matrix& hlist, bool toplevel)
+{
+  int len = hlist.numel ();
+
+  gh_manager& gh_mgr = __get_gh_manager__ ();
+
+  for (int i = len-1; i >= 0; i--)
+    {
+      graphics_object obj = gh_mgr.get_object (hlist(i));
+
+      if (obj)
+        draw (obj, toplevel);
+    }
+}
+
+void
+opengl_renderer::set_viewport (int w, int h)
+{
+#if defined (HAVE_OPENGL)
+
+  m_glfcns.glViewport (0, 0, w, h);
+
+#else
+
+  octave_unused_parameter (w);
+  octave_unused_parameter (h);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+
+#endif
+}
+
+Matrix
+opengl_renderer::get_viewport_scaled (void) const
+{
+  Matrix retval (1, 4, 0.0);
 
 #if defined (HAVE_OPENGL)
 #if defined (HAVE_FRAMEWORK_OPENGL)
-    GLint vp[4];
+  GLint vp[4];
 #else
-    int vp[4];
+  int vp[4];
 #endif
 
-    m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
-
-    for (int i = 0; i < 4; i++)
-      retval(i) = static_cast<double> (vp[i]) / m_devpixratio;
+  m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
+
+  for (int i = 0; i < 4; i++)
+    retval(i) = static_cast<double> (vp[i]) / m_devpixratio;
 
 #else
 
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
 
-    return retval;
-  }
-
-  void
-  opengl_renderer::set_color (const Matrix& c)
-  {
+  return retval;
+}
+
+void
+opengl_renderer::set_color (const Matrix& c)
+{
 #if defined (HAVE_OPENGL)
 
-    m_glfcns.glColor3dv (c.data ());
-
-    if (! c.isempty ())
-      m_txt_renderer.set_color (c);
+  m_glfcns.glColor3dv (c.data ());
+
+  if (! c.isempty ())
+    m_txt_renderer.set_color (c);
 
 #else
 
-    octave_unused_parameter (c);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (c);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::set_font (const base_properties& props)
-  {
-    bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on";
-    m_txt_renderer.set_anti_aliasing (do_anti_alias);
-    m_txt_renderer.set_font (props.get ("fontname").string_value (),
-                             props.get ("fontweight").string_value (),
-                             props.get ("fontangle").string_value (),
-                             props.get ("__fontsize_points__").double_value ()
-                             * m_devpixratio);
-  }
-
-  void
-  opengl_renderer::set_polygon_offset (bool on, float offset)
-  {
+}
+
+void
+opengl_renderer::set_font (const base_properties& props)
+{
+  bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on";
+  m_txt_renderer.set_anti_aliasing (do_anti_alias);
+  m_txt_renderer.set_font (props.get ("fontname").string_value (),
+                           props.get ("fontweight").string_value (),
+                           props.get ("fontangle").string_value (),
+                           props.get ("__fontsize_points__").double_value ()
+                           * m_devpixratio);
+}
+
+void
+opengl_renderer::set_polygon_offset (bool on, float offset)
+{
 #if defined (HAVE_OPENGL)
 
-    if (on)
-      {
-        m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL);
-        m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE);
-        m_glfcns.glPolygonOffset (offset, offset);
-      }
-    else
-      {
-        m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL);
-        m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE);
-      }
+  if (on)
+    {
+      m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL);
+      m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE);
+      m_glfcns.glPolygonOffset (offset, offset);
+    }
+  else
+    {
+      m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL);
+      m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE);
+    }
 
 #else
 
-    octave_unused_parameter (on);
-    octave_unused_parameter (offset);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (on);
+  octave_unused_parameter (offset);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::set_linewidth (float w)
-  {
+}
+
+void
+opengl_renderer::set_linewidth (float w)
+{
 #if defined (HAVE_OPENGL)
-    // Measure LineWidth in points.  See bug #53056.
-    m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio);
+  // Measure LineWidth in points.  See bug #53056.
+  m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio);
 
 #else
 
-    octave_unused_parameter (w);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (w);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::set_linestyle (const std::string& s, bool use_stipple,
-                                  double linewidth)
-  {
+}
+
+void
+opengl_renderer::set_linestyle (const std::string& s, bool use_stipple,
+                                double linewidth)
+{
 #if defined (HAVE_OPENGL)
-    // Measure LineWidth in points.  See bug #53056.
-    int factor = math::round (points_to_pixels (linewidth) * m_devpixratio);
-    if (factor < 1)
-      factor = 1;
-
-    uint16_t pattern = 0xFFFF;
-
-    bool solid = false;
-
-    if (s == "-")
-      solid = true;
-    else if (s == ":")
-      {
-        if (factor > 1)
-          pattern = 0x5555;
-        else
-          pattern = 0x1111;
-      }
-    else if (s == "--")
-      {
-        if (factor > 1)
-          pattern = 0x0F0F;
-        else
-          pattern = 0x01FF;
-      }
-    else if (s == "-.")
-      {
-        if (factor > 1)
-          pattern = 0x6F6F;
-        else
-          pattern = 0x18FF;
-      }
-    else
-      pattern = 0x0000;
-
-    m_glfcns.glLineStipple (factor, pattern);
-
-    if (solid && ! use_stipple)
-      m_glfcns.glDisable (GL_LINE_STIPPLE);
-    else
-      m_glfcns.glEnable (GL_LINE_STIPPLE);
+  // Measure LineWidth in points.  See bug #53056.
+  int factor = math::round (points_to_pixels (linewidth) * m_devpixratio);
+  if (factor < 1)
+    factor = 1;
+
+  uint16_t pattern = 0xFFFF;
+
+  bool solid = false;
+
+  if (s == "-")
+    solid = true;
+  else if (s == ":")
+    {
+      if (factor > 1)
+        pattern = 0x5555;
+      else
+        pattern = 0x1111;
+    }
+  else if (s == "--")
+    {
+      if (factor > 1)
+        pattern = 0x0F0F;
+      else
+        pattern = 0x01FF;
+    }
+  else if (s == "-.")
+    {
+      if (factor > 1)
+        pattern = 0x6F6F;
+      else
+        pattern = 0x18FF;
+    }
+  else
+    pattern = 0x0000;
+
+  m_glfcns.glLineStipple (factor, pattern);
+
+  if (solid && ! use_stipple)
+    m_glfcns.glDisable (GL_LINE_STIPPLE);
+  else
+    m_glfcns.glEnable (GL_LINE_STIPPLE);
 
 #else
 
-    octave_unused_parameter (s);
-    octave_unused_parameter (use_stipple);
-    octave_unused_parameter (linewidth);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (s);
+  octave_unused_parameter (use_stipple);
+  octave_unused_parameter (linewidth);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2,
-                                double z1, double z2)
-  {
+}
+
+void
+opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2,
+                              double z1, double z2)
+{
 #if defined (HAVE_OPENGL)
 
-    double dx = (x2-x1);
-    double dy = (y2-y1);
-    double dz = (z2-z1);
-
-    x1 -= 0.001*dx; x2 += 0.001*dx;
-    y1 -= 0.001*dy; y2 += 0.001*dy;
-    z1 -= 0.001*dz; z2 += 0.001*dz;
-
-    ColumnVector p (4, 0.0);
-
-    p(0) = -1; p(3) = x2;
-    m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ());
-    p(0) = 1; p(3) = -x1;
-    m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ());
-    p(0) = 0; p(1) = -1; p(3) = y2;
-    m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ());
-    p(1) = 1; p(3) = -y1;
-    m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ());
-    p(1) = 0; p(2) = -1; p(3) = z2;
-    m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ());
-    p(2) = 1; p(3) = -z1;
-    m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ());
-
-    m_xmin = x1; m_xmax = x2;
-    m_ymin = y1; m_ymax = y2;
-    m_zmin = z1; m_zmax = z2;
+  double dx = (x2-x1);
+  double dy = (y2-y1);
+  double dz = (z2-z1);
+
+  x1 -= 0.001*dx; x2 += 0.001*dx;
+  y1 -= 0.001*dy; y2 += 0.001*dy;
+  z1 -= 0.001*dz; z2 += 0.001*dz;
+
+  ColumnVector p (4, 0.0);
+
+  p(0) = -1; p(3) = x2;
+  m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ());
+  p(0) = 1; p(3) = -x1;
+  m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ());
+  p(0) = 0; p(1) = -1; p(3) = y2;
+  m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ());
+  p(1) = 1; p(3) = -y1;
+  m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ());
+  p(1) = 0; p(2) = -1; p(3) = z2;
+  m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ());
+  p(2) = 1; p(3) = -z1;
+  m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ());
+
+  m_xmin = x1; m_xmax = x2;
+  m_ymin = y1; m_ymax = y2;
+  m_zmin = z1; m_zmax = z2;
 
 #else
 
-    octave_unused_parameter (x1);
-    octave_unused_parameter (x2);
-    octave_unused_parameter (y1);
-    octave_unused_parameter (y2);
-    octave_unused_parameter (z1);
-    octave_unused_parameter (z2);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (x1);
+  octave_unused_parameter (x2);
+  octave_unused_parameter (y1);
+  octave_unused_parameter (y2);
+  octave_unused_parameter (z1);
+  octave_unused_parameter (z2);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::set_clipping (bool enable)
-  {
+}
+
+void
+opengl_renderer::set_clipping (bool enable)
+{
 #if defined (HAVE_OPENGL)
 
-    bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
-
-    if (enable != has_clipping)
-      {
-        if (enable)
-          for (int i = 0; i < 6; i++)
-            m_glfcns.glEnable (GL_CLIP_PLANE0+i);
-        else
-          for (int i = 0; i < 6; i++)
-            m_glfcns.glDisable (GL_CLIP_PLANE0+i);
-      }
+  bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
+
+  if (enable != has_clipping)
+    {
+      if (enable)
+        for (int i = 0; i < 6; i++)
+          m_glfcns.glEnable (GL_CLIP_PLANE0+i);
+      else
+        for (int i = 0; i < 6; i++)
+          m_glfcns.glDisable (GL_CLIP_PLANE0+i);
+    }
 
 #else
 
-    octave_unused_parameter (enable);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (enable);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::init_marker (const std::string& m, double size, float width)
-  {
+}
+
+void
+opengl_renderer::init_marker (const std::string& m, double size, float width)
+{
 #if defined (HAVE_OPENGL)
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
-
-    Matrix vp = get_viewport_scaled ();
-    m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2);
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPushMatrix ();
-
-    set_clipping (false);
-    set_linewidth (width);
-
-    m_marker_id = make_marker_list (m, size, false);
-    m_filled_marker_id = make_marker_list (m, size, true);
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glPushMatrix ();
+  m_glfcns.glLoadIdentity ();
+
+  Matrix vp = get_viewport_scaled ();
+  m_glfcns.glOrtho (0, vp(2), vp(3), 0, m_xZ1, m_xZ2);
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glPushMatrix ();
+
+  set_clipping (false);
+  set_linewidth (width);
+
+  m_marker_id = make_marker_list (m, size, false);
+  m_filled_marker_id = make_marker_list (m, size, true);
 
 #else
 
-    octave_unused_parameter (m);
-    octave_unused_parameter (size);
-    octave_unused_parameter (width);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (m);
+  octave_unused_parameter (size);
+  octave_unused_parameter (width);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::change_marker (const std::string& m, double size)
-  {
+}
+
+void
+opengl_renderer::change_marker (const std::string& m, double size)
+{
 #if defined (HAVE_OPENGL)
 
-    m_marker_id = make_marker_list (m, size, false);
-    m_filled_marker_id = make_marker_list (m, size, true);
+  m_marker_id = make_marker_list (m, size, false);
+  m_filled_marker_id = make_marker_list (m, size, true);
 
 #else
 
-    octave_unused_parameter (m);
-    octave_unused_parameter (size);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (m);
+  octave_unused_parameter (size);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::end_marker (void)
-  {
+}
+
+void
+opengl_renderer::end_marker (void)
+{
 #if defined (HAVE_OPENGL)
 
-    m_glfcns.glDeleteLists (m_marker_id, 1);
-    m_glfcns.glDeleteLists (m_filled_marker_id, 1);
-
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPopMatrix ();
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPopMatrix ();
-    set_linewidth (0.5f);
+  m_glfcns.glDeleteLists (m_marker_id, 1);
+  m_glfcns.glDeleteLists (m_filled_marker_id, 1);
+
+  m_glfcns.glMatrixMode (GL_MODELVIEW);
+  m_glfcns.glPopMatrix ();
+  m_glfcns.glMatrixMode (GL_PROJECTION);
+  m_glfcns.glPopMatrix ();
+  set_linewidth (0.5f);
 
 #else
 
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::draw_marker (double x, double y, double z,
-                                const Matrix& lc, const Matrix& fc,
-                                const double la, const double fa)
-  {
+}
+
+void
+opengl_renderer::draw_marker (double x, double y, double z,
+                              const Matrix& lc, const Matrix& fc,
+                              const double la, const double fa)
+{
 #if defined (HAVE_OPENGL)
 
-    ColumnVector tmp = m_xform.transform (x, y, z, false);
-
-    m_glfcns.glLoadIdentity ();
-    m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2));
-
-    if (m_filled_marker_id > 0 && fc.numel () > 0)
-      {
-        m_glfcns.glColor4d (fc(0), fc(1), fc(2), fa);
-        set_polygon_offset (true, -1.0);
-        m_glfcns.glCallList (m_filled_marker_id);
-        if (lc.numel () > 0)
-          {
-            m_glfcns.glColor4d (lc(0), lc(1), lc(2), la);
-            m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
-            m_glfcns.glEdgeFlag (GL_TRUE);
-            set_polygon_offset (true, -2.0);
-            m_glfcns.glCallList (m_filled_marker_id);
-            m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
-          }
-        set_polygon_offset (false);
-      }
-    else if (m_marker_id > 0 && lc.numel () > 0)
-      {
-        m_glfcns.glColor4d (lc(0), lc(1), lc(2), la);
-        m_glfcns.glCallList (m_marker_id);
-      }
+  ColumnVector tmp = m_xform.transform (x, y, z, false);
+
+  m_glfcns.glLoadIdentity ();
+  m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2));
+
+  if (m_filled_marker_id > 0 && fc.numel () > 0)
+    {
+      m_glfcns.glColor4d (fc(0), fc(1), fc(2), fa);
+      set_polygon_offset (true, -1.0);
+      m_glfcns.glCallList (m_filled_marker_id);
+      if (lc.numel () > 0)
+        {
+          m_glfcns.glColor4d (lc(0), lc(1), lc(2), la);
+          m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+          m_glfcns.glEdgeFlag (GL_TRUE);
+          set_polygon_offset (true, -2.0);
+          m_glfcns.glCallList (m_filled_marker_id);
+          m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+        }
+      set_polygon_offset (false);
+    }
+  else if (m_marker_id > 0 && lc.numel () > 0)
+    {
+      m_glfcns.glColor4d (lc(0), lc(1), lc(2), la);
+      m_glfcns.glCallList (m_marker_id);
+    }
 
 #else
 
-    octave_unused_parameter (x);
-    octave_unused_parameter (y);
-    octave_unused_parameter (z);
-    octave_unused_parameter (lc);
-    octave_unused_parameter (fc);
-    octave_unused_parameter (la);
-    octave_unused_parameter (fa);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (x);
+  octave_unused_parameter (y);
+  octave_unused_parameter (z);
+  octave_unused_parameter (lc);
+  octave_unused_parameter (fc);
+  octave_unused_parameter (la);
+  octave_unused_parameter (fa);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::init_maxlights (void)
-  {
+}
+
+void
+opengl_renderer::init_maxlights (void)
+{
 #if defined (HAVE_OPENGL)
 
-    // Check actual maximum number of lights possible
-    if (m_max_lights == 0)
-      {
-        GLint max_lights;
-        m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights);
-        m_max_lights = max_lights;
-      }
+  // Check actual maximum number of lights possible
+  if (m_max_lights == 0)
+    {
+      GLint max_lights;
+      m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights);
+      m_max_lights = max_lights;
+    }
 
 #else
 
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  std::string
-  opengl_renderer::get_string (unsigned int id) const
-  {
+}
+
+std::string
+opengl_renderer::get_string (unsigned int id) const
+{
 #if defined (HAVE_OPENGL)
 
-    // This is kind of ugly, but glGetString returns a pointer to GLubyte
-    // and there is no std::string constructor that matches.  Is there a
-    // better way?
-
-    std::ostringstream buf;
-
-    buf << m_glfcns.glGetString (static_cast<GLenum> (id));
-
-    return std::string (buf.str ());
+  // This is kind of ugly, but glGetString returns a pointer to GLubyte
+  // and there is no std::string constructor that matches.  Is there a
+  // better way?
+
+  std::ostringstream buf;
+
+  buf << m_glfcns.glGetString (static_cast<GLenum> (id));
+
+  return std::string (buf.str ());
 
 #else
 
-    octave_unused_parameter (id);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
-    return std::string ();
+  octave_unused_parameter (id);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+  return std::string ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i)
-  {
+}
+
+void
+opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i)
+{
 #if defined (HAVE_OPENGL)
 
-    double x = n(j,i,0);
-    double y = n(j,i,1);
-    double z = n(j,i,2);
-
-    double d = sqrt (x*x + y*y + z*z);
-
-    double dir = 1.0;
-
-    if (bfl_mode > 0)
-      dir = ((x*m_view_vector(0) + y*m_view_vector(1) + z*m_view_vector(2) < 0)
-             ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
-
-    m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d);
+  double x = n(j, i, 0);
+  double y = n(j, i, 1);
+  double z = n(j, i, 2);
+
+  double d = sqrt (x*x + y*y + z*z);
+
+  double dir = 1.0;
+
+  if (bfl_mode > 0)
+    dir = ((x*m_view_vector(0) + y*m_view_vector(1) + z*m_view_vector(2) < 0)
+           ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
+
+  m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d);
 
 #else
 
-    octave_unused_parameter (bfl_mode);
-    octave_unused_parameter (n);
-    octave_unused_parameter (j);
-    octave_unused_parameter (i);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (bfl_mode);
+  octave_unused_parameter (n);
+  octave_unused_parameter (j);
+  octave_unused_parameter (i);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  double
-  opengl_renderer::points_to_pixels (const double val) const
-  {
-    gh_manager& gh_mgr = __get_gh_manager__ ();
-
-    // FIXME: Does making this static cause problems if figure is moved to a
-    //        2nd monitor with a different value for "screenpixelsperinch"?
-    static const double pix_per_pts =
-      gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0;
-
-    double retval = val;
-
-    if (! m_printing)
-      retval *= pix_per_pts;
-
-    return retval;
-  }
-
-  unsigned int
-  opengl_renderer::make_marker_list (const std::string& marker, double size,
-                                     bool filled) const
-  {
+}
+
+double
+opengl_renderer::points_to_pixels (const double val) const
+{
+  gh_manager& gh_mgr = __get_gh_manager__ ();
+
+  // FIXME: Does making this static cause problems if figure is moved to a
+  //        2nd monitor with a different value for "screenpixelsperinch"?
+  static const double pix_per_pts =
+    gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0;
+
+  double retval = val;
+
+  if (! m_printing)
+    retval *= pix_per_pts;
+
+  return retval;
+}
+
+unsigned int
+opengl_renderer::make_marker_list (const std::string& marker, double size,
+                                   bool filled) const
+{
 #if defined (HAVE_OPENGL)
 
-    char c = marker[0];
-
-    if (filled && (c == '+' || c == 'x' || c == '*' || c == '.'
-                   || c == '|' || c == '_'))
-      return 0;
-
-    unsigned int ID = m_glfcns.glGenLists (1);
-
-    // FIXME: See bug #53056 (measure LineWidth in points).
-    double sz = points_to_pixels (size);
-
-    // constants for the * marker
-    const double sqrt2d4 = 0.35355339059327;
-    double tt = sz*sqrt2d4;
-
-    m_glfcns.glNewList (ID, GL_COMPILE);
-
-    switch (marker[0])
+  char c = marker[0];
+
+  if (filled && (c == '+' || c == 'x' || c == '*' || c == '.'
+                 || c == '|' || c == '_'))
+    return 0;
+
+  unsigned int ID = m_glfcns.glGenLists (1);
+
+  // FIXME: See bug #53056 (measure LineWidth in points).
+  double sz = points_to_pixels (size);
+
+  // constants for the * marker
+  const double sqrt2d4 = 0.35355339059327;
+  double tt = sz*sqrt2d4;
+
+  m_glfcns.glNewList (ID, GL_COMPILE);
+
+  switch (marker[0])
+    {
+    case '+':
+      m_glfcns.glBegin (GL_LINES);
+      m_glfcns.glVertex2d (-sz/2, 0);
+      m_glfcns.glVertex2d (sz/2, 0);
+      m_glfcns.glVertex2d (0, -sz/2);
+      m_glfcns.glVertex2d (0, sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case '|':
+      m_glfcns.glBegin (GL_LINES);
+      m_glfcns.glVertex2d (0, -sz/2);
+      m_glfcns.glVertex2d (0, sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case '_':
+      m_glfcns.glBegin (GL_LINES);
+      m_glfcns.glVertex2d (-sz/2, 0);
+      m_glfcns.glVertex2d (sz/2, 0);
+      m_glfcns.glEnd ();
+      break;
+    case 'x':
+      m_glfcns.glBegin (GL_LINES);
+      m_glfcns.glVertex2d (-sz/2, -sz/2);
+      m_glfcns.glVertex2d (sz/2, sz/2);
+      m_glfcns.glVertex2d (-sz/2, sz/2);
+      m_glfcns.glVertex2d (sz/2, -sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case '*':
+      m_glfcns.glBegin (GL_LINES);
+      m_glfcns.glVertex2d (-sz/2, 0);
+      m_glfcns.glVertex2d (sz/2, 0);
+      m_glfcns.glVertex2d (0, -sz/2);
+      m_glfcns.glVertex2d (0, sz/2);
+      m_glfcns.glVertex2d (-tt, -tt);
+      m_glfcns.glVertex2d (+tt, +tt);
+      m_glfcns.glVertex2d (-tt, +tt);
+      m_glfcns.glVertex2d (+tt, -tt);
+      m_glfcns.glEnd ();
+      break;
+    case '.':
       {
-      case '+':
-        m_glfcns.glBegin (GL_LINES);
-        m_glfcns.glVertex2d (-sz/2, 0);
-        m_glfcns.glVertex2d (sz/2, 0);
-        m_glfcns.glVertex2d (0, -sz/2);
-        m_glfcns.glVertex2d (0, sz/2);
-        m_glfcns.glEnd ();
-        break;
-      case '|':
-        m_glfcns.glBegin (GL_LINES);
-        m_glfcns.glVertex2d (0, -sz/2);
-        m_glfcns.glVertex2d (0, sz/2);
-        m_glfcns.glEnd ();
-        break;
-      case '_':
-        m_glfcns.glBegin (GL_LINES);
-        m_glfcns.glVertex2d (-sz/2, 0);
-        m_glfcns.glVertex2d (sz/2, 0);
-        m_glfcns.glEnd ();
-        break;
-      case 'x':
-        m_glfcns.glBegin (GL_LINES);
-        m_glfcns.glVertex2d (-sz/2, -sz/2);
-        m_glfcns.glVertex2d (sz/2, sz/2);
-        m_glfcns.glVertex2d (-sz/2, sz/2);
-        m_glfcns.glVertex2d (sz/2, -sz/2);
-        m_glfcns.glEnd ();
-        break;
-      case '*':
-        m_glfcns.glBegin (GL_LINES);
-        m_glfcns.glVertex2d (-sz/2, 0);
-        m_glfcns.glVertex2d (sz/2, 0);
-        m_glfcns.glVertex2d (0, -sz/2);
-        m_glfcns.glVertex2d (0, sz/2);
-        m_glfcns.glVertex2d (-tt, -tt);
-        m_glfcns.glVertex2d (+tt, +tt);
-        m_glfcns.glVertex2d (-tt, +tt);
-        m_glfcns.glVertex2d (+tt, -tt);
+        // The dot marker is special and is drawn at 1/3rd the specified size
+
+        // Ensure that something is drawn even at very small markersizes
+        if (sz > 0 && sz < 3)
+          sz = 3;
+
+        int div = static_cast<int> (M_PI * sz / 12);
+        if (! (div % 2))
+          div += 1;               // ensure odd number for left/right symmetry
+        div = std::max (div, 3);  // ensure at least a few vertices are drawn
+        double ang_step = M_PI / div;
+
+        m_glfcns.glBegin (GL_POLYGON);
+        for (double ang = 0; ang < 2*M_PI; ang += ang_step)
+          m_glfcns.glVertex2d (sz/6* cos (ang), sz/6*sin (ang));
         m_glfcns.glEnd ();
-        break;
-      case '.':
-        {
-          // The dot marker is special and is drawn at 1/3rd the specified size
-
-          // Ensure that something is drawn even at very small markersizes
-          if (sz > 0 && sz < 3)
-            sz = 3;
-
-          int div = static_cast<int> (M_PI * sz / 12);
-          if (! (div % 2))
-            div += 1;               // ensure odd number for left/right symmetry
-          div = std::max (div, 3);  // ensure at least a few vertices are drawn
-          double ang_step = M_PI / div;
-
-          m_glfcns.glBegin (GL_POLYGON);
-          for (double ang = 0; ang < 2*M_PI; ang += ang_step)
-            m_glfcns.glVertex2d (sz/6*cos (ang), sz/6*sin (ang));
-          m_glfcns.glEnd ();
-        }
-        break;
-      case 's':
+      }
+      break;
+    case 's':
+      m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+      m_glfcns.glVertex2d (-sz/2, -sz/2);
+      m_glfcns.glVertex2d (-sz/2, sz/2);
+      m_glfcns.glVertex2d (sz/2, sz/2);
+      m_glfcns.glVertex2d (sz/2, -sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case 'o':
+      {
+        int div = static_cast<int> (M_PI * sz / 4);
+        if (! (div % 2))
+          div += 1;               // ensure odd number for left/right symmetry
+        div = std::max (div, 5);  // ensure at least a few vertices are drawn
+        double ang_step = M_PI / div;
+
         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        m_glfcns.glVertex2d (-sz/2, -sz/2);
-        m_glfcns.glVertex2d (-sz/2, sz/2);
-        m_glfcns.glVertex2d (sz/2, sz/2);
-        m_glfcns.glVertex2d (sz/2, -sz/2);
-        m_glfcns.glEnd ();
-        break;
-      case 'o':
-        {
-          int div = static_cast<int> (M_PI * sz / 4);
-          if (! (div % 2))
-            div += 1;               // ensure odd number for left/right symmetry
-          div = std::max (div, 5);  // ensure at least a few vertices are drawn
-          double ang_step = M_PI / div;
-
-          m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-          for (double ang = 0; ang < 2*M_PI; ang += ang_step)
-            m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang));
-          m_glfcns.glEnd ();
-        }
-        break;
-      case 'd':
-        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        m_glfcns.glVertex2d (0, -sz/2);
-        m_glfcns.glVertex2d (sz/2, 0);
-        m_glfcns.glVertex2d (0, sz/2);
-        m_glfcns.glVertex2d (-sz/2, 0);
-        m_glfcns.glEnd ();
-        break;
-      case 'v':
-        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        m_glfcns.glVertex2d (0, sz/2);
-        m_glfcns.glVertex2d (sz/2, -sz/2);
-        m_glfcns.glVertex2d (-sz/2, -sz/2);
+        for (double ang = 0; ang < 2*M_PI; ang += ang_step)
+          m_glfcns.glVertex2d (sz/2* cos (ang), sz/2*sin (ang));
         m_glfcns.glEnd ();
-        break;
-      case '^':
-        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        m_glfcns.glVertex2d (0, -sz/2);
-        m_glfcns.glVertex2d (-sz/2, sz/2);
-        m_glfcns.glVertex2d (sz/2, sz/2);
-        m_glfcns.glEnd ();
-        break;
-      case '>':
+      }
+      break;
+    case 'd':
+      m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+      m_glfcns.glVertex2d (0, -sz/2);
+      m_glfcns.glVertex2d (sz/2, 0);
+      m_glfcns.glVertex2d (0, sz/2);
+      m_glfcns.glVertex2d (-sz/2, 0);
+      m_glfcns.glEnd ();
+      break;
+    case 'v':
+      m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+      m_glfcns.glVertex2d (0, sz/2);
+      m_glfcns.glVertex2d (sz/2, -sz/2);
+      m_glfcns.glVertex2d (-sz/2, -sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case '^':
+      m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+      m_glfcns.glVertex2d (0, -sz/2);
+      m_glfcns.glVertex2d (-sz/2, sz/2);
+      m_glfcns.glVertex2d (sz/2, sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case '>':
+      m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+      m_glfcns.glVertex2d (sz/2, 0);
+      m_glfcns.glVertex2d (-sz/2, sz/2);
+      m_glfcns.glVertex2d (-sz/2, -sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case '<':
+      m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+      m_glfcns.glVertex2d (-sz/2, 0);
+      m_glfcns.glVertex2d (sz/2, -sz/2);
+      m_glfcns.glVertex2d (sz/2, sz/2);
+      m_glfcns.glEnd ();
+      break;
+    case 'p':
+      {
+        double ang, r, dr;
+        dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02;
+
         m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        m_glfcns.glVertex2d (sz/2, 0);
-        m_glfcns.glVertex2d (-sz/2, sz/2);
-        m_glfcns.glVertex2d (-sz/2, -sz/2);
-        m_glfcns.glEnd ();
-        break;
-      case '<':
-        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        m_glfcns.glVertex2d (-sz/2, 0);
-        m_glfcns.glVertex2d (sz/2, -sz/2);
-        m_glfcns.glVertex2d (sz/2, sz/2);
+        for (int i = 0; i < 2*5; i++)
+          {
+            ang = (-0.5 + double (i+1) / 5) * M_PI;
+            r = 1.0 - (dr * fmod (double (i+1), 2.0));
+            m_glfcns.glVertex2d (sz/2*r* cos (ang), sz/2*r*sin (ang));
+          }
         m_glfcns.glEnd ();
-        break;
-      case 'p':
-        {
-          double ang, r, dr;
-          dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02;
-
-          m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-          for (int i = 0; i < 2*5; i++)
-            {
-              ang = (-0.5 + double (i+1) / 5) * M_PI;
-              r = 1.0 - (dr * fmod (double (i+1), 2.0));
-              m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
-            }
-          m_glfcns.glEnd ();
-        }
-        break;
-      case 'h':
-        {
-          double ang, r, dr;
-          dr = 1.0 - 0.5/sin (M_PI/3)*1.02;
-
-          m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-          for (int i = 0; i < 2*6; i++)
-            {
-              ang = (0.5 + double (i+1) / 6.0) * M_PI;
-              r = 1.0 - (dr * fmod (double (i+1), 2.0));
-              m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
-            }
-          m_glfcns.glEnd ();
-        }
-        break;
-      default:
-        warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ());
-        break;
       }
-
-    m_glfcns.glEndList ();
-
-    return ID;
+      break;
+    case 'h':
+      {
+        double ang, r, dr;
+        dr = 1.0 - 0.5/sin (M_PI/3)*1.02;
+
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        for (int i = 0; i < 2*6; i++)
+          {
+            ang = (0.5 + double (i+1) / 6.0) * M_PI;
+            r = 1.0 - (dr * fmod (double (i+1), 2.0));
+            m_glfcns.glVertex2d (sz/2*r* cos (ang), sz/2*r*sin (ang));
+          }
+        m_glfcns.glEnd ();
+      }
+      break;
+    default:
+      warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ());
+      break;
+    }
+
+  m_glfcns.glEndList ();
+
+  return ID;
 
 #else
 
-    octave_unused_parameter (marker);
-    octave_unused_parameter (size);
-    octave_unused_parameter (filled);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (marker);
+  octave_unused_parameter (size);
+  octave_unused_parameter (filled);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::text_to_pixels (const std::string& txt,
-                                   uint8NDArray& pixels,
-                                   Matrix& bbox,
-                                   int halign, int valign, double rotation)
-  {
-    m_txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign,
-                                   rotation, m_interpreter);
-  }
-
-  void
-  opengl_renderer::text_to_strlist (const std::string& txt,
-                                    std::list<text_renderer::string>& lst,
-                                    Matrix& bbox,
-                                    int halign, int valign, double rotation)
-  {
-    m_txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign,
-                                    rotation, m_interpreter);
-  }
-
-  Matrix
-  opengl_renderer::render_text (const std::string& txt,
-                                double x, double y, double z,
-                                int halign, int valign, double rotation)
-  {
+}
+
+void
+opengl_renderer::text_to_pixels (const std::string& txt,
+                                 uint8NDArray& pixels,
+                                 Matrix& bbox,
+                                 int halign, int valign, double rotation)
+{
+  m_txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign,
+                                 rotation, m_interpreter);
+}
+
+void
+opengl_renderer::text_to_strlist (const std::string& txt,
+                                  std::list<text_renderer::string>& lst,
+                                  Matrix& bbox,
+                                  int halign, int valign, double rotation)
+{
+  m_txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign,
+                                  rotation, m_interpreter);
+}
+
+Matrix
+opengl_renderer::render_text (const std::string& txt,
+                              double x, double y, double z,
+                              int halign, int valign, double rotation)
+{
 #if defined (HAVE_OPENGL)
 
-    Matrix bbox (1, 4, 0.0);
-
-    if (txt.empty ())
-      return bbox;
-
-    if (m_txt_renderer.ok ())
-      {
-        uint8NDArray pixels;
-        text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
-
-        render_text (pixels, bbox, x, y, z, rotation);
-      }
-
+  Matrix bbox (1, 4, 0.0);
+
+  if (txt.empty ())
     return bbox;
 
+  if (m_txt_renderer.ok ())
+    {
+      uint8NDArray pixels;
+      text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
+
+      render_text (pixels, bbox, x, y, z, rotation);
+    }
+
+  return bbox;
+
 #else
 
-    octave_unused_parameter (txt);
-    octave_unused_parameter (x);
-    octave_unused_parameter (y);
-    octave_unused_parameter (z);
-    octave_unused_parameter (halign);
-    octave_unused_parameter (valign);
-    octave_unused_parameter (rotation);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (txt);
+  octave_unused_parameter (x);
+  octave_unused_parameter (y);
+  octave_unused_parameter (z);
+  octave_unused_parameter (halign);
+  octave_unused_parameter (valign);
+  octave_unused_parameter (rotation);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
-
-  void
-  opengl_renderer::render_text (uint8NDArray pixels, Matrix bbox,
-                                double x, double y, double z, double rotation)
-  {
+}
+
+void
+opengl_renderer::render_text (uint8NDArray pixels, Matrix bbox,
+                              double x, double y, double z, double rotation)
+{
 #if defined (HAVE_OPENGL)
 
-    // Transform data coordinates to screen pixel ortho coordinates
-    ColumnVector pixpos = get_transform ().transform (x, y, z, false);
-    Matrix xdata(1, 2, bbox(0) / m_devpixratio);
-    xdata(1) += (bbox(2) - 1) / m_devpixratio;
-    Matrix ydata(1, 2, -bbox(1) / m_devpixratio);
-    ydata(1) -= (bbox(3) - 1) / m_devpixratio;
-
-    bool blend = m_glfcns.glIsEnabled (GL_BLEND);
-    m_glfcns.glEnable (GL_BLEND);
-    m_glfcns.glEnable (GL_ALPHA_TEST);
-
-    set_ortho_coordinates ();
-
-    // Translate coordinates so that the text anchor is (0,0)
-    m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
-
-    m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
-
-    // Permute pixels returned by freetype
-    Array<octave_idx_type> perm (dim_vector (3, 1));
-    perm(0) = 2;
-    perm(1) = 1;
-    perm(2) = 0;
-    draw_texture_image (pixels.permute (perm),
-                        xdata, ydata, true);
-
-    restore_previous_coordinates ();
-
-    m_glfcns.glDisable (GL_ALPHA_TEST);
-
-    if (! blend)
-      m_glfcns.glDisable (GL_BLEND);
+  // Transform data coordinates to screen pixel ortho coordinates
+  ColumnVector pixpos = get_transform ().transform (x, y, z, false);
+  Matrix xdata(1, 2, bbox(0) / m_devpixratio);
+  xdata(1) += (bbox(2) - 1) / m_devpixratio;
+  Matrix ydata(1, 2, -bbox(1) / m_devpixratio);
+  ydata(1) -= (bbox(3) - 1) / m_devpixratio;
+
+  bool blend = m_glfcns.glIsEnabled (GL_BLEND);
+  m_glfcns.glEnable (GL_BLEND);
+  m_glfcns.glEnable (GL_ALPHA_TEST);
+
+  set_ortho_coordinates ();
+
+  // Translate coordinates so that the text anchor is (0,0)
+  m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
+
+  m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
+
+  // Permute pixels returned by freetype
+  Array<octave_idx_type> perm (dim_vector (3, 1));
+  perm(0) = 2;
+  perm(1) = 1;
+  perm(2) = 0;
+  draw_texture_image (pixels.permute (perm),
+                      xdata, ydata, true);
+
+  restore_previous_coordinates ();
+
+  m_glfcns.glDisable (GL_ALPHA_TEST);
+
+  if (! blend)
+    m_glfcns.glDisable (GL_BLEND);
 
 #else
 
-    octave_unused_parameter (pixels);
-    octave_unused_parameter (bbox);
-    octave_unused_parameter (x);
-    octave_unused_parameter (y);
-    octave_unused_parameter (z);
-    octave_unused_parameter (rotation);
-
-    // This shouldn't happen because construction of opengl_renderer
-    // objects is supposed to be impossible if OpenGL is not available.
-
-    panic_impossible ();
+  octave_unused_parameter (pixels);
+  octave_unused_parameter (bbox);
+  octave_unused_parameter (x);
+  octave_unused_parameter (y);
+  octave_unused_parameter (z);
+  octave_unused_parameter (rotation);
+
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
 
 #endif
-  }
+}
 
 OCTAVE_END_NAMESPACE(octave)