changeset 7874:e3a502930e2a

eliminate src/graphics subdirectory
author John W. Eaton <jwe@octave.org>
date Thu, 05 Jun 2008 18:00:37 -0400
parents 02b590f46a29
children bff8dbc1be11
files ChangeLog Makeconf.in configure.in src/ChangeLog src/DLD-FUNCTIONS/fltk_backend.cc src/Makefile.in src/gl-render.cc src/gl-render.h src/graphics/ChangeLog src/graphics/Makefile.in src/graphics/Makerules.in src/graphics/fltk_backend/Makefile.in src/graphics/fltk_backend/fltk_backend.cc src/graphics/opengl/Makefile.in src/graphics/opengl/gl-render.cc src/graphics/opengl/gl-render.h
diffstat 16 files changed, 3962 insertions(+), 4322 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Jun 05 20:42:14 2008 +0200
+++ b/ChangeLog	Thu Jun 05 18:00:37 2008 -0400
@@ -1,3 +1,12 @@
+2008-06-05  John W. Eaton  <jwe@octave.org>
+
+	* configure.in: Add FT2_CFLAGS to XTRA_CXXFLAGS, not CXXFLAGS.
+	Don't add FT2_LIBS to LIBS.
+	Don't generate src/graphics/Makefile, src/graphics/Makerules,
+	src/graphics/opengl/Makefile, or src/graphics/fltk_backend/Makefile.
+
+	* Makeconf.in (FT2_LIBS, GRAPHICS_LIBS): Substitute here.
+
 2008-06-04  Shai Ayal  <shaiay@users.sourceforge.net>
 
 	* configure.in: Use AC_TRY_LINK in FTGL test.
--- a/Makeconf.in	Thu Jun 05 20:42:14 2008 +0200
+++ b/Makeconf.in	Thu Jun 05 18:00:37 2008 -0400
@@ -215,6 +215,8 @@
 LIBREADLINE = @LIBREADLINE@
 TERMLIBS = @TERMLIBS@
 
+FT2_LIBS = @FT2_LIBS@
+GRAPHICS_LIBS = @GRAPHICS_LIBS@
 QHULL_LIBS = @QHULL_LIBS@
 REGEX_LIBS = @REGEX_LIBS@
 BLAS_LIBS = @BLAS_LIBS@
--- a/configure.in	Thu Jun 05 20:42:14 2008 +0200
+++ b/configure.in	Thu Jun 05 18:00:37 2008 -0400
@@ -664,11 +664,12 @@
           OPENGL_LIBS="$OPENGL_LIBS $FT2_LIBS -lftgl"
 	  LIBS="$save_LIBS"
           AC_DEFINE(HAVE_FTGL, 1, [Define to 1 if FTGL is present])
-	  AC_MSG_RESULT(yes)],[
+	  AC_MSG_RESULT(yes)
+	  XTRA_CXXFLAGS="$XTRA_CXXFLAGS $FT2_CFLAGS"],[
+	  AC_MSG_RESULT(no)])],[
+          warn_graphics="FTGL library not found. Native renderer will not have on-screen text"])
 	  LIBS="$save_LIBS"
 	  CXXFLAGS="$save_CXXFLAGS"
-	  AC_MSG_RESULT(no)])],[
-          warn_graphics="FTGL library not found. Native renderer will not have on-screen text"])
     AC_LANG_POP(C++)
   fi
 fi
@@ -1961,9 +1962,7 @@
   libcruft/qrupdate/Makefile
   libcruft/ranlib/Makefile libcruft/slatec-fn/Makefile
   libcruft/slatec-err/Makefile libcruft/villad/Makefile
-  libcruft/blas-xtra/Makefile libcruft/lapack-xtra/Makefile
-  src/graphics/Makefile src/graphics/Makerules
-  src/graphics/opengl/Makefile src/graphics/fltk_backend/Makefile])
+  libcruft/blas-xtra/Makefile libcruft/lapack-xtra/Makefile])
 AC_OUTPUT
 
 AC_CONFIG_COMMANDS([default-1],[[chmod +x install-octave]],[[]])
--- a/src/ChangeLog	Thu Jun 05 20:42:14 2008 +0200
+++ b/src/ChangeLog	Thu Jun 05 18:00:37 2008 -0400
@@ -1,5 +1,21 @@
 2008-06-05  John W. Eaton  <jwe@octave.org>
 
+	* gl-render.cc (opengl_renderer::draw): Correctly avoid shadow
+	warnings from gcc for xmin, xmax, ymin, ymax, zmin, and zmax.
+
+	* graphics/ChangeLog, graphics/Makefile.in, graphics/Makerules.in,
+	graphics/fltk_backend/Makefile.in, graphics/opengl/Makefile.in:
+	Delete.
+
+	* gl-render.cc, gl-render.h: Move here from src/graphics/opengl.
+	* Makefile.in: Add them to the appropriate lists.
+	(octave$(EXEEXT)): Also link with $(OPENGL_LIBS)
+
+	* fltk_backend.cc: Move here from src/graphics/fltk_backend.
+	* Makefile.in (DLD_XSRC): Add it to the list
+	(fltk_backend.oct): Include special rules for linking with
+	$(GRAPHICS_LIBS) and $(FT2_LIBS).
+
 	* dynamic-ld.cc (octave_dynamic_loader::mex_mangler,
 	octave_dynamic_loader::mex_uscore_mangler,
 	octave_dynamic_loader::mex_f77_mangler): New functions.
@@ -12,6 +28,192 @@
 	* graphics.cc (properties::update_normals): Style fixes.
 	* graphics.h.in: Style fixes.
 
+	ChangeLog entries for gl-render.h, gl-render.cc, and
+	fltk_backend.cc before the move:
+
+	2008-06-05  John W. Eaton  <jwe@octave.org>
+
+	* opengl/gl-render.cc (xmin): New static function.
+
+	* opengl/gl-render.h (opengl_renderer): Style fixes.
+	* fltk_backend/fltk_backend.cc: Style fixes.
+
+	2008-04-26  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.h (opengl_renderer::draw(hggroup)): New method.
+	* opengl/gl-render.cc (opengl_renderer::draw(hggroup)): Likewise.
+	(opengl_renderer::draw(graphics_object)): Call it.
+
+	2008-03-17  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (plot_window::resize,
+	plot_window::draw): make canvas the size of figure.position
+
+	2008-03-09  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (plot_window::handle): add zoom
+	stack
+
+	2008-03-06  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (plot_window::handle): Add handling
+	of the 'a' and 'g' keys
+	(plot_window: toggle_grid): New helper function
+	(plot_window): Add new togglegrid button
+
+	2008-03-01  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (OpenGL_fltk::draw_overlay,
+	OpenGL_fltk::zoom, OpenGL_fltk::set_zoom_box): Added to support
+	zoom box
+	(plot_window::handle): Added zoom box code, B-3 now does autoscale
+	(plot_window::axis_auto): New utility function to call axis("auto")
+	(plot_window::button_press): "A" button now does autoscale
+
+	* opengl/gl-render.cc (make_marker_list): Add the "+x*.^v><"
+	markers
+
+	2008-02-27  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.cc (opengl_renderer::draw(patch),
+	opengl_renderer::draw(surface)): Adapt to type change of facealpha and
+	edgealpha, using double_radio_property class.
+
+	2008-02-26  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.h: Make sure windows.h is included before OpenGL
+	headers.
+	* fltk_backend/Makefile.in (FLTK_EXTRA_CXXFLAGS): Use $(srcdir) for
+	includesion of OpenGL renderer headers.
+	(Makeconf): Move inclusion of Makeconf later on, to avoid freezing
+	MinGW make.
+
+	* fltk_backend/Makefile.in (GRAPHICS_CFLAGS): New substituted
+	variable.
+
+	2008-02-25  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (class plot_window): Many changes
+	to use figure::properties instead of figure handle to reference
+	the figure
+	(class figure_manager): ditto
+	(__fltk_redraw__): moved most of functionality into the
+	figure_manager class
+	(plot_window::pixel2pos): Modified to use axes::pixel2coord
+	(plot_window::pixel2staus): Modified to use pixel2pos
+	(plot_window::handle): Added zoom with mouse 
+
+	2008-02-24  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (OpenGL_fltk::Draw): removed double
+	buffer switch
+	(OpenGL_fltk::setup_viewport): removed call to glOrtho --
+	gl-render takes care of all the transformations
+
+	2008-02-23  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/fltk_backend.cc (plot_window::mark_modifed): mark
+	the whole window as damaged (otherwise changing figure.postion
+	does not have immediate effect)
+	(plot_window::draw): New function, checks for window size 
+	(__fltk_maxtime__): New DEFUN to allow tweaking of fltk timeout
+	(__fltk_redraw__): Use fltk_maxtime as timeout
+
+	2008-02-21  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.cc (opengl_renderer::patch_tesselator::combine):
+	Protect against NULL vertex data.
+
+	* opengl/gl-render.cc (opengl_renderer::draw(patch)): Add marker
+	rendering of patch objects.
+
+	2008-02-21  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* opengl/gl-render.cc: remove OpenGL includes
+	* opengl/gl-render.h: add OpenGL includes
+	* fltk_backend/fltk_backend.cc: remove OpenGL includes
+	(__fltk_redraw__): put figure handle into the figure's
+	__plot_stream__ property for later
+	(fltk_backend::close_figure): use argument as figure handle to
+	close
+
+	2008-02-20  Shai Ayal  <shaiay@users.sourceforge.net>
+
+	* fltk_backend/Makefile.in: initial import
+
+	* fltk_backend/fltk_backend.cc: initial import
+	
+
+	2008-02-20  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.h (opengl_renderer::draw(patch)): New method to
+	render patch objects.
+	(class opengl_renderer::patch_tesselator): Forward declaration.
+	* opengl/gl-render.cc (opengl_texture::create): Use RGB data format
+	instead of RGBA.
+	(class opengl_tesselator): New classes to abstract GLU tessellation
+	process.
+	(class opengl_renderer::patch_tesselator): New class to render opaque
+	patch objects.
+	(class vertex_data): New class to hold vertex data during tessellation
+	of patch objects.
+	(opengl_renderer::draw(patch)): New method to render patch objects (no
+	transparency, no border, no marker yet).
+	(opengl_renderer::draw(graphics_object)): Dispatch to it.
+
+	* opengl/gl-render.cc (opengl_renderer::draw(patch)): Use patch color
+	data and support face/vertex single color specification.
+
+	* opengl/gl-render.cc (opengl_tesselator::begin_polygon): Set
+	tessellation property also for non-filled polygons.
+	(opengl_renderer::patch_tesselator::vertex): Protect against empty
+	color matrices.
+	(opengl_renderer::draw(patch)): Render patch border (no transparency
+	yet).
+
+	2008-02-19  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.cc (opengl_texture::texture_rep::tex_coord,
+	opengl_texture::tex_coord): New wrapper around glTexCoord2d.
+	(opengl_renderer::draw(surface)): Use it for texturemap
+	implementation.
+	(opengl_renderer::draw(surface)): Fix indexing bug when creating clip
+	matrix.
+	(opengl_texture::operator=): Add assignment operator.
+	(opengl_texture::create): New static opengl_texture creator.
+	(opengl_texture::is_valid): New accessor.
+
+	2008-02-18  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.cc (class opengl_texture): New class to wrap
+	texture operations in OpenGL.
+
+	2008-02-17  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.cc (opengl_renderer::draw(surface)): Set material
+	color when rendering surface facets.
+
+	* opengl/gl-render.cc (opengl_renderer::draw(surface)): Add rendering
+	of mesh and markers.
+
+	2008-02-16  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.cc (opengl_renderer::draw(figure)): Initialize the
+	OpenGL context correctly.
+	(opengl_renderer::draw(surface)): Add missing glEnd call.
+
+	2008-02-14  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* opengl/gl-render.h opengl/gl-render.cc: Add rendering
+	interface for surface objects (actual implement still
+	missing).
+
+	2008-02-14  Michael Goffioul  <michael.goffioul@gmail.com>
+
+	* Makefile.in Makerules.in: Initial import
+	* opengl/Makefile.in: Likewise.
+	* opengl/gl-render.h opengl/gl-render.cc: Likewise.
+
 2008-06-04  Shai Ayal  <shaiay@users.sourceforge.net>
 
 	* graphics.cc (axes::properties::set_defaults): Preserve position
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DLD-FUNCTIONS/fltk_backend.cc	Thu Jun 05 18:00:37 2008 -0400
@@ -0,0 +1,811 @@
+/*
+
+Copyright (C) 2007, 2008 Shai Ayal
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+
+To initialize:
+
+  input_event_hook ("__fltk_redraw__");
+  __init_fltk__ ();
+  set (gcf (), "__backend__", "fltk");
+  plot (randn (1e3, 1));
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <map>
+#include <set>
+#include <sstream>
+#include <iostream>
+
+#include "gl-render.h"
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Output.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Gl_Window.H>
+#include <FL/fl_ask.H>
+#include <FL/fl_draw.H>
+#include <FL/gl.h>
+
+#include "oct.h"
+#include "parse.h"
+#include "graphics.h"
+
+#define FLTK_BACKEND_NAME "fltk"
+
+const char* help_text = "\
+Keyboard Shortcuts\n\
+a - autoscale\n\
+g - toggle grid\n\
+\n\
+Mouse\n\
+left drag - zoom\n\
+right click - unzoom\n\
+double click - copy coordinates to clipboard\
+";
+
+class OpenGL_fltk : public Fl_Gl_Window
+{
+public:
+  OpenGL_fltk (int xx, int yy, int ww, int hh, double num)
+    : Fl_Gl_Window (xx, yy, ww, hh, 0), number (num), in_zoom (false)
+  {
+    // ask for double buffering and a depth buffer
+    mode (FL_DEPTH | FL_DOUBLE);
+  }
+
+  ~OpenGL_fltk (void) { }
+
+  void zoom (bool z) { in_zoom = z; }
+  bool zoom (void) { return in_zoom; }
+  void set_zoom_box (const Matrix& zb) { zoom_box = zb; }
+
+private:
+  double number;
+  opengl_renderer renderer;
+  bool in_zoom;
+
+  // (x1,y1,x2,y2)
+  Matrix zoom_box;
+
+  void setup_viewport (int _w, int _h)
+  {
+    glMatrixMode (GL_PROJECTION);
+    glLoadIdentity ();
+    glViewport (0, 0, _w, _h);
+  }
+
+  void draw (void)
+  {
+    if (!valid ())
+      {
+	valid (1);
+	setup_viewport (w (), h ());
+      }
+
+    renderer.draw (gh_manager::lookup (number));
+  }
+
+  void resize (int _x,int _y,int _w,int _h)
+  {
+    Fl_Gl_Window::resize (_x, _y, _w, _h);
+    setup_viewport (_w, _h);
+    redraw ();
+  }
+
+  void draw_overlay (void)
+  {
+    if (!in_zoom)
+      return;
+
+    if (!valid())
+      {
+	valid(1);
+	setup_viewport (w (), h ());
+      }
+
+    glPushMatrix ();
+
+    glMatrixMode (GL_MODELVIEW);
+    glLoadIdentity ();
+
+    glMatrixMode (GL_PROJECTION);
+    glLoadIdentity ();
+    gluOrtho2D (0.0, w (), 0.0, h ());
+
+    glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
+    glDisable (GL_DEPTH_TEST);
+
+    glLineWidth (1);
+    glBegin (GL_LINE_STRIP);
+    gl_color(0);
+    glVertex2d (zoom_box(0), h () - zoom_box(1));
+    glVertex2d (zoom_box(0), h () - zoom_box(3));
+    glVertex2d (zoom_box(2), h () - zoom_box(3));
+    glVertex2d (zoom_box(2), h () - zoom_box(1));
+    glVertex2d (zoom_box(0), h () - zoom_box(1));
+    glEnd ();
+
+    glPopAttrib ();
+    glPopMatrix ();
+  }
+
+  int handle (int event)
+  {
+    int retval = Fl_Gl_Window::handle (event);
+
+    switch (event)
+      {
+      case FL_ENTER:
+	window ()->cursor (FL_CURSOR_CROSS);
+	return 1;
+
+      case FL_LEAVE:
+	window ()->cursor (FL_CURSOR_DEFAULT);
+	return 1;
+      }
+
+    return retval;
+  }
+};
+
+class plot_window : public Fl_Window
+{
+public:
+  plot_window (int _x, int _y, int _w, int _h, figure::properties& _fp)
+    : Fl_Window (_x, _y, _w, _h, "octave"), fp (_fp)
+  {
+    callback (window_close, static_cast<void*> (this));
+
+    begin ();
+    {
+      canvas = new
+	OpenGL_fltk (0, 0, _w , _h - status_h, number ());
+
+      autoscale = new
+	Fl_Button (0,
+		   _h - status_h,
+		   status_h,
+		   status_h,
+		   "A");
+      autoscale->callback (button_callback, static_cast<void*> (this));
+
+      togglegrid = new
+	Fl_Button (status_h,
+		   _h - status_h,
+		   status_h,
+		   status_h,
+		   "G");
+      togglegrid->callback (button_callback, static_cast<void*> (this));
+
+      help = new
+	Fl_Button (2*status_h,
+		   _h - status_h,
+		   status_h,
+		   status_h,
+		   "H");
+      help->callback (button_callback, static_cast<void*> (this));
+
+      status = new
+	Fl_Output (3*status_h,
+		   _h - status_h,
+		   _w > 2*status_h ? _w - status_h : 0,
+		   status_h, "");
+
+      status->textcolor (FL_BLACK);
+      status->color (FL_GRAY);
+      status->textfont (FL_COURIER);
+      status->textsize (10);
+      status->box (FL_ENGRAVED_BOX);
+
+      // This allows us to have a valid OpenGL context right away
+      canvas->mode (FL_DEPTH | FL_DOUBLE );
+      show ();
+      canvas->show ();
+      canvas->make_current ();
+    }
+    end ();
+
+    status->show ();
+    autoscale->show ();
+    togglegrid->show ();
+
+    resizable (canvas);
+    size_range (4*status_h, 2*status_h);
+
+    std::stringstream name;
+    name << "octave: figure " << number ();
+    label (name.str ().c_str ());
+  }
+
+  ~plot_window (void)
+  {
+    canvas->hide ();
+    status->hide ();
+    this->hide ();
+    delete canvas;
+    delete status;
+  }
+
+  // FIXME -- this could change
+  double number (void) { return fp.get___myhandle__ ().value (); }
+
+  void mark_modified (void)
+  {
+    damage (FL_DAMAGE_ALL);
+    canvas->damage (FL_DAMAGE_ALL);
+  }
+
+private:
+  // figure properties
+  figure::properties& fp;
+
+  // status area height
+  static const int status_h = 20;
+
+  // window callback
+  static void window_close (Fl_Widget*, void* data)
+  {
+    octave_value_list args;
+    args(0) = static_cast<plot_window*> (data)->number ();
+    feval ("close", args);
+  }
+
+  // button callbacks
+  static void button_callback (Fl_Widget* ww, void* data)
+  {
+    static_cast<plot_window*> (data)->button_press (ww);
+  }
+
+  void button_press (Fl_Widget* widg)
+  {
+    if (widg == autoscale)
+      axis_auto ();
+
+    if (widg == togglegrid)
+      toggle_grid ();
+
+    if (widg == help)
+      fl_message (help_text);
+  }
+
+  OpenGL_fltk* canvas;
+  Fl_Button* autoscale;
+  Fl_Button* togglegrid;
+  Fl_Button* help;
+  Fl_Output* status;
+
+  void axis_auto (void)
+  {
+    octave_value_list args;
+    args(0) = "auto";
+    feval ("axis",args);
+    mark_modified ();
+  }
+
+  void toggle_grid (void)
+  {
+    feval ("grid");
+    mark_modified ();
+  }
+
+  void pixel2pos (int px, int py, double& xx, double& yy) const
+  {
+    graphics_object ax = gh_manager::get_object (fp.get_currentaxes ());
+
+    if (ax && ax.isa ("axes"))
+      {
+	axes::properties& ap =
+	  dynamic_cast<axes::properties&> (ax.get_properties ());
+	ColumnVector pp = ap.pixel2coord (px, py);
+	xx = pp(0);
+	yy = pp(1);
+      }
+  }
+
+  graphics_handle pixel2axes (int /* px */, int /* py */)
+  {
+    Matrix kids = fp.get_children ();
+
+    for (octave_idx_type n = 0; n < kids.numel (); n++)
+      {
+	graphics_object ax = gh_manager::get_object (kids (n));
+	if (ax && ax.isa ("axes"))
+	  {
+#if 0
+	     axes::properties& ap =
+	       dynamic_cast<axes::properties&> (ax.get_properties ());
+
+	     // std::cout << "\npixpos="<<pixpos<<"(px,py)=("<<px<<","<<py<<")\n";
+	     if (px >= pixpos(0) && px <= pixpos(2)
+		 && py >= pixpos(1) && py <= pixpos(3))
+	       return ap.get___myhandle__ ();
+#endif
+	  }
+      }
+
+    return graphics_handle ();
+  }
+
+  void pixel2status (int px0, int py0, int px1 = -1, int py1 = -1)
+  {
+    double x0, y0, x1, y1;
+    std::stringstream cbuf;
+
+    pixel2pos (px0, py0, x0, y0);
+    cbuf << "[" << x0 << ", " << y0 << "]";
+    if (px1 >= 0)
+      {
+	pixel2pos (px1, py1, x1, y1);
+	cbuf << " -> ["<< x1 << ", " << y1 << "]";
+      }
+
+    status->value (cbuf.str ().c_str ());
+    status->redraw ();
+  }
+
+  void resize (int _x,int _y,int _w,int _h)
+  {
+    Fl_Window::resize (_x, _y, _w, _h);
+
+    Matrix pos (1,4,0);
+    pos(0) = _x;
+    pos(1) = _y;
+    pos(2) = _w;
+    pos(3) = _h - status_h;
+
+    fp.set_position (pos);
+  }
+
+  void draw (void)
+  {
+    Matrix pos = fp.get_position ().matrix_value ();
+    Fl_Window::resize (pos(0), pos(1) , pos(2), pos(3) + status_h);
+
+    return Fl_Window::draw ();
+  }
+
+  int handle (int event)
+  {
+    static int px0,py0;
+    static graphics_handle h0 = graphics_handle ();
+
+    int retval = Fl_Window::handle (event);
+
+    // we only handle events which are in the canvas area
+    if (Fl::event_y () >= h() - status_h)
+      return retval;
+
+    switch (event)
+      {
+      case FL_KEYDOWN:
+	switch(Fl::event_key ())
+	  {
+	  case 'a':
+	  case 'A':
+	    axis_auto ();
+	    break;
+
+	  case 'g':
+	  case 'G':
+	    toggle_grid ();
+	    break;
+	  }
+	break;
+
+      case FL_MOVE:
+	pixel2status (Fl::event_x (), Fl::event_y ());
+	break;
+
+      case FL_PUSH:
+	if (Fl::event_button () == 1)
+	  {
+	    px0 = Fl::event_x ();
+	    py0 = Fl::event_y ();
+	    h0 = pixel2axes (Fl::event_x (), Fl::event_y ());
+	    return 1;
+	  }
+	break;
+
+      case FL_DRAG:
+	pixel2status (px0, py0, Fl::event_x (), Fl::event_y ());
+	if (Fl::event_button () == 1)
+	  {
+	    canvas->zoom (true);
+	    Matrix zoom_box (1,4,0);
+	    zoom_box (0) = px0;
+	    zoom_box (1) = py0;
+	    zoom_box (2) =  Fl::event_x ();
+	    zoom_box (3) =  Fl::event_y ();
+	    canvas->set_zoom_box (zoom_box);
+	    canvas->redraw_overlay ();
+	    return 1;
+	  }
+	break;
+
+      case FL_RELEASE:
+	if (Fl::event_button () == 1)
+	  {
+	    // end of drag -- zoom
+	    if (canvas->zoom ())
+	      {
+		canvas->zoom (false);
+		double x0,y0,x1,y1;
+		graphics_object ax =
+		  gh_manager::get_object (fp.get_currentaxes ());
+		if (ax && ax.isa ("axes"))
+		  {
+		    axes::properties& ap =
+		      dynamic_cast<axes::properties&> (ax.get_properties ());
+		    pixel2pos (px0, py0, x0, y0);
+		    pixel2pos (Fl::event_x (), Fl::event_y (), x1, y1);
+		    Matrix xl (1,2,0);
+		    Matrix yl (1,2,0);
+		    if (x0 < x1)
+		      {
+			xl(0) = x0;
+			xl(1) = x1;
+		      }
+		    else
+		      {
+			xl(0) = x1;
+			xl(1) = x0;
+		      }
+
+		    if (y0 < y1)
+		      {
+			yl(0) = y0;
+			yl(1) = y1;
+		      }
+		    else
+		      {
+			yl(0) = y1;
+			yl(1) = y0;
+		      }
+		    ap.zoom (xl, yl);
+		    mark_modified ();
+		  }
+	      }
+	    // one click -- select axes
+	    else if ( Fl::event_clicks () == 0)
+	      {
+		std::cout << "ca="<< h0.value ()<<"\n";
+		if (h0.ok ())
+		  fp.set_currentaxes (h0.value());
+		return 1;
+	      }
+	  }
+	else if (Fl::event_button () == 3)
+	  {
+	    graphics_object ax =
+	      gh_manager::get_object (fp.get_currentaxes ());
+	    if (ax && ax.isa ("axes"))
+	      {
+		axes::properties& ap =
+		  dynamic_cast<axes::properties&> (ax.get_properties ());
+		ap.unzoom ();
+		mark_modified ();
+	      }
+	  }
+	break;
+      }
+
+    return retval;
+  }
+};
+
+class figure_manager
+{
+public:
+
+  static figure_manager& Instance (void)
+  {
+    static figure_manager fm;
+    return fm;
+  }
+
+  ~figure_manager (void)
+  {
+    close_all ();
+  }
+
+  void close_all (void)
+  {
+    wm_iterator win;
+    for (win = windows.begin (); win != windows.end (); win++)
+      delete (*win).second;
+    windows.clear ();
+  }
+
+  void new_window (figure::properties& fp)
+  {
+    int x, y, w, h;
+
+    int idx = figprops2idx (fp);
+    if (idx >= 0 && windows.find (idx) == windows.end ())
+      {
+	default_size(x,y,w,h);
+	idx2figprops (curr_index , fp);
+	windows[curr_index++] = new plot_window (x, y, w, h, fp);
+      }
+  }
+
+  void delete_window (int idx)
+  {
+    wm_iterator win;
+    if ((win = windows.find (idx)) != windows.end ())
+      {
+	delete (*win).second;
+	windows.erase (win);
+      }
+  }
+
+  void delete_window (std::string idx_str)
+  {
+    delete_window (str2idx (idx_str));
+  }
+
+  void mark_modified (int idx)
+  {
+    wm_iterator win;
+    if ((win=windows.find (idx)) != windows.end ())
+      {
+	(*win).second->mark_modified ();
+      }
+  }
+
+  void mark_modified (const graphics_handle& gh)
+  {
+    mark_modified (hnd2idx (gh));
+  }
+
+  Matrix get_size (int idx)
+  {
+    Matrix sz (1, 2, 0.0);
+
+    wm_iterator win;
+    if ((win = windows.find (idx)) != windows.end ())
+      {
+	sz(0) = (*win).second->w ();
+	sz(1) = (*win).second->h ();
+      }
+
+    return sz;
+  }
+
+  Matrix get_size (const graphics_handle& gh)
+  {
+    return get_size (hnd2idx (gh));
+  }
+
+private:
+  figure_manager (void) { }
+  figure_manager (const figure_manager&) { }
+  figure_manager& operator = (const figure_manager&) { return *this; }
+  // singelton -- hide all of the above
+
+  static int curr_index;
+  typedef std::map<int, plot_window*> window_map;
+  typedef window_map::iterator wm_iterator;;
+  window_map windows;
+
+  static std::string fltk_idx_header;
+
+  void default_size (int& x, int& y, int& w, int& h)
+  {
+    x = 10;
+    y = 10;
+    w = 400;
+    h = 300;
+  }
+
+  int str2idx (const caseless_str clstr)
+  {
+    int ind;
+    if (clstr.find (fltk_idx_header,0) == 0)
+      {
+	std::istringstream istr (clstr.substr (fltk_idx_header.size ()));
+	if (istr >> ind)
+	  return ind;
+      }
+    error ("fltk_backend: could not recognize fltk index");
+    return -1;
+  }
+
+  void idx2figprops (int idx, figure::properties& fp)
+  {
+    std::ostringstream ind_str;
+    ind_str << fltk_idx_header << idx;
+    fp.set___plot_stream__ (ind_str.str ());
+  }
+
+  int figprops2idx (const figure::properties& fp)
+  {
+    if (fp.get___backend__ () == FLTK_BACKEND_NAME)
+      {
+	octave_value ps = fp.get___plot_stream__ ();
+	if (ps.is_string ())
+	  return str2idx (ps.string_value ());
+	else
+	  return 0;
+      }
+    error ("fltk_backend:: figure is not fltk");
+    return -1;
+  }
+
+  int hnd2idx (const graphics_handle& fh)
+  {
+    return hnd2idx (fh.value ());
+  }
+
+  int hnd2idx (const double h)
+  {
+    graphics_object fobj = gh_manager::get_object (h);
+    if (fobj &&  fobj.isa ("figure"))
+      {
+	figure::properties& fp =
+	  dynamic_cast<figure::properties&> (fobj.get_properties ());
+	return figprops2idx (fp);
+      }
+    error ("fltk_backend:: not a figure");
+    return -1;
+  }
+};
+
+std::string figure_manager::fltk_idx_header="fltk index=";
+int figure_manager::curr_index = 1;
+
+class fltk_backend : public base_graphics_backend
+{
+public:
+  fltk_backend (void)
+    : base_graphics_backend (FLTK_BACKEND_NAME) { }
+
+  ~fltk_backend (void) { }
+
+  bool is_valid (void) const { return true; }
+
+  void close_figure (const octave_value& ov) const
+  {
+    if (ov.is_string ())
+      figure_manager::Instance ().delete_window (ov.string_value ());
+  }
+
+  void redraw_figure (const graphics_handle& fh) const
+  {
+    figure_manager::Instance ().mark_modified (fh);
+  }
+
+  void print_figure (const graphics_handle& /*fh*/,
+		     const std::string& /*term*/,
+		     const std::string& /*file*/, bool /*mono*/,
+		     const std::string& /*debug_file*/) const { }
+
+  Matrix get_canvas_size (const graphics_handle& fh) const
+  {
+    return figure_manager::Instance ().get_size (fh);
+  }
+
+  double get_screen_resolution (void) const
+  {
+    // FLTK doesn't give this info
+    return 72.0;
+  }
+
+  Matrix get_screen_size (void) const
+  {
+    Matrix sz (1, 2, 0.0);
+    sz(0) = Fl::w ();
+    sz(1) = Fl::h ();
+    return sz;
+  }
+};
+
+static bool backend_registered = false;
+// call this to init the fltk backend
+DEFUN_DLD (__init_fltk__, , , "")
+{
+  graphics_backend::register_backend (new fltk_backend);
+  backend_registered = true;
+
+  octave_value retval;
+  return retval;
+}
+
+
+// call this to delete the fltk backend
+DEFUN_DLD (__remove_fltk__, , , "")
+{
+  figure_manager::Instance ().close_all ();
+  graphics_backend::unregister_backend (FLTK_BACKEND_NAME);
+  backend_registered = false;
+
+  // FIXME ???
+  // give FLTK 10 seconds to wrap it up
+  Fl::wait(10);	
+  octave_value retval;
+  return retval;	
+}
+
+// give FLTK no more than 0.01 sec to do it's stuff
+static double fltk_maxtime = 1e-2;
+
+// call this to delete the fltk backend
+DEFUN_DLD (__fltk_maxtime__, args, ,"")
+{
+  octave_value retval = fltk_maxtime;
+
+  if (args.length () == 1)
+    {
+      if (args(0).is_real_scalar ())
+      fltk_maxtime = args(0).double_value ();
+    else
+      error ("argument must be a real scalar");
+    }
+
+  return retval;
+}
+
+// call this from the idle_callback to refresh windows
+DEFUN_DLD (__fltk_redraw__, , ,
+  "internal function for the fltk backend")
+{
+  octave_value retval;
+
+  if (!backend_registered)
+    return retval;
+
+  // we scan all figures and add those which use FLTK as a backend
+  graphics_object obj = gh_manager::get_object (0);
+  if (obj && obj.isa ("root_figure"))
+    {
+      base_properties& props = obj.get_properties ();
+      Matrix children = props.get_children ();
+
+      for (octave_idx_type n = 0; n < children.numel (); n++)
+        {
+          graphics_object fobj = gh_manager::get_object (children (n));
+          if (fobj && fobj.isa ("figure"))
+	    {
+	      figure::properties& fp =
+		dynamic_cast<figure::properties&> (fobj.get_properties ());
+	      if (fp.get___backend__ () == FLTK_BACKEND_NAME)
+		figure_manager::Instance ().new_window (fp);
+	    }
+        }
+    }
+
+  Fl::wait (fltk_maxtime);	
+
+  return retval;	
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/
--- a/src/Makefile.in	Thu Jun 05 20:42:14 2008 +0200
+++ b/src/Makefile.in	Thu Jun 05 18:00:37 2008 -0400
@@ -65,7 +65,8 @@
 DLD_XSRC := amd.cc balance.cc besselj.cc betainc.cc bsxfun.cc cellfun.cc \
 	chol.cc ccolamd.cc colamd.cc colloc.cc conv2.cc convhulln.cc daspk.cc \
 	dasrt.cc dassl.cc det.cc dispatch.cc dlmread.cc dmperm.cc eig.cc \
-	expm.cc fft.cc fft2.cc fftn.cc fftw.cc filter.cc find.cc fsolve.cc \
+	expm.cc fft.cc fft2.cc fftn.cc fftw.cc filter.cc find.cc \
+	fltk_backend.cc fsolve.cc \
 	gammainc.cc gcd.cc getgrent.cc getpwent.cc getrusage.cc \
 	givens.cc hess.cc hex2num.cc inv.cc kron.cc lookup.cc lsode.cc \
 	lu.cc luinc.cc matrix_type.cc max.cc md5sum.cc pinv.cc qr.cc \
@@ -117,8 +118,8 @@
 
 INCLUDES := Cell.h base-list.h builtins.h c-file-ptr-stream.h \
 	comment-list.h debug.h defun-dld.h defun-int.h defun.h \
-	dirfns.h dynamic-ld.h error.h file-io.h gripes.h help.h \
-	input.h lex.h load-path.h load-save.h ls-hdf5.h \
+	dirfns.h dynamic-ld.h error.h file-io.h gl-render.h \
+	gripes.h help.h input.h lex.h load-path.h load-save.h ls-hdf5.h \
 	ls-mat-ascii.h ls-mat4.h ls-mat5.h ls-oct-ascii.h \
 	ls-oct-binary.h ls-utils.h mex.h mexproto.h oct-errno.h \
 	oct-fstrm.h oct-hist.h oct-iostrm.h oct-map.h oct-obj.h \
@@ -196,9 +197,9 @@
 
 DIST_SRC := Cell.cc bitfcns.cc c-file-ptr-stream.cc comment-list.cc \
 	cutils.c data.cc debug.cc defaults.cc defun.cc dirfns.cc \
-	dynamic-ld.cc error.cc file-io.cc graphics.cc gripes.cc \
-	help.cc input.cc lex.l load-path.cc load-save.cc ls-hdf5.cc \
-	ls-mat-ascii.cc ls-mat4.cc ls-mat5.cc ls-oct-ascii.cc \
+	dynamic-ld.cc error.cc file-io.cc gl-render.cc graphics.cc \
+	gripes.cc help.cc input.cc lex.l load-path.cc load-save.cc \
+	ls-hdf5.cc ls-mat-ascii.cc ls-mat4.cc ls-mat5.cc ls-oct-ascii.cc \
 	ls-oct-binary.cc ls-utils.cc main.c mappers.cc matherr.c \
 	mex.cc oct-fstrm.cc oct-hist.cc oct-iostrm.cc oct-map.cc \
 	oct-obj.cc oct-prcstrm.cc oct-procbuf.cc oct-stream.cc \
@@ -348,7 +349,7 @@
 	$(OCTAVE_LIBS) \
 	$(LEXLIB) $(UMFPACK_LIBS) $(AMD_LIBS) $(CAMD_LIBS) $(COLAMD_LIBS) \
 	$(CHOLMOD_LIBS) $(CCOLAMD_LIBS) $(CXSPARSE_LIBS) $(BLAS_LIBS) \
-	$(FFTW_LIBS) $(LIBS) $(FLIBS)
+	$(FFTW_LIBS) $(OPENGL_LIBS) $(LIBS) $(FLIBS)
 
 stmp-pic: pic
 	@if [ -f stmp-pic ]; then \
@@ -633,6 +634,8 @@
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(CURL_LIBS)
     __glpk__.oct : pic/__glpk__.o octave$(EXEEXT)
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GLPK_LIBS)
+    fltk_backend.oct : pic/fltk_backend.o octave$(EXEEXT)
+	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GRAPHICS_LIBS) $(FT2_LIBS)
   else
     convhulln.oct : convhulln.o octave$(EXEEXT)
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(QHULL_LIBS)
@@ -646,6 +649,8 @@
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(CURL_LIBS)
     __glpk__.oct : __glpk__.o octave$(EXEEXT)
 	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GLPK_LIBS)
+    fltk_backend.oct : fltk_backend.o octave$(EXEEXT)
+	  $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GRAPHICS_LIBS) $(FT2_LIBS)
   endif
 endif
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gl-render.cc	Thu Jun 05 18:00:37 2008 -0400
@@ -0,0 +1,2783 @@
+/*
+
+Copyright (C) 2008 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <lo-mappers.h>
+#include "gl-render.h"
+
+#define LIGHT_MODE GL_FRONT_AND_BACK
+
+// Win32 API requires the CALLBACK attributes for
+// GLU callback functions. Define it to empty on
+// other platforms.
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+enum {
+  AXE_ANY_DIR   = 0,
+  AXE_DEPTH_DIR = 1,
+  AXE_HORZ_DIR  = 2,
+  AXE_VERT_DIR  = 3
+};
+
+static octave_idx_type
+xmin (octave_idx_type x, octave_idx_type y)
+{
+  return x < y ? x : y;
+}
+
+class
+opengl_texture
+{
+protected:
+  class texture_rep
+  {
+  public:
+    texture_rep (void) : valid (false), count (1) { }
+
+    texture_rep (GLuint _id, int _w, int _h, int _tw, int _th)
+	: id (_id), w (_w), h (_h), tw (_tw), th (_th),
+	  tx (double(w)/tw), ty (double(h)/th), valid (true),
+	  count (1) { }
+
+    ~texture_rep (void)
+      {
+	if (valid)
+	  glDeleteTextures (1, &id);
+      }
+
+    void bind (int mode) const
+      { if (valid) glBindTexture (mode, id); }
+
+    void tex_coord (double q, double r) const
+      { if (valid) glTexCoord2d (q*tx, r*ty); }
+
+    GLuint id;
+    int w, h;
+    int tw, th;
+    double tx, ty;
+    bool valid;
+    int count;
+  };
+
+  texture_rep *rep;
+
+private:
+  opengl_texture (texture_rep *_rep) : rep (_rep) { }
+
+public:
+  opengl_texture (void) : rep (new texture_rep ()) { }
+
+  opengl_texture (const opengl_texture& tx)
+      : rep (tx.rep)
+    {
+      rep->count++;
+    }
+
+  ~opengl_texture (void)
+    {
+      if (--rep->count == 0)
+	delete rep;
+    }
+
+  opengl_texture& operator = (const opengl_texture& tx)
+    {
+      if (--rep->count == 0)
+	delete rep;
+
+      rep = tx.rep;
+      rep->count++;
+
+      return *this;
+    }
+
+  static opengl_texture create (const octave_value& data);
+
+  void bind (int mode = GL_TEXTURE_2D) const
+    { rep->bind (mode); }
+
+  void tex_coord (double q, double r) const
+    { rep->tex_coord (q, r); }
+  
+  bool is_valid (void) const
+    { return rep->valid; }
+};
+
+static int
+next_power_of_2 (int n)
+{
+  int m = 1;
+
+  while (m < n && m < INT_MAX)
+    m <<= 1;
+
+  return m;
+}
+
+opengl_texture
+opengl_texture::create (const octave_value& data)
+{
+  opengl_texture retval;
+
+  dim_vector dv (data.dims ());
+
+  // Expect RGB data
+  if (dv.length () == 3 && dv(2) == 3)
+    {
+      int h = dv(0), w = dv(1), tw, th;
+      GLuint id;
+      bool ok = true;
+
+      tw = next_power_of_2 (w);
+      th = next_power_of_2 (w);
+
+      glGenTextures (1, &id);
+      glBindTexture (GL_TEXTURE_2D, id);
+
+      if (data.is_double_type ())
+	{
+	  NDArray _a = data.array_value ();
+
+	  OCTAVE_LOCAL_BUFFER (float, 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]   = _a(i,j,0);
+		a[idx+1] = _a(i,j,1);
+		a[idx+2] = _a(i,j,2);
+	      }
+
+	  glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
+			GL_RGB, GL_FLOAT, a);
+	}
+      else if (data.is_uint8_type ())
+	{
+	  uint8NDArray _a = data.uint8_array_value ();
+
+	  OCTAVE_LOCAL_BUFFER (octave_uint8, 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]   = _a(i,j,0);
+		a[idx+1] = _a(i,j,1);
+		a[idx+2] = _a(i,j,2);
+	      }
+
+	  glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
+			GL_RGB, GL_UNSIGNED_BYTE, a);
+	}
+      else
+	{
+	  ok = false;
+	  warning ("opengl_texture::create: invalid texture data type (expected double or uint8)");
+	}
+
+      if (ok)
+	{
+	  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+	  if (glGetError () != GL_NO_ERROR)
+	    warning ("opengl_texture::create: OpenGL error while generating texture data");
+	  else
+	    retval = opengl_texture (new texture_rep (id, w, h, tw, th));
+	}
+    }
+  else
+    warning ("opengl_texture::create: invalid texture data size");
+
+  return retval;
+}
+
+class
+opengl_tesselator
+{
+public:
+  typedef void (CALLBACK *fcn) (void);
+
+public:
+
+  opengl_tesselator (void) : glu_tess (0) { init (); }
+
+  virtual ~opengl_tesselator (void)
+    { if (glu_tess) gluDeleteTess (glu_tess); }
+
+  void begin_polygon (bool filled = true)
+    {
+      gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY,
+		       (filled ? GL_FALSE : GL_TRUE));
+      fill = filled;
+      gluTessBeginPolygon (glu_tess, this);
+    }
+
+  void end_polygon (void) const
+    { gluTessEndPolygon (glu_tess); }
+
+  void begin_contour (void) const
+    { gluTessBeginContour (glu_tess); }
+
+  void end_contour (void) const
+    { gluTessEndContour (glu_tess); }
+
+  void add_vertex (double *loc, void *data) const
+    { gluTessVertex (glu_tess, loc, data); }
+
+protected:
+  virtual void begin (GLenum /*type*/) { }
+
+  virtual void end (void) { }
+
+  virtual void vertex (void */*data*/) { }
+
+  virtual void combine (GLdouble /*c*/[3], void */*data*/[4],
+			GLfloat /*w*/[4], void **/*out_data*/) { }
+
+  virtual void edge_flag (GLboolean /*flag*/) { }
+
+  virtual void error (GLenum err)
+    { ::error ("OpenGL tesselation error (%d)", err); }
+
+  virtual void init (void)
+    {
+      glu_tess = gluNewTess ();
+
+      gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA,
+		       reinterpret_cast<fcn> (tess_begin));
+      gluTessCallback (glu_tess, GLU_TESS_END_DATA,
+		       reinterpret_cast<fcn> (tess_end));
+      gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA,
+		       reinterpret_cast<fcn> (tess_vertex));
+      gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA,
+		       reinterpret_cast<fcn> (tess_combine));
+      gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA,
+		       reinterpret_cast<fcn> (tess_edge_flag));
+      gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA,
+		       reinterpret_cast<fcn> (tess_error));
+    }
+
+  bool is_filled (void) const { return fill; }
+
+private:
+  static void CALLBACK tess_begin (GLenum type, void *t)
+    { reinterpret_cast<opengl_tesselator *> (t)->begin (type); }
+  
+  static void CALLBACK tess_end (void *t)
+    { reinterpret_cast<opengl_tesselator *> (t)->end (); }
+  
+  static void CALLBACK tess_vertex (void *v, void *t)
+    { reinterpret_cast<opengl_tesselator *> (t)->vertex (v); }
+  
+  static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4],
+				     void **out,  void *t)
+    { reinterpret_cast<opengl_tesselator *> (t)->combine (c, v, w, out); }
+  
+  static void CALLBACK tess_edge_flag (GLboolean flag, void *t)
+    { reinterpret_cast<opengl_tesselator *> (t)->edge_flag (flag); }
+  
+  static void CALLBACK tess_error (GLenum err, void *t)
+    { reinterpret_cast<opengl_tesselator *> (t)->error (err); }
+
+private:
+  GLUtesselator *glu_tess;
+  bool fill;
+};
+
+class
+vertex_data
+{
+public:
+  class vertex_data_rep
+  {
+  public:
+    Matrix coords;
+    Matrix color;
+    Matrix normal;
+    double alpha;
+    float ambient;
+    float diffuse;
+    float specular;
+    float specular_exp;
+
+    // reference counter
+    int count;
+
+    vertex_data_rep (void) { }
+
+    vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& n,
+		     double a, float as, float ds, float ss, float se)
+	: coords (c), color (col), normal (n), alpha (a),
+	  ambient (as), diffuse (ds), specular (ss), specular_exp (se),
+	  count (1) { }
+  };
+
+private:
+  vertex_data_rep *rep;
+
+  vertex_data_rep *nil_rep (void) const
+    {
+      static vertex_data_rep *nr = new vertex_data_rep ();
+
+      return nr;
+    }
+
+public:
+  vertex_data (void) : rep (nil_rep ()) { }
+
+  vertex_data (const vertex_data& v) : rep (v.rep)
+    { rep->count++; }
+
+  vertex_data (const Matrix& c, const Matrix& col, const Matrix& n,
+	       double a, float as, float ds, float ss, float se)
+      : rep (new vertex_data_rep (c, col, n, a, as, ds, ss, se))
+    {
+      rep->count++;
+    }
+
+  vertex_data (vertex_data_rep *new_rep)
+      : rep (new_rep) { }
+
+  ~vertex_data (void)
+    {
+      if (--rep->count == 0)
+	delete rep;
+    }
+
+  vertex_data& operator = (const vertex_data& v)
+    {
+      if (--rep->count == 0)
+	delete rep;
+
+      rep = v.rep;
+      rep->count++;
+
+      return *this;
+    }
+
+  vertex_data_rep *get_rep (void) const { return rep; }
+};
+
+#include <Array.cc>
+
+class
+opengl_renderer::patch_tesselator : public opengl_tesselator
+{
+public:
+  patch_tesselator (opengl_renderer *r, int cmode, int lmode, int idx = 0)
+      : opengl_tesselator (), renderer (r),
+        color_mode (cmode), light_mode (lmode), index (idx),
+        first (true) { }
+
+protected:
+  void begin (GLenum type)
+    {
+      //printf("patch_tesselator::begin (%d)\n", type);
+      first = true;
+
+      if (color_mode == 2 || light_mode == 2)
+	glShadeModel (GL_SMOOTH);
+      else
+	glShadeModel (GL_FLAT);
+
+      if (is_filled ())
+	renderer->set_polygon_offset (true, 1+index);
+
+      glBegin (type);
+    }
+
+  void end (void)
+    {
+      //printf("patch_tesselator::end\n");
+      glEnd ();
+      renderer->set_polygon_offset (false);
+    }
+
+  void vertex (void *data)
+    {
+      vertex_data::vertex_data_rep *v
+	  = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
+      //printf("patch_tesselator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->coords(2));
+
+      // FIXME: why did I need to keep the first vertex of the face
+      // in JHandles? I think it's related to the fact that the 
+      // tessellation process might re-order the vertices, such that
+      // the first one you get here might not be the first one of the face;
+      // but I can't figure out the actual reason.
+      if (color_mode > 0 && (first || color_mode == 2))
+	{
+	  Matrix col = v->color;
+
+	  if (col.numel () == 3)
+	    {
+	      glColor3dv (col.data ());
+	      if (light_mode > 0)
+		{
+		  float buf[4] = { 0, 0, 0, 1 };
+
+		  for (int k = 0; k < 3; k++)
+		    buf[k] = (v->ambient * col(k));
+		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
+
+		  for (int k = 0; k < 3; k++)
+		    buf[k] = (v->diffuse * col(k));
+		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
+		}
+	    }
+	}
+
+      if (light_mode > 0 && (first || light_mode == 2))
+	glNormal3dv (v->normal.data ());
+
+      glVertex3dv (v->coords.data ());
+
+      first = false;
+    }
+
+  void combine (GLdouble xyz[3], void *data[4], GLfloat w[4],
+		void **out_data)
+    {
+      //printf("patch_tesselator::combine\n");
+
+      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 nn (1, 3, 0.0);
+      double aa = 0.0;
+
+      vv(0) = xyz[0];
+      vv(1) = xyz[1];
+      vv(2) = xyz[2];
+
+      if (v[0]->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]->color(ic));
+	}
+
+      if (v[0]->normal.numel () > 0)
+	{
+	  for (int in = 0; in < 3; in++)
+	    for (int iv = 0; iv < vmax; iv++)
+	      nn(in) += (w[iv] * v[iv]->normal(in));
+	}
+
+      for (int iv = 0; iv < vmax; iv++)
+	aa += (w[iv] * v[iv]->alpha);
+
+      vertex_data new_v (vv, cc, nn, aa, v[0]->ambient, v[0]->diffuse,
+			 v[0]->specular, v[0]->specular_exp);
+      tmp_vdata.push_back (new_v);
+
+      *out_data = new_v.get_rep ();
+    }
+
+private:
+  opengl_renderer *renderer;
+  int color_mode;	// 0: uni,  1: flat, 2: interp
+  int light_mode;	// 0: none, 1: flat, 2: gouraud
+  int index;
+  bool first;
+  std::list<vertex_data> tmp_vdata;
+};
+
+void
+opengl_renderer::draw (const graphics_object& go)
+{
+  if (! go.valid_object ())
+    return;
+
+  const base_properties& props = go.get_properties ();
+
+  if (go.isa ("figure"))
+    draw (dynamic_cast<const figure::properties&> (props));
+  else if (go.isa ("axes"))
+    draw (dynamic_cast<const axes::properties&> (props));
+  else if (go.isa ("line"))
+    draw (dynamic_cast<const line::properties&> (props));
+  else if (go.isa ("surface"))
+    draw (dynamic_cast<const surface::properties&> (props));
+  else if (go.isa ("patch"))
+    draw (dynamic_cast<const patch::properties&> (props));
+  else if (go.isa ("hggroup"))
+    draw (dynamic_cast<const hggroup::properties&> (props));
+  else
+    warning ("opengl_renderer: cannot render object of type `%s'",
+	     props.graphics_object_name ().c_str ());
+}
+
+void
+opengl_renderer::draw (const figure::properties& props)
+{
+  backend = props.get_backend ();
+
+  // Initialize OpenGL context
+
+  glEnable (GL_DEPTH_TEST);
+  glDepthFunc (GL_LEQUAL);
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glEnable (GL_NORMALIZE);
+
+  // Clear background
+
+  Matrix c = props.get_color_rgb ();
+
+  if (c.length() >= 3)
+    {
+      glClearColor (c(0), c(1), c(2), 1);
+      glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    }
+
+  // Draw children
+
+  draw (props.get_children ());
+}
+
+void
+opengl_renderer::draw (const axes::properties& props)
+{
+  // setup OpenGL transformation
+
+  Matrix x_zlim = props.get_transform_zlim ();
+  Matrix x_mat1 = props.get_opengl_matrix_1 ();
+  Matrix x_mat2 = props.get_opengl_matrix_2 ();
+  
+  xZ1 = x_zlim(0)-(x_zlim(1)-x_zlim(0))/2;
+  xZ2 = x_zlim(1)+(x_zlim(1)-x_zlim(0))/2;
+
+  int vw[4];
+  glGetIntegerv (GL_VIEWPORT, vw);
+
+  glMatrixMode (GL_MODELVIEW);
+  glLoadIdentity ();
+  glScaled(1, 1, -1);
+  glMultMatrixd (x_mat1.data ());
+  glMatrixMode (GL_PROJECTION);
+  glLoadIdentity ();
+  glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+  glMultMatrixd (x_mat2.data ());
+  glMatrixMode (GL_MODELVIEW);
+
+  glClear (GL_DEPTH_BUFFER_BIT);
+
+  // store axes transformation data
+
+  xform = props.get_transform ();
+  
+  // draw axes object
+  
+  Matrix xlim = xform.xscale (props.get_xlim ().matrix_value ());
+  Matrix ylim = xform.yscale (props.get_ylim ().matrix_value ());
+  Matrix zlim = xform.zscale (props.get_zlim ().matrix_value ());
+  double x_min = xlim(0), x_max = xlim(1);
+  double y_min = ylim(0), y_max = ylim(1);
+  double z_min = zlim(0), z_max = zlim(1);
+  
+  double xd = (props.xdir_is ("normal") ? 1 : -1);
+  double yd = (props.ydir_is ("normal") ? 1 : -1);
+  double zd = (props.zdir_is ("normal") ? 1 : -1);
+
+  ColumnVector p1, p2, xv (3), yv (3), zv (3);
+  int xstate, ystate, zstate;
+
+  xstate = ystate = zstate = AXE_ANY_DIR;
+
+  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
+  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
+  xv(0) = xround (p2(0)-p1(0));
+  xv(1) = xround (p2(1)-p1(1));
+  xv(2) = (p2(2)-p1(2));
+  if (xv(0) == 0 && xv(1) == 0)
+    xstate = AXE_DEPTH_DIR;
+  else if (xv(2) == 0)
+    {
+      if (xv(0) == 0)
+        xstate = AXE_VERT_DIR;
+      else if (xv(1) == 0)
+        xstate = AXE_HORZ_DIR;
+    }
+  double xPlane;
+  if (xv(2) == 0)
+    if (xv(1) == 0)
+      xPlane = (xv(0) > 0 ? x_max : x_min);
+    else
+      xPlane = (xv(1) < 0 ? x_max : x_min);
+  else
+    xPlane = (xv(2) < 0 ? x_min : x_max);
+  double xPlaneN = (xPlane == x_min ? x_max : x_min);
+  double fx = (x_max-x_min)/sqrt(xv(0)*xv(0)+xv(1)*xv(1));
+
+  p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
+  p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
+  yv(0) = xround (p2(0)-p1(0));
+  yv(1) = xround (p2(1)-p1(1));
+  yv(2) = (p2(2)-p1(2));
+  if (yv(0) == 0 && yv(1) == 0)
+    ystate = AXE_DEPTH_DIR;
+  else if (yv(2) == 0)
+    {
+      if (yv(0) == 0)
+        ystate = AXE_VERT_DIR;
+      else if (yv(1) == 0)
+        ystate = AXE_HORZ_DIR;
+    }
+  double yPlane;
+  if (yv(2) == 0)
+    if (yv(1) == 0)
+      yPlane = (yv(0) > 0 ? y_max : y_min);
+    else
+      yPlane = (yv(1) < 0 ? y_max : y_min);
+  else
+    yPlane = (yv(2) < 0 ? y_min : y_max);
+  double yPlaneN = (yPlane == y_min ? y_max : y_min);
+  double fy = (y_max-y_min)/sqrt(yv(0)*yv(0)+yv(1)*yv(1));
+
+  p1 = xform.transform((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
+  p2 = xform.transform((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
+  zv(0) = xround(p2(0)-p1(0));
+  zv(1) = xround (p2(1)-p1(1));
+  zv(2) = (p2(2)-p1(2));
+  if (zv(0) == 0 && zv(1) == 0)
+    zstate = AXE_DEPTH_DIR;
+  else if (zv(2) == 0)
+  {
+    if (zv(0) == 0)
+      zstate = AXE_VERT_DIR;
+    else if (zv(1) == 0)
+      zstate = AXE_HORZ_DIR;
+  }
+  double zPlane;
+  if (zv(2) == 0)
+    if (zv(1) == 0)
+      zPlane = (zv(0) > 0 ? z_min : z_max);
+    else
+      zPlane = (zv(1) < 0 ? z_min : z_max);
+  else
+    zPlane = (zv(2) < 0 ? z_min : z_max);
+  double zPlaneN = (zPlane == z_min ? z_max : z_min);
+  double fz = (z_max-z_min)/sqrt(zv(0)*zv(0)+zv(1)*zv(1));
+
+  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
+        (ystate > AXE_DEPTH_DIR ? 1 : 0) +
+        (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
+  if (props.tickdirmode_is ("auto"))
+  {
+    // FIXME: tickdir should be updated (code below comes
+    //        from JHandles)
+    //autoMode++;
+    //TickDir.set(mode2d ? "in" : "out", true);
+    //autoMode--;
+  }
+
+  // FIXME: use ticklength property
+  double xticklen = 7, yticklen = 7, zticklen = 7;
+
+  //double tickdir = (props.tickdir_is ("in") ? -1 : 1);
+  double tickdir = (props.tickdirmode_is ("auto") ?
+		    (mode2d ? -1 : 1) :
+		    (props.tickdir_is ("in") ? -1 : 1));
+  double xtickoffset = (mode2d && tickdir < 0 ? 0 : xticklen) + 5;
+  double ytickoffset = (mode2d && tickdir < 0 ? 0 : yticklen) + 5;
+  double ztickoffset = (mode2d && tickdir < 0 ? 0 : zticklen) + 5;
+
+  bool xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
+  bool x2Dtop = false;
+  bool y2Dright = false;
+  double zpTick = zPlane;
+
+  /* 2D mode */
+  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
+  {
+    if (props.xaxislocation_is ("top"))
+    {
+      double tmp = yPlane;
+      yPlane = yPlaneN;
+      yPlaneN = tmp;
+      x2Dtop = true;
+    }
+    if (props.yaxislocation_is ("right"))
+    {
+      double tmp = xPlane;
+      xPlane = xPlaneN;
+      xPlaneN = tmp;
+      y2Dright = true;
+    }
+    if (props.layer_is ("top"))
+      zpTick = zPlaneN;
+  }
+
+  Matrix axe_color = props.get_color_rgb ();
+  bool visible = props.is_visible ();
+  bool box = props.is_box ();
+
+  // Axes planes
+
+  if (axe_color.numel () > 0 && visible)
+    {
+      set_color (axe_color);
+      set_polygon_offset (true, 2.5);
+
+      glBegin (GL_QUADS);
+
+      // X plane
+      glVertex3d (xPlane, y_min, z_min);
+      glVertex3d (xPlane, y_max, z_min);
+      glVertex3d (xPlane, y_max, z_max);
+      glVertex3d (xPlane, y_min, z_max);
+
+      // Y plane
+      glVertex3d (x_min, yPlane, z_min);
+      glVertex3d (x_max, yPlane, z_min);
+      glVertex3d (x_max, yPlane, z_max);
+      glVertex3d (x_min, yPlane, z_max);
+
+      // Z plane
+      glVertex3d (x_min, y_min, zPlane);
+      glVertex3d (x_max, y_min, zPlane);
+      glVertex3d (x_max, y_max, zPlane);
+      glVertex3d (x_min, y_max, zPlane);
+
+      glEnd ();
+
+      set_polygon_offset (false);
+    }
+
+  // Axes box
+
+  set_linestyle ("-", true);
+  set_linewidth (props.get_linewidth ());
+
+  if (visible)
+    {
+      glBegin (GL_LINES);
+
+      // X box
+      set_color (props.get_xcolor_rgb ());
+      glVertex3d (xPlaneN, yPlaneN, zPlane);
+      glVertex3d (xPlane, yPlaneN, zPlane);
+      if (box)
+        {
+          glVertex3d (xPlaneN, yPlane, zPlane);
+          glVertex3d (xPlane, yPlane, zPlane);
+          glVertex3d (xPlaneN, yPlane, zPlaneN);
+          glVertex3d (xPlane, yPlane, zPlaneN);
+          glVertex3d (xPlaneN, yPlaneN, zPlaneN);
+          glVertex3d (xPlane, yPlaneN, zPlaneN);
+        }
+
+      // Y box
+      set_color (props.get_ycolor_rgb ());
+      glVertex3d (xPlaneN, yPlaneN, zPlane);
+      glVertex3d (xPlaneN, yPlane, zPlane);
+      if (box)
+        {
+          glVertex3d (xPlane, yPlaneN, zPlane);
+          glVertex3d (xPlane, yPlane, zPlane);
+          glVertex3d (xPlane, yPlaneN, zPlaneN);
+          glVertex3d (xPlane, yPlane, zPlaneN);
+          glVertex3d (xPlaneN, yPlaneN, zPlaneN);
+          glVertex3d (xPlaneN, yPlane, zPlaneN);
+        }
+
+      // Z box
+      set_color (props.get_zcolor_rgb ());
+      if (xySym)
+        {
+          glVertex3d (xPlaneN, yPlane, zPlaneN);
+          glVertex3d (xPlaneN, yPlane, zPlane);
+        }
+      else
+        {
+          glVertex3d (xPlane, yPlaneN, zPlaneN);
+          glVertex3d (xPlane, yPlaneN, zPlane);
+        }
+      if (box)
+        {
+          glVertex3d (xPlane, yPlane, zPlaneN);
+          glVertex3d (xPlane, yPlane, zPlane);
+          if (xySym)
+            {
+              glVertex3d (xPlane, yPlaneN, zPlaneN);
+              glVertex3d (xPlane, yPlaneN, zPlane);
+            }
+          else
+            {
+              glVertex3d (xPlaneN, yPlane, zPlaneN);
+              glVertex3d (xPlaneN, yPlane, zPlane);
+            }
+          glVertex3d (xPlaneN, yPlaneN, zPlaneN);
+          glVertex3d (xPlaneN, yPlaneN, zPlane);
+        }
+
+      glEnd ();
+    }
+
+  std::string gridstyle = props.get_gridlinestyle ();
+  std::string minorgridstyle = props.get_minorgridlinestyle ();
+
+  // X grid
+
+  if (visible && xstate != AXE_DEPTH_DIR)
+    {
+      bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
+      bool do_xminorgrid = (props.is_xminorgrid () && (minorgridstyle != "none"));
+      bool do_xminortick = props.is_xminortick ();
+      Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ());
+      // FIXME: use pre-computed minor ticks
+      Matrix xmticks;
+      // FIXME: use xticklabels property
+      string_vector xticklabels;
+      int wmax = 0, hmax = 0;
+      bool tick_along_z = xisinf (fy);
+      Matrix tickpos (xticks.numel (), 3);
+
+      set_color (props.get_xcolor_rgb ());
+
+      // grid lines
+      if (do_xgrid)
+        {
+          set_linestyle (gridstyle, true);
+          glBegin (GL_LINES);
+          for (int i = 0; i < xticks.numel (); i++)
+            {
+              double xval = xticks(i);
+
+              glVertex3d (xval, yPlaneN, zpTick);
+              glVertex3d (xval, yPlane, zpTick);
+              if (zstate != AXE_DEPTH_DIR)
+                {
+                  glVertex3d (xval, yPlane, zPlaneN);
+                  glVertex3d (xval, yPlane, zPlane);
+                }
+            }
+          glEnd ();
+          set_linestyle ("-", true);
+        }
+
+      // tick marks
+      if (tick_along_z)
+        {
+          glBegin (GL_LINES);
+          for (int i = 0; i < xticks.numel (); i++)
+            {
+              double xval = xticks(i);
+
+              glVertex3d (xval, yPlaneN, zPlane);
+              glVertex3d (xval, yPlaneN, zPlane+signum(zPlane-zPlaneN)*fz*xticklen*tickdir);
+              if (box && xstate != AXE_ANY_DIR)
+                {
+                  glVertex3d (xval, yPlaneN, zPlaneN);
+                  glVertex3d (xval, yPlaneN,
+                        zPlaneN+signum(zPlaneN-zPlane)*fz*xticklen*tickdir);
+                }
+              tickpos(i,0) = xval;
+              tickpos(i,1) = yPlaneN;
+              tickpos(i,2) = zPlane+signum(zPlane-zPlaneN)*fz*xtickoffset;
+            }
+          glEnd ();
+        }
+      else
+        {
+          glBegin (GL_LINES);
+          for (int i = 0; i < xticks.numel (); i++)
+            {
+              double xval = xticks(i);
+
+              glVertex3d (xval, yPlaneN, zpTick);
+              glVertex3d (xval, yPlaneN+signum(yPlaneN-yPlane)*fy*xticklen*tickdir, zpTick);
+              if (box && xstate != AXE_ANY_DIR)
+                {
+                  glVertex3d (xval, yPlane, zpTick);
+                  glVertex3d (xval,
+                        yPlane+signum(yPlane-yPlaneN)*fy*xticklen*tickdir, zpTick);
+                }
+              tickpos(i,0) = xval;
+              tickpos(i,1) = yPlaneN+signum(yPlaneN-yPlane)*fy*xtickoffset;
+              tickpos(i,2) = zPlane;
+            }
+          glEnd ();
+        }
+
+      // FIXME: tick texts
+
+      // minor grid lines
+      if (do_xminorgrid)
+        {
+          set_linestyle (minorgridstyle, true);
+          glBegin (GL_LINES);
+          for (int i = 0; i < xmticks.numel (); i++)
+            {
+              double xval = xmticks(i);
+
+              glVertex3d (xval, yPlaneN, zpTick);
+              glVertex3d (xval, yPlane, zpTick);
+              if (zstate != AXE_DEPTH_DIR)
+                {
+                  glVertex3d (xval, yPlane, zPlaneN);
+                  glVertex3d (xval, yPlane, zPlane);
+                }
+            }
+          glEnd ();
+          set_linestyle ("-", true);
+        }
+			
+      // minor tick marks
+      if (do_xminortick)
+        {
+          if (tick_along_z)
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < xmticks.numel (); i++)
+                {
+                  double xval = xmticks(i);
+
+                  glVertex3d (xval, yPlaneN, zPlane);
+                  glVertex3d (xval, yPlaneN,
+                      zPlane+signum(zPlane-zPlaneN)*fz*xticklen/2*tickdir);
+                  if (box && xstate != AXE_ANY_DIR)
+                    {
+                      glVertex3d (xval, yPlaneN, zPlaneN);
+                      glVertex3d (xval, yPlaneN,
+                          zPlaneN+signum(zPlaneN-zPlane)*fz*xticklen/2*tickdir);
+                    }
+                }
+              glEnd ();
+            }
+          else
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < xmticks.numel (); i++)
+                {
+                  double xval = xmticks(i);
+
+                  glVertex3d (xval, yPlaneN, zpTick);
+                  glVertex3d (xval,
+                        yPlaneN+signum(yPlaneN-yPlane)*fy*xticklen/2*tickdir, zpTick);
+                  if (box && xstate != AXE_ANY_DIR)
+                    {
+                      glVertex3d (xval, yPlane, zpTick);
+                      glVertex3d (xval,
+                            yPlane+signum(yPlane-yPlaneN)*fy*xticklen/2*tickdir, zpTick);
+                    }
+                }
+              glEnd ();
+            }
+        }
+
+      text::properties& xlabel_props =
+        reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_xlabel ()).get_properties ());
+
+      // FIXME: auto-positioning should be disabled if the 
+      //        label has been positioned manually
+      if (! xlabel_props.get_string ().empty ())
+        {
+          xlabel_props.set_horizontalalignment (xstate > AXE_DEPTH_DIR ? "center" : (xySym ? "left" : "right"));
+	  xlabel_props.set_verticalalignment (xstate == AXE_VERT_DIR ? "bottom" : (zd*zv(2) <= 0 ? "top" : "bottom"));
+
+          double angle = 0;
+          ColumnVector p = graphics_xform::xform_vector ((x_min+x_max)/2, yPlaneN, zPlane);
+
+          if (tick_along_z)
+            p(2) += (signum(zPlane-zPlaneN)*fz*xtickoffset);
+          else
+            p(1) += (signum(yPlaneN-yPlane)*fy*xtickoffset);
+          p = xform.transform (p(0), p(1), p(2), false);
+          switch (xstate)
+            {
+              case AXE_ANY_DIR:
+                p(0) += (xySym ? wmax : -wmax);
+                p(1) += (zd*zv(2) <= 0 ? hmax : -hmax);
+                break;
+              case AXE_VERT_DIR:
+                p(0) -= wmax;
+                angle = 90;
+                break;
+              case AXE_HORZ_DIR:
+                p(1) += hmax;
+                break;
+            }
+          p = xform.untransform (p(0), p(1), p(2), true);
+          xlabel_props.set_position (p.extract_n (0, 3).transpose ());
+          xlabel_props.set_rotation (angle);
+        }
+    }
+
+  // Y grid
+		
+  if (ystate != AXE_DEPTH_DIR && visible)
+    {
+      bool do_ygrid = (props.is_ygrid () && (gridstyle != "none"));
+      bool do_yminorgrid = (props.is_yminorgrid () && (minorgridstyle != "none"));
+      bool do_yminortick = props.is_yminortick ();
+      Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ());
+      // FIXME: use pre-computed minor ticks
+      Matrix ymticks;
+      // FIXME: use yticklabels property
+      string_vector yticklabels;
+      int wmax = 0, hmax = 0;
+      bool tick_along_z = xisinf (fx);
+      Matrix tickpos (yticks.numel (), 3);
+
+      set_color (props.get_ycolor_rgb ());
+
+      // grid lines
+      if (do_ygrid)
+        {
+          set_linestyle (gridstyle, true);
+          glBegin (GL_LINES);
+          for (int i = 0; i < yticks.numel (); i++)
+            {
+              double yval = yticks(i);
+
+              glVertex3d (xPlaneN, yval, zpTick);
+              glVertex3d (xPlane, yval, zpTick);
+              if (zstate != AXE_DEPTH_DIR)
+                {
+                  glVertex3d (xPlane, yval, zPlaneN);
+                  glVertex3d (xPlane, yval, zPlane);
+                }
+            }
+          glEnd ();
+          set_linestyle ("-", true);
+        }
+
+      // tick marks
+      if (tick_along_z)
+        {
+          glBegin (GL_LINES);
+          for (int i = 0; i < yticks.numel (); i++)
+            {
+              double yval = yticks(i);
+
+              glVertex3d (xPlaneN, yval, zPlane);
+              glVertex3d (xPlaneN, yval, zPlane+signum(zPlane-zPlaneN)*fz*yticklen*tickdir);
+              if (box && ystate != AXE_ANY_DIR)
+                {
+                  glVertex3d (xPlaneN, yval, zPlaneN);
+                  glVertex3d (xPlaneN, yval,
+                        zPlaneN+signum(zPlaneN-zPlane)*fz*yticklen*tickdir);
+                }
+              tickpos(i,0) = xPlaneN;
+              tickpos(i,1) = yval;
+              tickpos(i,2) = zPlane+signum(zPlane-zPlaneN)*fz*ytickoffset;
+            }
+          glEnd ();
+        }
+      else
+        {
+          glBegin (GL_LINES);
+          for (int i = 0; i < yticks.numel (); i++)
+            {
+              double yval = yticks(i);
+
+              glVertex3d (xPlaneN, yval, zpTick);
+              glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*yticklen*tickdir, yval, zpTick);
+              if (box && ystate != AXE_ANY_DIR)
+                {
+                  glVertex3d (xPlane, yval, zpTick);
+                  glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*yticklen*tickdir,
+                        yval, zpTick);
+                }
+              tickpos(i,0) = xPlaneN+signum(xPlaneN-xPlane)*fx*ytickoffset;
+              tickpos(i,1) = yval;
+              tickpos(i,2) = zPlane;
+            }
+          glEnd ();
+        }
+
+      // FIXME: tick texts
+
+      // minor grid lines
+      if (do_yminorgrid)
+        {
+          set_linestyle (minorgridstyle, true);
+          glBegin (GL_LINES);
+          for (int i = 0; i < ymticks.numel (); i++)
+            {
+              double yval = ymticks(i);
+
+              glVertex3d (xPlaneN, yval, zpTick);
+              glVertex3d (xPlane, yval, zpTick);
+              if (zstate != AXE_DEPTH_DIR)
+                {
+                  glVertex3d (xPlane, yval, zPlaneN);
+                  glVertex3d (xPlane, yval, zPlane);
+                }
+            }
+          glEnd ();
+          set_linestyle ("-", true);
+        }
+
+      // minor tick marks
+      if (do_yminortick)
+        {
+          if (tick_along_z)
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < ymticks.numel (); i++)
+                {
+                  double yval = ymticks(i);
+
+                  glVertex3d (xPlaneN, yval, zPlane);
+                  glVertex3d (xPlaneN, yval,
+                        zPlane+signum(zPlane-zPlaneN)*fz*yticklen/2*tickdir);
+                  if (box && ystate != AXE_ANY_DIR)
+                    {
+                      glVertex3d (xPlaneN, yval, zPlaneN);
+                      glVertex3d (xPlaneN, yval,
+                            zPlaneN+signum(zPlaneN-zPlane)*fz*yticklen/2*tickdir);
+                    }
+                }
+              glEnd ();
+            }
+          else
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < ymticks.numel (); i++)
+                {
+                  double yval = ymticks(i);
+
+                  glVertex3d (xPlaneN, yval, zpTick);
+                  glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*yticklen/2*tickdir,
+                        yval, zpTick);
+                  if (box && ystate != AXE_ANY_DIR)
+                    {
+                      glVertex3d (xPlane, yval, zpTick);
+                      glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*yticklen/2*tickdir,
+                            yval, zpTick);
+                    }
+                }
+              glEnd ();
+            }
+        }
+
+      text::properties& ylabel_props =
+        reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_ylabel ()).get_properties ());
+
+      // FIXME: auto-positioning should be disabled if the 
+      //        label has been positioned manually
+      if (! ylabel_props.get_string ().empty ())
+        {
+          ylabel_props.set_horizontalalignment (ystate > AXE_DEPTH_DIR ? "center" : (!xySym ? "left" : "right"));
+	  ylabel_props.set_verticalalignment (ystate == AXE_VERT_DIR ? "bottom" : (zd*zv(2) <= 0 ? "top" : "bottom"));
+
+          double angle = 0;
+          ColumnVector p = graphics_xform::xform_vector (xPlaneN, (y_min+y_max)/2, zPlane);
+
+          if (tick_along_z)
+            p(2) += (signum(zPlane-zPlaneN)*fz*ytickoffset);
+          else
+            p(0) += (signum(xPlaneN-xPlane)*fx*ytickoffset);
+          p = xform.transform (p(0), p(1), p(2), false);
+          switch (ystate)
+            {
+              case AXE_ANY_DIR:
+                p(0) += (!xySym ? wmax : -wmax);
+                p(1) += (zd*zv(2) <= 0 ? hmax : -hmax);
+                break;
+              case AXE_VERT_DIR:
+                p(0) -= wmax;
+                angle = 90;
+                break;
+              case AXE_HORZ_DIR:
+                p(1) += hmax;
+                break;
+            }
+          p = xform.untransform(p(0), p(1), p(2), true);
+          ylabel_props.set_position (p.extract_n (0, 3).transpose ());
+          ylabel_props.set_rotation (angle);
+        }
+    }
+		
+  // Z Grid
+
+  if (zstate != AXE_DEPTH_DIR && visible)
+    {
+      bool do_zgrid = (props.is_zgrid () && (gridstyle != "none"));
+      bool do_zminorgrid = (props.is_zminorgrid () && (minorgridstyle != "none"));
+      bool do_zminortick = props.is_zminortick ();
+      Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ());
+      // FIXME: use pre-computed minor ticks
+      Matrix zmticks;
+      // FIXME: use zticklabels property
+      string_vector zticklabels;
+      int wmax = 0, hmax = 0;
+      Matrix tickpos (zticks.numel (), 3);
+
+      set_color (props.get_zcolor_rgb ());
+
+      // grid lines
+      if (do_zgrid)
+        {
+          set_linestyle (gridstyle, true);
+          glBegin (GL_LINES);
+          for (int i = 0; i < zticks.numel (); i++)
+            {
+              double zval = zticks(i);
+
+              glVertex3d (xPlaneN, yPlane, zval);
+              glVertex3d (xPlane, yPlane, zval);
+              glVertex3d (xPlane, yPlaneN, zval);
+              glVertex3d (xPlane, yPlane, zval);
+            }
+          glEnd ();
+          set_linestyle ("-", true);
+        }
+
+      // tick marks
+      if (xySym)
+        {
+          if (xisinf (fy))
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < zticks.numel (); i++)
+                {
+                  double zval = zticks(i);
+
+                  glVertex3d (xPlaneN, yPlane, zval);
+                  glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*zticklen*tickdir,
+                        yPlane, zval);
+                  if (box && zstate != AXE_ANY_DIR)
+                    {
+                      glVertex3d (xPlane, yPlane, zval);
+                      glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen*tickdir,
+                            yPlane, zval);
+                    }
+                  tickpos(i,0) = xPlaneN+signum(xPlaneN-xPlane)*fx*ztickoffset;
+                  tickpos(i,1) = yPlane;
+                  tickpos(i,2) = zval;
+                }
+              glEnd ();
+            }
+          else
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < zticks.numel (); i++)
+                {
+                  double zval = zticks(i);
+
+                  glVertex3d (xPlaneN, yPlane, zval);
+                  glVertex3d (xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*zticklen*tickdir, zval);
+                  tickpos(i,0) = xPlaneN;
+                  tickpos(i,1) = yPlane+signum(yPlane-yPlaneN)*fy*ztickoffset;
+                  tickpos(i,2) = zval;
+                }
+              glEnd ();
+            }
+        }
+      else
+        {
+          if (xisinf (fx))
+            {
+              glBegin (GL_LINES);
+              for (int i = 0; i < zticks.numel (); i++)
+                {
+                  double zval = zticks(i);
+
+                  glVertex3d (xPlane, yPlaneN, zval);
+                  glVertex3d (xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*zticklen*tickdir, zval);
+                  if (box && zstate != AXE_ANY_DIR)
+                    {
+                      glVertex3d (xPlane, yPlane, zval);
+                      glVertex3d (xPlane, yPlane+signum(yPlane-yPlaneN)*fy*zticklen*tickdir, zval);
+                    }
+                  tickpos(i,0) = xPlane;
+                  tickpos(i,1) = yPlaneN+signum(yPlaneN-yPlane)*fy*ztickoffset;
+                  tickpos(i,2) = zval;
+                }
+              glEnd ();
+            }
+          else
+          {
+            glBegin (GL_LINES);
+            for (int i = 0; i < zticks.numel (); i++)
+              {
+                double zval = zticks(i);
+
+                glVertex3d (xPlane, yPlaneN, zval);
+                glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen*tickdir, yPlaneN, zval);
+                tickpos(i,0) = xPlane+signum(xPlane-xPlaneN)*fx*ztickoffset;
+                tickpos(i,1) = yPlaneN;
+                tickpos(i,2) = zval;
+              }
+            glEnd ();
+          }
+        }
+
+      // FIXME: tick texts
+
+      // minor grid lines
+      if (do_zminorgrid)
+        {
+          set_linestyle (minorgridstyle, true);
+          glBegin (GL_LINES);
+          for (int i = 0; i < zmticks.numel (); i++)
+            {
+              double zval = zmticks(i);
+
+              glVertex3d (xPlaneN, yPlane, zval);
+              glVertex3d (xPlane, yPlane, zval);
+              glVertex3d (xPlane, yPlaneN, zval);
+              glVertex3d (xPlane, yPlane, zval);
+            }
+          glEnd ();
+          set_linestyle ("-", true);
+        }
+
+      // minor tick marks
+      if (do_zminortick)
+        {
+          if (xySym)
+            {
+              if (xisinf (fy))
+                {
+                  glBegin (GL_LINES);
+                  for (int i = 0; i < zmticks.numel (); i++)
+                    {
+                      double zval = zmticks(i);
+
+                      glVertex3d (xPlaneN, yPlane, zval);
+                      glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*zticklen/2*tickdir,
+                            yPlane, zval);
+                      if (box && zstate != AXE_ANY_DIR)
+                        {
+                          glVertex3d (xPlane, yPlane, zval);
+                          glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir,
+                                yPlane, zval);
+                        }
+                    }
+                  glEnd ();
+                }
+              else
+                {
+                  glBegin (GL_LINES);
+                  for (int i = 0; i < zmticks.numel (); i++)
+                    {
+                      double zval = zmticks(i);
+
+                      glVertex3d (xPlaneN, yPlane, zval);
+                      glVertex3d (xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, zval);
+                    }
+                  glEnd ();
+                }
+            }
+          else
+            {
+              if (xisinf (fx))
+                {
+                  glBegin (GL_LINES);
+                  for (int i = 0; i < zmticks.numel (); i++)
+                    {
+                      double zval = zmticks(i);
+
+                      glVertex3d (xPlane, yPlaneN, zval);
+                      glVertex3d (xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*zticklen/2*tickdir, zval);
+                      if (box && zstate != AXE_ANY_DIR)
+                        {
+                          glVertex3d (xPlane, yPlane, zval);
+                          glVertex3d (xPlane, yPlane+signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, zval);
+                        }
+                    }
+                  glEnd ();
+                }
+              else
+                {
+                  glBegin (GL_LINES);
+                  for (int i = 0; i < zmticks.numel (); i++)
+                    {
+                      double zval = zmticks(i);
+
+                      glVertex3d (xPlane, yPlaneN, zval);
+                      glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir, yPlaneN, zval);
+                    }
+                  glEnd ();
+                }
+            }
+        }
+
+      text::properties& zlabel_props =
+        reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_zlabel ()).get_properties ());
+
+      // FIXME: auto-positioning should be disabled if the 
+      //        label has been positioned manually
+      if (! zlabel_props.get_string ().empty ())
+        {
+          bool camAuto = props.cameraupvectormode_is ("auto");
+
+          zlabel_props.set_horizontalalignment ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
+	  zlabel_props.set_verticalalignment(zstate == AXE_VERT_DIR ? "bottom" : ((zd*zv(2) < 0 || camAuto) ? "bottom" : "top"));
+
+          double angle = 0;
+          ColumnVector p;
+
+          if (xySym)
+            {
+              p = graphics_xform::xform_vector (xPlaneN, yPlane, (z_min+z_max)/2);
+              if (xisinf (fy))
+                p(0) += (signum(xPlaneN-xPlane)*fx*ztickoffset);
+              else
+                p(1) += (signum(yPlane-yPlaneN)*fy*ztickoffset);
+            }
+          else
+            {
+              p = graphics_xform::xform_vector (xPlane, yPlaneN, (z_min+z_max)/2);
+              if (xisinf (fx))
+                p(1) += (signum(yPlaneN-yPlane)*fy*ztickoffset);
+              else
+                p(0) += (signum(xPlane-xPlaneN)*fx*ztickoffset);
+            }
+          p = xform.transform (p(0), p(1), p(2), false);
+          switch (zstate)
+            {
+              case AXE_ANY_DIR:
+                if (camAuto)
+                  {
+                    p(0) -= wmax;
+                    angle = 90;
+                  }
+                /* FIXME: what's the correct offset?
+                   p[0] += (!xySym ? wmax : -wmax);
+                   p[1] += (zd*zv[2] <= 0 ? hmax : -hmax);
+                   */
+                break;
+              case AXE_VERT_DIR:
+                p(0) -= wmax;
+                angle = 90;
+                break;
+              case AXE_HORZ_DIR:
+                p(1) += hmax;
+                break;
+            }
+          p = xform.untransform (p(0), p(1), p(2), true);
+          zlabel_props.set_position (p.extract_n (0, 3).transpose ());
+          zlabel_props.set_rotation (angle);
+        }
+    }
+
+  set_linestyle ("-");
+
+  // Title
+
+  text::properties& title_props =
+    reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_title ()).get_properties ());
+      
+  // FIXME: auto-positioning should be disabled if the 
+  //        title has been positioned manually
+  if (! title_props.get_string ().empty ())
+    {
+      Matrix bb = props.get_boundingbox (true);
+      ColumnVector p = xform.untransform (bb(0)+bb(2)/2, (bb(1)-10),
+          (x_zlim(0)+x_zlim(1))/2, true);
+      title_props.set_position (p.extract_n(0, 3).transpose ());
+    }
+
+  set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max);
+
+  // Children
+
+  Matrix children = props.get_children ();
+  std::list<graphics_object> obj_list;
+  std::list<graphics_object>::iterator it;
+
+  // 1st pass: draw light objects
+
+  for (int i = 0; i < children.numel (); i++)
+    {
+      graphics_object go = gh_manager::get_object (children (i));
+
+      if (go.get_properties ().is_visible ())
+        {
+          if (go.isa ("light"))
+	    draw (go);
+          else
+            obj_list.push_back (go);
+        }
+    }
+
+  // 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
+
+  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
+}
+
+void
+opengl_renderer::draw (const line::properties& props)
+{
+  Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
+  Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
+  Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
+
+  bool has_z = (z.numel () > 0);
+  int n = static_cast<int> (::xmin (::xmin (x.numel (), y.numel ()), (has_z ? z.numel () : INT_MAX)));
+  octave_uint8 clip_mask = (props.is_clipping () ? 0x7F : 0x40), clip_ok (0x40);
+
+  std::vector<octave_uint8> 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 = (zmin+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"))
+    {
+      set_color (props.get_color_rgb ());
+      set_linestyle (props.get_linestyle (), false);
+      set_linewidth (props.get_linewidth ());
+
+      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;
+		      glBegin (GL_LINE_STRIP);
+		      glVertex3d (x(i-1), y(i-1), z(i-1));
+		    }
+		  glVertex3d (x(i), y(i), z(i));
+		}
+	      else if (flag)
+		{
+		  flag = false;
+		  glEnd ();
+		}
+	    }
+
+	  if (flag)
+	    glEnd ();
+	}
+      else
+	{
+	  bool flag = false;
+
+	  for (int i = 1; i < n; i++)
+	    {
+	      if ((clip[i-1] & clip[i]) == clip_ok)
+		{
+		  if (! flag)
+		    {
+		      flag = true;
+		      glBegin (GL_LINE_STRIP);
+		      glVertex2d (x(i-1), y(i-1));
+		    }
+		  glVertex2d (x(i), y(i));
+		}
+	      else if (flag)
+		{
+		  flag = false;
+		  glEnd ();
+		}
+	    }
+
+	  if (flag)
+	    glEnd ();
+	}
+	  
+      set_linewidth (0.5);
+      set_linestyle ("-");
+    }
+
+  set_clipping (false);
+
+  if (! props.marker_is ("none") &&
+      ! (props.markeredgecolor_is ("none")
+	 && props.markerfacecolor_is ("none")))
+    {
+      Matrix lc, fc;
+
+      if (props.markeredgecolor_is ("auto"))
+	lc = props.get_color_rgb ();
+      else if (! props.markeredgecolor_is ("none"))
+	lc = props.get_markeredgecolor_rgb ();
+
+      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), lc, fc);
+        }
+
+      end_marker ();
+    }
+
+  set_clipping (props.is_clipping ());
+}
+
+void
+opengl_renderer::draw (const surface::properties& props)
+{
+  Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
+  Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
+  Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
+
+  int zr = z.rows (), zc = z.columns ();
+
+  NDArray c;
+  NDArray n = props.get_vertexnormals ().array_value ();
+
+  // FIXME: handle transparency
+  Matrix a;
+
+  if (props.facelighting_is ("phong") || props.edgelighting_is ("phong"))
+    warning ("opengl_renderer::draw: phong light model not supported");
+
+  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") ? 1 : 2));
+  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") ? 1 : 2));
+  int ea_mode = (props.edgealpha_is_double () ? 0 :
+		 (props.edgealpha_is ("flat") ? 1 : 2));
+
+  Matrix fcolor = (fc_mode == 3 ? Matrix (1, 3, 1.0) : 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 ();
+  float cb[4] = { 0, 0, 0, 1 };
+
+  opengl_texture tex;
+
+  int i1, i2, j1, j2;
+  bool x_mat = (x.rows () == z.rows ());
+  bool y_mat = (y.columns () == z.columns ());
+
+  i1 = i2 = j1 = j2 = 0;
+
+  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 ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0)
+    c = props.get_color_data ().array_value ();
+
+  if (fa_mode > 0 || ea_mode > 0)
+    {
+      // FIXME: implement alphadata conversion
+      //a = props.get_alpha_data ();
+    }
+
+  if (fl_mode > 0 || el_mode > 0)
+    {
+      float buf[4] = { ss, ss, ss, 1 };
+
+      glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
+      glMaterialf (LIGHT_MODE, GL_SHININESS, se);
+    }
+
+  // FIXME: good candidate for caching, transfering pixel
+  // data to OpenGL is time consuming.
+  if (fc_mode == 3)
+    tex = opengl_texture::create (props.get_color_data ());
+
+  if (! props.facecolor_is ("none"))
+    {
+      if (props.get_facealpha_double () == 1)
+	{
+	  if (fc_mode == 0 || fc_mode == 3)
+	    {
+	      glColor3dv (fcolor.data ());
+	      if (fl_mode > 0)
+		{
+		  for (int i = 0; i < 3; i++)
+		    cb[i] = (as * fcolor(i));
+		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+		  for (int i = 0; i < 3; i++)
+		    cb[i] *= (ds / as);
+		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+		}
+	    }
+
+	  if (fl_mode > 0)
+	    glEnable (GL_LIGHTING);
+	  glShadeModel ((fc_mode == 2 || fl_mode == 2) ? GL_SMOOTH : GL_FLAT);
+	  set_polygon_offset (true, 1);
+	  if (fc_mode == 3)
+	    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 (x_mat)
+		    {
+		      j1 = j-1;
+		      j2 = j;
+		    }
+
+		  glBegin (GL_QUADS);
+
+		  // Vertex 1
+		  if (fc_mode == 3)
+		    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);
+		      glColor3fv (cb);
+
+		      if (fl_mode > 0)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= as;
+			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+			  
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= (ds / as);
+			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			}
+		    }
+		  if (fl_mode > 0)
+		    glNormal3d (n(j-1,i-1,0), n(j-1,i-1,1), n(j-1,i-1,2));
+		  glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
+
+		  // Vertex 2
+		  if (fc_mode == 3)
+		    tex.tex_coord (double (i) / (zc-1), double (j-1) / (zr-1));
+		  else if (fc_mode == 2)
+		    {
+		      for (int k = 0; k < 3; k++)
+			cb[k] = c(j-1, i, k);
+		      glColor3fv (cb);
+
+		      if (fl_mode > 0)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= as;
+			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+			  
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= (ds / as);
+			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			}
+		    }
+		  if (fl_mode == 2)
+		    glNormal3d (n(j-1,i,0), n(j-1,i,1), n(j-1,i,2));
+		  glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
+		  
+		  // Vertex 3
+		  if (fc_mode == 3)
+		    tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1));
+		  else if (fc_mode == 2)
+		    {
+		      for (int k = 0; k < 3; k++)
+			cb[k] = c(j, i, k);
+		      glColor3fv (cb);
+
+		      if (fl_mode > 0)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= as;
+			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+			  
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= (ds / as);
+			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			}
+		    }
+		  if (fl_mode == 2)
+		    glNormal3d (n(j,i,0), n(j,i,1), n(j,i,2));
+		  glVertex3d (x(j2,i), y(j,i2), z(j,i));
+		  
+		  // Vertex 4
+		  if (fc_mode == 3)
+		    tex.tex_coord (double (i-1) / (zc-1), double (j) / (zr-1));
+		  else if (fc_mode == 2)
+		    {
+		      for (int k = 0; k < 3; k++)
+			cb[k] = c(j, i-1, k);
+		      glColor3fv (cb);
+
+		      if (fl_mode > 0)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= as;
+			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+			  
+			  for (int k = 0; k < 3; k++)
+			    cb[k] *= (ds / as);
+			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			}
+		    }
+		  if (fl_mode == 2)
+		    glNormal3d (n(j,i-1,0), n(j,i-1,1), n(j,i-1,2));
+		  glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
+
+		  glEnd ();
+		}
+	    }
+
+	  set_polygon_offset (false);
+	  if (fc_mode == 3)
+	    glDisable (GL_TEXTURE_2D);
+
+	  if (fl_mode > 0)
+	    glDisable (GL_LIGHTING);
+	}
+      else
+	{
+	  // FIXME: implement transparency
+	}
+    }
+
+  if (! props.edgecolor_is ("none"))
+    {
+      if (props.get_edgealpha_double () == 1)
+	{
+	  if (ec_mode == 0)
+	    {
+	      glColor3dv (ecolor.data ());
+	      if (fl_mode > 0)
+		{
+		  for (int i = 0; i < 3; i++)
+		    cb[i] = (as * ecolor(i));
+		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+		  for (int i = 0; i < 3; i++)
+		    cb[i] *= (ds / as);
+		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+		}
+	    }
+
+	  if (el_mode > 0)
+	    glEnable (GL_LIGHTING);
+	  glShadeModel ((ec_mode == 2 || el_mode == 2) ? GL_SMOOTH : GL_FLAT);
+
+	  set_linestyle (props.get_linestyle (), false);
+	  set_linewidth (props.get_linewidth ());
+
+	  // 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 (x_mat)
+			{
+			  j1 = j-1;
+			  j2 = j;
+			}
+
+		      glBegin (GL_LINES);
+
+		      // Vertex 1
+		      if (ec_mode > 0)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] = c(j-1, i, k);
+			  glColor3fv (cb);
+
+			  if (fl_mode > 0)
+			    {
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= as;
+			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= (ds / as);
+			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			    }
+			}
+		      if (el_mode > 0)
+			glNormal3d (n(j-1,i,0), n(j-1,i,1), n(j-1,i,2));
+		      glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
+
+		      // Vertex 2
+		      if (ec_mode == 2)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] = c(j, i, k);
+			  glColor3fv (cb);
+
+			  if (fl_mode > 0)
+			    {
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= as;
+			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= (ds / as);
+			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			    }
+			}
+		      if (el_mode == 2)
+			glNormal3d (n(j,i,0), n(j,i,1), n(j,i,2));
+		      glVertex3d (x(j2,i), y(j,i2), z(j,i));
+
+		      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 (y_mat)
+			{
+			  i1 = i-1;
+			  i2 = i;
+			}
+
+		      glBegin (GL_LINES);
+
+		      // Vertex 1
+		      if (ec_mode > 0)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] = c(j, i-1, k);
+			  glColor3fv (cb);
+
+			  if (fl_mode > 0)
+			    {
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= as;
+			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= (ds / as);
+			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			    }
+			}
+		      if (el_mode > 0)
+			glNormal3d (n(j,i-1,0), n(j,i-1,1), n(j,i-1,2));
+		      glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
+		      
+		      // Vertex 2
+		      if (ec_mode == 2)
+			{
+			  for (int k = 0; k < 3; k++)
+			    cb[k] = c(j, i, k);
+			  glColor3fv (cb);
+
+			  if (fl_mode > 0)
+			    {
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= as;
+			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+			      for (int k = 0; k < 3; k++)
+				cb[k] *= (ds / as);
+			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+			    }
+			}
+		      if (el_mode == 2)
+			glNormal3d (n(j,i,0), n(j,i,1), n(j,i,2));
+		      glVertex3d (x(j2,i), y(j,i2), z(j,i));
+		      
+		      glEnd ();
+		    }
+		}
+	    }
+
+	  set_linestyle ("-");
+	  set_linewidth (0.5);
+
+	  if (el_mode > 0)
+	    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 = ! props.markeredgecolor_is ("none");
+      bool do_face = ! props.markerfacecolor_is ("none");
+
+      Matrix mecolor = props.get_markeredgecolor_rgb ();
+      Matrix mfcolor = props.get_markerfacecolor_rgb ();
+      Matrix cc (1, 3, 0.0);
+
+      if (mecolor.numel () == 0 && props.markeredgecolor_is ("auto"))
+	{
+	  mecolor = props.get_edgecolor_rgb ();
+	  do_edge = ! props.edgecolor_is ("none");
+	}
+
+      if (mfcolor.numel () == 0 && props.markerfacecolor_is ("auto"))
+	{
+	  mfcolor = props.get_facecolor_rgb ();
+	  do_face = ! props.facecolor_is ("none");
+	}
+
+      if ((mecolor.numel () == 0 || mfcolor.numel () == 0)
+	  && c.numel () == 0)
+	c = props.get_color_data ().array_value ();
+
+      init_marker (props.get_marker (), props.get_markersize (),
+		   props.get_linewidth ());
+
+      for (int i = 0; i < zc; i++)
+	{
+	  if (y_mat)
+	    i1 = i;
+	  
+	  for (int j = 0; j < zr; j++)
+	    {
+	      if (clip(j,i))
+		continue;
+
+	      if (x_mat)
+		j1 = j;
+
+	      if ((do_edge && mecolor.numel () == 0)
+		  || (do_face && mfcolor.numel () == 0))
+		{
+		  for (int k = 0; k < 3; k++)
+		    cc(k) = c(j,i,k);
+		}
+
+	      Matrix lc = (do_edge ? (mecolor.numel () == 0 ? cc : mecolor) : Matrix ());
+	      Matrix fc = (do_face ? (mfcolor.numel () == 0 ? cc : mfcolor) : Matrix ());
+
+	      draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc);
+	    }
+	}
+
+      end_marker ();
+    }
+}
+
+// FIXME: global optimization (rendering, data structures...), there
+// is probably a smarter/faster/less-memory-consuming way to do this.
+void
+opengl_renderer::draw (const patch::properties &props)
+{
+  Matrix f = props.get_faces ().matrix_value ();
+  Matrix v = xform.scale (props.get_vertices ().matrix_value ());
+  Matrix c;
+  Matrix n = props.get_vertexnormals ().matrix_value ();
+  Matrix a;
+
+  int nv = v.rows ();
+  // int vmax = v.columns ();
+  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_rgb () ? 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_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));
+
+  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 ();
+
+  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 (nf, 0);
+
+  for (int i = 0; i < nf; i++)
+    {
+      bool fclip = false;
+      int count = 0;
+
+      for (int j = 0; j < fcmax && ! xisnan (f(i,j)); j++, count++)
+	fclip = (fclip || clip(int (f(i,j) - 1)));
+
+      clip_f(i) = fclip;
+      count_f(i) = count;
+    }
+
+  if (fc_mode > 0 || ec_mode > 0)
+    {
+      c = props.get_color_data ().matrix_value ();
+
+      if (c.rows () == 1)
+	{
+	  // Single color specifications, we can simplify a little bit
+	  
+	  if (fc_mode > 0)
+	    {
+	      fcolor = c;
+	      fc_mode = 0;
+	    }
+
+	  if (ec_mode > 0)
+	    {
+	      ecolor = c;
+	      ec_mode = 0;
+	    }
+
+	  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 ()));
+    }
+
+  Array2<vertex_data> vdata (f.dims ());
+
+  for (int i = 0; i < nf; i++)
+    for (int j = 0; j < count_f(i); j++)
+      {
+	int idx = int (f(i,j) - 1);
+
+	Matrix vv (1, 3, 0.0);
+	Matrix cc;
+	Matrix nn(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);
+	// FIXME: uncomment when patch object has normal computation
+	//nn(0) = n(idx,0); nn(1) = n(idx,1); nn(2) = n(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 (a.numel () > 0)
+	  {
+	    if (has_facealpha)
+	      aa = a(i);
+	    else
+	      aa = a(idx);
+	  }
+
+	vdata(i,j) =
+	    vertex_data (vv, cc, nn, aa, as, ds, ss, se);
+      }
+
+  if (fl_mode > 0 || el_mode > 0)
+    {
+      float buf[4] = { ss, ss, ss, 1 };
+
+      glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
+      glMaterialf (LIGHT_MODE, GL_SHININESS, se);
+    }
+
+  if (! props.facecolor_is ("none"))
+    {
+      // FIXME: adapt to double-radio property
+      if (props.get_facealpha_double () == 1)
+	{
+	  if (fc_mode == 0)
+	    {
+	      glColor3dv (fcolor.data ());
+	      if (fl_mode > 0)
+		{
+		  float cb[4] = { 0, 0, 0, 1 };
+
+		  for (int i = 0; i < 3; i++)
+		    cb[i] = (as * fcolor(i));
+		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+		  for (int i = 0; i < 3; i++)
+		    cb[i] *= (ds / as);
+		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+		}
+	    }
+
+	  if (fl_mode > 0)
+	    glEnable (GL_LIGHTING);
+
+	  // FIXME: use __index__ property from patch object
+	  patch_tesselator tess (this, fc_mode, fl_mode, 0);
+
+	  for (int i = 0; i < nf; i++)
+	    {
+	      if (clip_f(i))
+		continue;
+
+	      tess.begin_polygon (true);
+	      tess.begin_contour ();
+
+	      for (int j = 0; j < count_f(i); j++)
+		{
+		  vertex_data::vertex_data_rep *vv = vdata(i,j).get_rep ();
+	
+		  tess.add_vertex (vv->coords.fortran_vec (), vv);
+		}
+
+	      tess.end_contour ();
+	      tess.end_polygon ();
+	    }
+
+	  if (fl_mode > 0)
+	    glDisable (GL_LIGHTING);
+	}
+      else
+	{
+	  // FIXME: implement transparency
+	}
+    }
+
+  if (! props.edgecolor_is ("none"))
+    {
+      // FIXME: adapt to double-radio property
+      if (props.get_edgealpha_double () == 1)
+	{
+	  if (ec_mode == 0)
+	    {
+	      glColor3dv (ecolor.data ());
+	      if (el_mode > 0)
+		{
+		  float cb[4] = { 0, 0, 0, 1 };
+
+		  for (int i = 0; i < 3; i++)
+		    cb[i] = (as * ecolor(i));
+		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+
+		  for (int i = 0; i < 3; i++)
+		    cb[i] *= (ds / as);
+		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+		}
+	    }
+
+	  if (el_mode > 0)
+	    glEnable (GL_LIGHTING);
+
+	  set_linestyle (props.get_linestyle (), false);
+	  set_linewidth (props.get_linewidth ());
+
+	  // FIXME: use __index__ property from patch object; should we
+	  // offset patch contour as well?
+	  patch_tesselator tess (this, ec_mode, el_mode);
+
+	  for (int i = 0; i < nf; i++)
+	    {
+	      if (clip_f(i))
+		continue;
+
+	      tess.begin_polygon (false);
+	      tess.begin_contour ();
+
+	      for (int j = 0; j < count_f(i); j++)
+		{
+		  vertex_data::vertex_data_rep *vv = vdata(i,j).get_rep ();
+	
+		  tess.add_vertex (vv->coords.fortran_vec (), vv);
+		}
+
+	      tess.end_contour ();
+	      tess.end_polygon ();
+	    }
+
+	  set_linestyle ("-");
+	  set_linewidth (0.5);
+
+	  if (el_mode > 0)
+	    glDisable (GL_LIGHTING);
+	}
+      else
+	{
+	  // FIXME: implement transparency
+	}
+    }
+
+  if (! props.marker_is ("none") &&
+      ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none")))
+    {
+      bool do_edge = ! props.markeredgecolor_is ("none");
+      bool do_face = ! props.markerfacecolor_is ("none");
+
+      Matrix mecolor = props.get_markeredgecolor_rgb ();
+      Matrix mfcolor = props.get_markerfacecolor_rgb ();
+      Matrix cc (1, 3, 0.0);
+
+      if (mecolor.numel () == 0 && props.markeredgecolor_is ("auto"))
+	{
+	  mecolor = props.get_edgecolor_rgb ();
+	  do_edge = ! props.edgecolor_is ("none");
+	}
+
+      if (mfcolor.numel () == 0 && props.markerfacecolor_is ("auto"))
+	{
+	  mfcolor = props.get_facecolor_rgb ();
+	  do_face = ! props.facecolor_is ("none");
+	}
+
+      init_marker (props.get_marker (), props.get_markersize (),
+		   props.get_linewidth ());
+
+      for (int i = 0; i < nf; i++)
+	for (int j = 0; j < count_f(i); j++)
+	  {
+	    int idx = int (f(i,j) - 1);
+
+	    if (clip(idx))
+	      continue;
+
+	    Matrix lc = (do_edge ? (mecolor.numel () == 0 ?
+				    vdata(i,j).get_rep ()->color : mecolor)
+			 : Matrix ());
+	    Matrix fc = (do_face ? (mfcolor.numel () == 0 ?
+				    vdata(i,j).get_rep ()->color : mfcolor)
+			 : Matrix ());
+
+	    draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc);
+	  }
+
+      end_marker ();
+    }
+}
+
+void
+opengl_renderer::draw (const hggroup::properties &props)
+{
+  draw (props.get_children ());
+}
+
+void
+opengl_renderer::set_viewport (int w, int h)
+{
+  glViewport (0, 0, w, h);
+}
+
+void
+opengl_renderer::set_color (const Matrix& c)
+{
+  glColor3dv (c.data ());
+}
+
+void
+opengl_renderer::set_polygon_offset (bool on, double offset)
+{
+  if (on)
+    {
+      glPolygonOffset (offset, offset);
+      glEnable (GL_POLYGON_OFFSET_FILL);
+    }
+  else
+    glDisable (GL_POLYGON_OFFSET_FILL);
+}
+
+void
+opengl_renderer::set_linewidth (float w)
+{
+  glLineWidth (w);
+}
+
+void
+opengl_renderer::set_linestyle (const std::string& s, bool use_stipple)
+{
+  bool solid = false;
+
+  if (s == "-")
+    {
+      glLineStipple (1, static_cast<unsigned short> (0xFFFF));
+      solid = true;
+    }
+  else if (s == ":")
+    glLineStipple (1, static_cast<unsigned short> (0x8888));
+  else if (s == "--")
+    glLineStipple (1, static_cast<unsigned short> (0x0FFF));
+  else if (s == "-.")
+    glLineStipple (1, static_cast<unsigned short> (0x020F));
+  else
+    glLineStipple (1, static_cast<unsigned short> (0x0000));
+
+  if (solid && ! use_stipple)
+    glDisable (GL_LINE_STIPPLE);
+  else
+    glEnable (GL_LINE_STIPPLE);
+}
+
+void
+opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2,
+			      double z1, double 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;
+  glClipPlane (GL_CLIP_PLANE0, p.data ());
+  p(0) = 1; p(3) = -x1;
+  glClipPlane (GL_CLIP_PLANE1, p.data ());
+  p(0) = 0; p(1) = -1; p(3) = y2;
+  glClipPlane (GL_CLIP_PLANE2, p.data ());
+  p(1) = 1; p(3) = -y1;
+  glClipPlane (GL_CLIP_PLANE3, p.data ());
+  p(1) = 0; p(2) = -1; p(3) = z2;
+  glClipPlane (GL_CLIP_PLANE4, p.data ());
+  p(2) = 1; p(3) = -z1;
+  glClipPlane (GL_CLIP_PLANE5, p.data ());
+
+  xmin = x1; xmax = x2;
+  ymin = y1; ymax = y2;
+  zmin = z1; zmax = z2;
+}
+
+void
+opengl_renderer::set_clipping (bool enable)
+{
+  bool has_clipping = (glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
+
+  if (enable != has_clipping)
+    {
+      if (enable)
+        for (int i = 0; i < 6; i++)
+          glEnable (GL_CLIP_PLANE0+i);
+      else
+        for (int i = 0; i < 6; i++)
+          glDisable (GL_CLIP_PLANE0+i);
+    }
+}
+
+void
+opengl_renderer::init_marker (const std::string& m, double size, float width)
+{
+  int vw[4];
+
+  glGetIntegerv (GL_VIEWPORT, vw);
+
+  glMatrixMode (GL_PROJECTION);
+  glPushMatrix ();
+  glLoadIdentity ();
+  glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+  glMatrixMode (GL_MODELVIEW);
+  glPushMatrix ();
+
+  set_clipping (false);
+  set_linewidth (width);
+
+  marker_id = make_marker_list (m, size, false);
+  filled_marker_id = make_marker_list (m, size, true);
+}
+
+void
+opengl_renderer::end_marker (void)
+{
+  glDeleteLists (marker_id, 1);
+  glDeleteLists (filled_marker_id, 1);
+
+  glMatrixMode (GL_MODELVIEW);
+  glPopMatrix ();
+  glMatrixMode (GL_PROJECTION);
+  glPopMatrix ();
+  set_linewidth (0.5f);
+}
+
+void
+opengl_renderer::draw_marker (double x, double y, double z,
+			      const Matrix& lc, const Matrix& fc)
+{
+  ColumnVector tmp = xform.transform (x, y, z, false);
+  
+  glLoadIdentity ();
+  glTranslated (tmp(0), tmp(1), -tmp(2));
+
+  if (fc.numel () > 0)
+    {
+      glColor3dv (fc.data ());
+      set_polygon_offset (true, 1.0);
+      glCallList (filled_marker_id);
+      set_polygon_offset (false);
+    }
+
+  if (lc.numel () > 0)
+    {
+      glColor3dv (lc.data ());
+      glCallList (marker_id);
+    }
+}
+
+unsigned int
+opengl_renderer::make_marker_list (const std::string& marker, double size,
+				   bool filled) const
+{
+  unsigned int ID = glGenLists (1);
+  double sz = size * backend.get_screen_resolution () / 72.0;
+
+  // constants for the * marker
+  const double sqrt2d4 = 0.35355339059327;
+  double tt = sz*sqrt2d4;
+
+  glNewList (ID, GL_COMPILE);
+
+  switch (marker[0])
+    {
+    case '+':
+      glBegin (GL_LINES);
+      glVertex2f (-sz/2 ,0     );
+      glVertex2f (sz/2  ,0     );
+      glVertex2f (0     ,-sz/2 );
+      glVertex2f (0     ,sz/2  );
+      glEnd ();
+      break;
+    case 'x':
+      glBegin(GL_LINES);
+      glVertex2f (-sz/2 ,-sz/2);
+      glVertex2f (sz/2  ,sz/2 );
+      glVertex2f (-sz/2 ,sz/2 );
+      glVertex2f (sz/2 ,-sz/2 );
+      glEnd ();
+      break;
+    case '*':
+      glBegin (GL_LINES);
+      glVertex2f (-sz/2 ,0     );
+      glVertex2f (sz/2  ,0     );
+      glVertex2f (0     ,-sz/2 );
+      glVertex2f (0     ,sz/2  );
+      glVertex2f (-tt   ,-tt   );
+      glVertex2f (+tt   ,+tt   );
+      glVertex2f (-tt   ,+tt   );
+      glVertex2f (+tt   ,-tt   );
+      glEnd ();
+      break;
+    case '.':
+      glBegin (GL_POLYGON);
+      glVertex2f (-sz/10, -sz/10);
+      glVertex2f (-sz/10, sz/10 );
+      glVertex2f (sz/10 , sz/10 );
+      glVertex2f (sz/10 , -sz/10);
+      glEnd ();
+      break;
+    case 's':
+      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+      glVertex2d (-sz/2, -sz/2);
+      glVertex2d (-sz/2,  sz/2);
+      glVertex2d ( sz/2,  sz/2);
+      glVertex2d ( sz/2, -sz/2);
+      glEnd();
+      break;
+    case 'o':
+      {
+	double ang_step = M_PI / 5;
+
+	glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+	for (double ang = 0; ang < (2*M_PI); ang += ang_step)
+	  glVertex2d (sz*cos(ang)/2, sz*sin(ang)/2);
+	glEnd ();
+      }
+      break;
+    case 'd':
+      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+      glVertex2d (    0, -sz/2);
+      glVertex2d ( sz/2,     0);
+      glVertex2d (    0,  sz/2);
+      glVertex2d (-sz/2,     0);
+      glEnd();
+      break;
+    case '^':
+      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+      glVertex2f (0     ,  sz/2);
+      glVertex2f (sz/2  , -sz/2);
+      glVertex2f (-sz/2 , -sz/2);
+      glEnd ();
+      break;
+    case 'v':
+      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+      glVertex2f (0     ,-sz/2);
+      glVertex2f (-sz/2 ,sz/2 );
+      glVertex2f (sz/2  ,sz/2 );
+      glEnd ();
+      break;
+    case '>':
+      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+      glVertex2f (sz/2  ,0    );
+      glVertex2f (-sz/2 ,sz/2 );
+      glVertex2f (-sz/2 ,-sz/2);
+      glEnd ();
+      break;
+    case '<':
+      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
+      glVertex2f (-sz/2 ,0    );
+      glVertex2f (sz/2  ,-sz/2);
+      glVertex2f (sz/2  ,sz/2 );
+      glEnd ();
+    default:
+      warning ("opengl_renderer: unsupported marker `%s'",
+	       marker.c_str ());
+      break;
+    }
+
+  glEndList ();
+
+  return ID;
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gl-render.h	Thu Jun 05 18:00:37 2008 -0400
@@ -0,0 +1,138 @@
+/*
+
+Copyright (C) 2008 Michael Goffioul
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if !defined (gl_render_h)
+#define gl_render_h 1
+
+#include "config.h"
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#include "graphics.h"
+
+class
+OCTGRAPHICS_API
+opengl_renderer
+{
+public:
+  opengl_renderer (void) { }
+
+  virtual ~opengl_renderer (void) { }
+
+  virtual void draw (const graphics_handle& h)
+    { draw (gh_manager::get_object (h)); }
+
+  virtual void draw (const graphics_object& go);
+
+  virtual void draw (const Matrix& hlist)
+    {
+      int len = hlist.length ();
+
+      for (int i = 0; i < len; i++)
+	{
+	  graphics_handle h = gh_manager::lookup (hlist(i));
+
+	  if (h.ok ())
+	    draw (h);
+	}
+    }
+
+  virtual void set_viewport (int w, int h);
+
+protected:
+  virtual void draw (const figure::properties& props);
+  virtual void draw (const axes::properties& props);
+  virtual void draw (const line::properties& props);
+  virtual void draw (const surface::properties& props);
+  virtual void draw (const patch::properties& props);
+  virtual void draw (const hggroup::properties& props);
+
+  virtual void set_color (const Matrix& c);
+  virtual void set_polygon_offset (bool on, double offset = 0.0);
+  virtual void set_linewidth (float w);
+  virtual void set_linestyle (const std::string& s, bool stipple = false);
+  virtual void set_clipbox (double x1, double x2, double y1, double y2,
+			    double z1, double z2);
+  virtual void set_clipping (bool on);
+
+  virtual void init_marker (const std::string& m, double size, float width);
+  virtual void end_marker (void);
+  virtual void draw_marker (double x, double y, double z,
+			    const Matrix& lc, const Matrix& fc);
+
+private:
+  opengl_renderer (const opengl_renderer&) { }
+
+  opengl_renderer& operator = (const opengl_renderer&)
+    { return *this; }
+
+  bool is_nan_or_inf (double x, double y, double z) const
+    {
+      return (xisnan (x) || xisnan (y) || xisnan (z)
+	      || xisinf (x) || xisinf (y) || xisinf (z));
+    }
+
+  octave_uint8 clip_code (double x, double y, double z) const
+    {
+      return ((x < xmin ? 1 : 0)
+	      | (x > xmax ? 1 : 0) << 1
+	      | (y < ymin ? 1 : 0) << 2
+	      | (y > ymax ? 1 : 0) << 3
+	      | (z < zmin ? 1 : 0) << 4
+	      | (z > zmax ? 1 : 0) << 5
+	      | (is_nan_or_inf (x, y, z) ? 0 : 1) << 6);
+    }
+
+  unsigned int make_marker_list (const std::string& m, double size,
+				 bool filled) const;
+
+private:
+  // the backend associated with the figure being rendered
+  graphics_backend backend;
+
+  // axes transformation data
+  graphics_xform xform;
+
+  // axis limits in model scaled coordinate
+  double xmin, xmax;
+  double ymin, ymax;
+  double zmin, zmax;
+
+  // Z projection limits in windows coordinate
+  double xZ1, xZ2;
+
+  // call lists identifiers for markers
+  unsigned int marker_id, filled_marker_id;
+
+  // camera information for primitive sorting
+  ColumnVector camera_pos, camera_dir;
+
+private:
+  class patch_tesselator;
+};
+
+#endif
--- a/src/graphics/ChangeLog	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,182 +0,0 @@
-2008-06-05  John W. Eaton  <jwe@octave.org>
-
-	* opengl/gl-render.cc (xmin): New static function.
-
-	* opengl/gl-render.h (opengl_renderer): Style fixes.
-	* fltk_backend/fltk_backend.cc: Style fixes.
-
-2008-04-26  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.h (opengl_renderer::draw(hggroup)): New method.
-	* opengl/gl-render.cc (opengl_renderer::draw(hggroup)): Likewise.
-	(opengl_renderer::draw(graphics_object)): Call it.
-
-2008-03-17  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (plot_window::resize,
-	plot_window::draw): make canvas the size of figure.position
-
-2008-03-09  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (plot_window::handle): add zoom
-	stack
-
-2008-03-06  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (plot_window::handle): Add handling
-	of the 'a' and 'g' keys
-	(plot_window: toggle_grid): New helper function
-	(plot_window): Add new togglegrid button
-
-2008-03-01  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (OpenGL_fltk::draw_overlay,
-	OpenGL_fltk::zoom, OpenGL_fltk::set_zoom_box): Added to support
-	zoom box
-	(plot_window::handle): Added zoom box code, B-3 now does autoscale
-	(plot_window::axis_auto): New utility function to call axis("auto")
-	(plot_window::button_press): "A" button now does autoscale
-
-	* opengl/gl-render.cc (make_marker_list): Add the "+x*.^v><"
-	markers
-
-2008-02-27  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.cc (opengl_renderer::draw(patch),
-	opengl_renderer::draw(surface)): Adapt to type change of facealpha and
-	edgealpha, using double_radio_property class.
-
-2008-02-26  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.h: Make sure windows.h is included before OpenGL
-	headers.
-	* fltk_backend/Makefile.in (FLTK_EXTRA_CXXFLAGS): Use $(srcdir) for
-	includesion of OpenGL renderer headers.
-	(Makeconf): Move inclusion of Makeconf later on, to avoid freezing
-	MinGW make.
-
-	* fltk_backend/Makefile.in (GRAPHICS_CFLAGS): New substituted
-	variable.
-
-2008-02-25  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (class plot_window): Many changes
-	to use figure::properties instead of figure handle to reference
-	the figure
-	(class figure_manager): ditto
-	(__fltk_redraw__): moved most of functionality into the
-	figure_manager class
-	(plot_window::pixel2pos): Modified to use axes::pixel2coord
-	(plot_window::pixel2staus): Modified to use pixel2pos
-	(plot_window::handle): Added zoom with mouse 
-
-2008-02-24  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (OpenGL_fltk::Draw): removed double
-	buffer switch
-	(OpenGL_fltk::setup_viewport): removed call to glOrtho --
-	gl-render takes care of all the transformations
-
-2008-02-23  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/fltk_backend.cc (plot_window::mark_modifed): mark
-	the whole window as damaged (otherwise changing figure.postion
-	does not have immediate effect)
-	(plot_window::draw): New function, checks for window size 
-	(__fltk_maxtime__): New DEFUN to allow tweaking of fltk timeout
-	(__fltk_redraw__): Use fltk_maxtime as timeout
-
-2008-02-21  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.cc (opengl_renderer::patch_tesselator::combine):
-	Protect against NULL vertex data.
-
-	* opengl/gl-render.cc (opengl_renderer::draw(patch)): Add marker
-	rendering of patch objects.
-
-2008-02-21  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* opengl/gl-render.cc: remove OpenGL includes
-	* opengl/gl-render.h: add OpenGL includes
-	* fltk_backend/fltk_backend.cc: remove OpenGL includes
-	(__fltk_redraw__): put figure handle into the figure's
-	__plot_stream__ property for later
-	(fltk_backend::close_figure): use argument as figure handle to
-	close
-
-2008-02-20  Shai Ayal  <shaiay@users.sourceforge.net>
-
-	* fltk_backend/Makefile.in: initial import
-
-	* fltk_backend/fltk_backend.cc: initial import
-	
-
-2008-02-20  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.h (opengl_renderer::draw(patch)): New method to
-	render patch objects.
-	(class opengl_renderer::patch_tesselator): Forward declaration.
-	* opengl/gl-render.cc (opengl_texture::create): Use RGB data format
-	instead of RGBA.
-	(class opengl_tesselator): New classes to abstract GLU tessellation
-	process.
-	(class opengl_renderer::patch_tesselator): New class to render opaque
-	patch objects.
-	(class vertex_data): New class to hold vertex data during tessellation
-	of patch objects.
-	(opengl_renderer::draw(patch)): New method to render patch objects (no
-	transparency, no border, no marker yet).
-	(opengl_renderer::draw(graphics_object)): Dispatch to it.
-
-	* opengl/gl-render.cc (opengl_renderer::draw(patch)): Use patch color
-	data and support face/vertex single color specification.
-
-	* opengl/gl-render.cc (opengl_tesselator::begin_polygon): Set
-	tessellation property also for non-filled polygons.
-	(opengl_renderer::patch_tesselator::vertex): Protect against empty
-	color matrices.
-	(opengl_renderer::draw(patch)): Render patch border (no transparency
-	yet).
-
-2008-02-19  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.cc (opengl_texture::texture_rep::tex_coord,
-	opengl_texture::tex_coord): New wrapper around glTexCoord2d.
-	(opengl_renderer::draw(surface)): Use it for texturemap
-	implementation.
-	(opengl_renderer::draw(surface)): Fix indexing bug when creating clip
-	matrix.
-	(opengl_texture::operator=): Add assignment operator.
-	(opengl_texture::create): New static opengl_texture creator.
-	(opengl_texture::is_valid): New accessor.
-
-2008-02-18  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.cc (class opengl_texture): New class to wrap
-	texture operations in OpenGL.
-
-2008-02-17  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.cc (opengl_renderer::draw(surface)): Set material
-	color when rendering surface facets.
-
-	* opengl/gl-render.cc (opengl_renderer::draw(surface)): Add rendering
-	of mesh and markers.
-
-2008-02-16  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.cc (opengl_renderer::draw(figure)): Initialize the
-	OpenGL context correctly.
-	(opengl_renderer::draw(surface)): Add missing glEnd call.
-
-2008-02-14  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* opengl/gl-render.h opengl/gl-render.cc: Add rendering
-	interface for surface objects (actual implement still
-	missing).
-
-2008-06-04  Michael Goffioul  <michael.goffioul@gmail.com>
-
-	* Makefile.in Makerules.in: Initial import
-	* opengl/Makefile.in: Likewise.
-	* opengl/gl-render.h opengl/gl-render.cc: Likewise.
--- a/src/graphics/Makefile.in	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-# Makefile for octave's src/graphics directory
-#
-# Copyright (C) 2008 John W. Eaton
-#
-# This file is part of Octave.
-# 
-# Octave is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 3 of the License, or (at
-# your option) any later version.
-# 
-# Octave is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with Octave; see the file COPYING.  If not, see
-# <http://www.gnu.org/licenses/>.
-
-TOPDIR = ../..
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-
-include $(TOPDIR)/Makeconf
-
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_DATA = @INSTALL_DATA@
-
-GRAPHICS_DIRS = @GRAPHICS_OPENGL@
-
-SUBDIRS = $(GRAPHICS_DIRS)
-
-DISTSUBDIRS = $(GRAPHICS_DIRS)
-
-CLEAN_SUBDIRS = $(DISTSUBDIRS)
-
-DISTFILES = Makefile.in Makerules.in
-
-all: $(SUBDIRS)
-.PHONY: all
-
-$(SUBDIRS):
-	$(MAKE) -C $@ all
-.PHONY: $(SUBDIRS)
-
-check: all
-.PHONY: check
-
-install install-strip uninstall::
-	@$(subdir-for-command)
-
-clean mostlyclean distclean maintainer-clean::
-	@$(foreach d, $(CLEAN_SUBDIRS), $(do-subdir-for-command))
-
-install-strip::
-	$(MAKE) INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" install
-
-tags TAGS:: $(SOURCES)
-	$(SUBDIR_FOR_COMMAND)
-
-dist:
-	for dir in $(DISTSUBDIRS); do mkdir $(TOPDIR)/`cat $(TOPDIR)/.fname`/src/graphics/$$dir; $(MAKE) -C $$dir $@; done
-	ln $(addprefix $(srcdir)/, $(DISTFILES)) $(TOPDIR)/`cat $(TOPDIR)/.fname`/src/graphics
-.PHONY: dist
-
-.NOTPARALLEL:
--- a/src/graphics/Makerules.in	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-# Makefile for octave's src/graphics directory
-#
-# Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-#               2002, 2003, 2004, 2005, 2006, 2007 John W. Eaton
-#
-# This file is part of Octave.
-# 
-# Octave is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 3 of the License, or (at
-# your option) any later version.
-# 
-# Octave is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with Octave; see the file COPYING.  If not, see
-# <http://www.gnu.org/licenses/>.
-
-include $(TOPDIR)/Makeconf
-
-DLL_CDEFS = @OCTGRAPHICS_DLL_DEFS@
-DLL_CXXDEFS = @OCTGRAPHICS_DLL_DEFS@
-
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_DATA = @INSTALL_DATA@
-
-PT_FLAGS = -fexternal-templates -fno-implicit-templates
-CXXFLAGS_NO_PT_FLAGS = $(filter-out $(PT_FLAGS), $(ALL_CXXFLAGS))
-
-OCTGRAPHICS_INCS = $(GRAPHICS_INCS)
-
-OCTGRAPHICS_SRC = $(GRAPHICS_SRC)
-
-OCTGRAPHICS_BASE = $(basename $(notdir $(OCTGRAPHICS_SRC)))
-
-OCTGRAPHICS_OBJ = $(addsuffix .o, $(OCTGRAPHICS_BASE))
-
-ifeq ($(SHARED_LIBS), true)
-  ifdef CXXPICFLAG
-    OCTGRAPHICS_PICOBJ := $(addprefix pic/, $(OCTGRAPHICS_OBJ))
-  else
-    OCTGRAPHICS_PICOBJ := $(OCTGRAPHICS_OBJ)
-  endif
-endif
-
-SOURCES := $(OCTGRAPHICS_SRC)
-
-# Ugh.
-
-DEP_1 := $(OCTGRAPHICS_SRC)
-MAKEDEPS := $(patsubst %.cc, %.d, $(DEP_1))
-
-OCTGRAPHICS_LINK_DEPS = \
-  -L$(TOPDIR)/src $(LIBOCTINTERP) -L$(TOPDIR)/liboctave $(LIBOCTAVE) \
-  -L$(TOPDIR)/libcruft $(LIBCRUFT) $(GRAPHICS_EXTRA_LIBS) $(LIBS) $(FLIBS)
-
-DISTFILES = $(OCTGRAPHICS_SRC) $(OCTGRAPHICS_INCS) \
-	$(addprefix $(srcdir)/, Makefile.in $(SPECIAL))
-
-CWD = $(shell pwd)
-THISDIR = $(notdir $(CWD))
-
-ifeq ($(SHARED_LIBS), true)
-  ifeq ($(STATIC_LIBS), true)
-    LIBRARIES = $(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT) $(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT_VER)
-  else
-    LIBRARIES = $(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT_VER)
-  endif
-else
-  ifeq ($(STATIC_LIBS), true)
-    LIBRARIES = $(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT)
-  else
-    ## This is not going to work, but hey, you asked for it...
-    LIBRARIES =
-  endif
-endif
-
-all: $(LIBRARIES)
-.PHONY: all
-
-objects: $(OCTGRAPHICS_OBJ)
-
-libraries: $(LIBRARIES)
-.PHONY: libraries
-
-$(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT): $(OCTGRAPHICS_OBJ)
-	rm -f $@
-	$(TEMPLATE_AR) $(TEMPLATE_ARFLAGS) $@ $^
-	$(RANLIB) $@
-
-$(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT_VER): $(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT)
-	rm -f $@
-	$(LN_S) $< $@
-
-$(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT): $(OCTGRAPHICS_PICOBJ)
-	rm -f $@
-	$(SH_LD) $(SH_LDFLAGS) $(SONAME_FLAGS) -o $@ $^ $(OCTGRAPHICS_LINK_DEPS)
-
-stmp-pic: pic
-	@if [ -f stmp-pic ]; then \
-	  true; \
-	else \
-	  echo "touch stmp-pic"; \
-	  touch stmp-pic; \
-	fi
-
-pic:
-	@if [ -d pic ]; then \
-	  true; \
-	else \
-	  echo "mkdir pic"; \
-	  mkdir pic; \
-	fi
-
-$(OCTGRAPHICS_PICOBJ): stmp-pic
-
-PREREQ := $(GRAPHICS_PREREQ)
-
-$(MAKEDEPS): $(PREREQ)
-
-check: all
-.PHONY: check
-
-install: install-bin install-lib install-inc
-.PHONY: install
-
-install-strip:
-	$(MAKE) INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" install
-.PHONY: install-strip
-
-install-bin:
-.PHONY: install-bin
-
-install-lib:
-	$(top_srcdir)/mkinstalldirs $(DESTDIR)$(octlibdir)
-	if $(STATIC_LIBS); then \
-	  rm -f $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT); \
-	  $(INSTALL_DATA) $(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT) \
-	    $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT); \
-	  $(RANLIB) $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT); \
-	fi
-	if $(SHARED_LIBS); then \
-	  rm -f $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT_VER); \
-	  $(INSTALL) $(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB) \
-	    $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB_VER); \
-	  rm -f $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB); \
-	  (cd $(DESTDIR)$(octlibdir) ; $(LN_S) $(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB_VER) $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB)); \
-	  if  test x$(SHLBIN) != x ; then \
-	    rm -f $(DESTDIR)$(bindir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLBIN); \
-	    $(INSTALL_PROGRAM) \
-	      $(LIBPRE)$(GRAPHICS_NAME).$(SHLBIN) $(DESTDIR)$(bindir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLBIN); \
-	  fi; \
-	fi
-.PHONY: install-lib
-
-install-inc:
-	$(top_srcdir)/mkinstalldirs $(DESTDIR)$(octincludedir)/octave
-	for f in $(OCTGRAPHICS_INCS); do \
-	  rm -f $(DESTDIR)$(octincludedir)/octave/$$f; \
-	  if [ -f $$f ]; then \
-	    $(INSTALL_DATA) $$f $(DESTDIR)$(octincludedir)/octave/$$f; \
-	  else \
-	    $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(octincludedir)/octave/$$f; \
-	  fi ; \
-	done
-.PHONY: install-inc
-
-uninstall:
-	rm -f $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT)
-	rm -f $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB)
-	rm -f $(DESTDIR)$(octlibdir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLLIB_VER)
-	if test x$(SHLBIN) != x ; then \
-	  rm -f $(DESTDIR)$(bindir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLBIN); \
-	  rm -f $(DESTDIR)$(bindir)/$(LIBPRE)$(GRAPHICS_NAME).$(SHLBIN_VER); \
-	fi
-	for f in $(OCTGRAPHICS_INCS); do \
-	  rm -f $(DESTDIR)$(octincludedir)/octave/$$f; \
-	done
-	-rmdir $(DESTDIR)$(octincludedir)/octave
-	-rmdir $(DESTDIR)$(octincludedir)
-	-rmdir $(DESTDIR)$(octlibdir)
-	-rmdir $(DESTDIR)$(octfiledir)
-.PHONY: uninstall
-
-tags: $(SOURCES) $(DLD_SRC)
-	ctags $(SOURCES) $(DLD_SRC)
-
-TAGS: $(SOURCES) $(DLD_SRC)
-	etags $(SOURCES) $(DLD_SRC)
-
-clean mostlyclean distclean maintainer-clean::
-	rm -f $(MAKEDEPS) $(OCTGRAPHICS_OBJ) $(OCTGRAPHICS_PICOBJ)
-	-rmdir pic
-	rm -f stmp-pic
-.PHONY: clean mostlyclean
-
-clean::
-	rm -f $(LIBPRE)$(GRAPHICS_NAME).$(LIBEXT)
-	rm -f $(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT_VER) $(LIBPRE)$(GRAPHICS_NAME).$(SHLEXT)
-
-distclean maintainer-clean::
-	rm -f Makefile
-.PHONY: distclean
-	
-maintainer-clean::
-	rm -f tags TAGS
-.PHONY: maintainer-clean
-
-dist:
-	ln $(EXTERNAL_DISTFILES) $(TOPDIR)/`cat $(TOPDIR)/.fname`/src/graphics/$(THISDIR)
-.PHONY: dist
-
-ifdef omit_deps
-.PHONY: $(MAKEDEPS)
-endif
-
--include $(MAKEDEPS)
--- a/src/graphics/fltk_backend/Makefile.in	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-# Makefile for octave's src/graphics/opengl directory
-#
-# Copyright (C) 1998, 2007 John W. Eaton
-#
-# This file is part of Octave.
-# 
-# Octave is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 3 of the License, or (at
-# your option) any later version.
-# 
-# Octave is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with Octave; see the file COPYING.  If not, see
-# <http://www.gnu.org/licenses/>.
-
-TOPDIR = ../../..
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-
-GRAPHICS_LIBS = @GRAPHICS_LIBS@
-GRAPHICS_CFLAGS = @GRAPHICS_CFLAGS@
-
-include $(TOPDIR)/Makeconf
-
-FLTK_EXTRA_LIBS = $(OPENGL_LIBS) $(GRAPHICS_LIBS)
-FLTK_EXTRA_CXXFLAGS = $(GRAPHICS_CFLAGS) -I$(srcdir)/../opengl
-
-FLTK_NAME = fltk_backend
-
-FLTK_SRC := fltk_backend.cc
-FLTK_OBJ := $(patsubst %.cc, %.o, $(FLTK_SRC))
-
-%.o : %.cc
-	$(CXX) -c $(CPPFLAGS) $(ALL_CXXFLAGS) $(FLTK_EXTRA_CXXFLAGS) $< -o $@
-
-# Some stupid egreps don't like empty elements in alternation patterns,
-# so we have to repeat ourselves because some stupid egreps don't like
-# empty elements in alternation patterns.
-
-DEFUN_PATTERN = "^[ \t]*DEF(CONSTFUN|CMD|UN|UN_DLD|UNX_DLD|UN_TEXT|UN_MAPPER)[ \t]*\\("
-
-DLD_DEF_FILES := $(patsubst %.cc, %.df, $(FLTK_SRC))
-
-OCTAVE_LFLAGS = -L$(TOPDIR)/liboctave -L$(TOPDIR)/libcruft \
-  -L$(TOPDIR)/src $(RLD_FLAG)
-
-ifeq ($(ENABLE_DYNAMIC_LINKING), true)
-  OCTAVE_LIBS = $(LIBOCTINTERP) $(LIBOCTAVE) \
-    $(SPECIAL_MATH_LIB) $(LIBCRUFT) \
-    $(LIBPLPLOT) $(LIBGLOB)
-else
-  OCTAVE_LIBS = $(LIBOCTINTERP) $(LIBOCTAVE) $(QHULL_LIBS) \
-    $(GLPK_LIBS) $(REGEX_LIBS) $(SPECIAL_MATH_LIB) $(LIBCRUFT) \
-    $(LIBPLPLOT) $(LIBGLOB)
-endif
-
-OCT_LINK_DEPS = \
-  -L../../../libcruft $(LIBCRUFT) -L../../../liboctave $(LIBOCTAVE) \
-  -L../../ $(LIBOCTINTERP) -L../opengl -loctgraphics_gl $(CHOLMOD_LIBS) $(UMFPACK_LIBS) $(AMD_LIBS) \
-   $(CAMD_LIBS) $(COLAMD_LIBS) $(CCOLAMD_LIBS) $(CXSPARSE_LIBS) $(BLAS_LIBS) \
-   $(FFTW_LIBS) $(LIBS) $(FLIBS) $(FLTK_EXTRA_LIBS) $(GRAPHICS_LIBS)
-
-
-fltk_backend.oct : fltk_backend.o
-	$(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) 
-
-test_stuff.oct : test_stuff.o
-	$(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) 
-
-all : fltk_backend.oct
--- a/src/graphics/fltk_backend/fltk_backend.cc	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,807 +0,0 @@
-/*
-
-Copyright (C) 2007 Shai Ayal
-
-This file is part of Octave.
-
-Octave is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 3 of the License, or (at your
-option) any later version.
-
-Octave is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with Octave; see the file COPYING.  If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#include <map>
-#include <set>
-#include <sstream>
-#include <iostream>
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "gl-render.h"
-
-#include <FL/Fl.H>
-#include <FL/Fl_Window.H>
-#include <FL/Fl_Output.H>
-#include <FL/Fl_Button.H>
-#include <FL/Fl_Gl_Window.H>
-#include <FL/fl_ask.H>
-#include <FL/fl_draw.H>
-#include <FL/gl.h>
-
-#include "oct.h"
-#include "parse.h"
-#include "graphics.h"
-
-#define FLTK_BACKEND_NAME "fltk"
-
-const char* help_text = "\
-Keyboard Shortcuts\n\
-a - autoscale\n\
-g - toggle grid\n\
-\n\
-Mouse\n\
-left drag - zoom\n\
-right click - unzoom\n\
-double click - copy coordinates to clipboard\
-";
-
-class OpenGL_fltk : public Fl_Gl_Window
-{
-public:
-  OpenGL_fltk (int xx, int yy, int ww, int hh, double num)
-    : Fl_Gl_Window (xx, yy, ww, hh, 0), number (num), in_zoom (false)
-  {
-    // ask for double buffering and a depth buffer
-    mode (FL_DEPTH | FL_DOUBLE);
-  }
-
-  ~OpenGL_fltk (void) { }
-
-  void zoom (bool z) { in_zoom = z; }
-  bool zoom (void) { return in_zoom; }
-  void set_zoom_box (const Matrix& zb) { zoom_box = zb; }
-
-private:
-  double number;
-  opengl_renderer renderer;
-  bool in_zoom;
-
-  // (x1,y1,x2,y2)
-  Matrix zoom_box;
-
-  void setup_viewport (int _w, int _h)
-  {
-    glMatrixMode (GL_PROJECTION);
-    glLoadIdentity ();
-    glViewport (0, 0, _w, _h);
-  }
-
-  void draw (void)
-  {
-    if (!valid ())
-      {
-	valid (1);
-	setup_viewport (w (), h ());
-      }
-
-    renderer.draw (gh_manager::lookup (number));
-  }
-
-  void resize (int _x,int _y,int _w,int _h)
-  {
-    Fl_Gl_Window::resize (_x, _y, _w, _h);
-    setup_viewport (_w, _h);
-    redraw ();
-  }
-
-  void draw_overlay (void)
-  {
-    if (!in_zoom)
-      return;
-
-    if (!valid())
-      {
-	valid(1);
-	setup_viewport (w (), h ());
-      }
-
-    glPushMatrix ();
-
-    glMatrixMode (GL_MODELVIEW);
-    glLoadIdentity ();
-
-    glMatrixMode (GL_PROJECTION);
-    glLoadIdentity ();
-    gluOrtho2D (0.0, w (), 0.0, h ());
-
-    glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
-    glDisable (GL_DEPTH_TEST);
-
-    glLineWidth (1);
-    glBegin (GL_LINE_STRIP);
-    gl_color(0);
-    glVertex2d (zoom_box(0), h () - zoom_box(1));
-    glVertex2d (zoom_box(0), h () - zoom_box(3));
-    glVertex2d (zoom_box(2), h () - zoom_box(3));
-    glVertex2d (zoom_box(2), h () - zoom_box(1));
-    glVertex2d (zoom_box(0), h () - zoom_box(1));
-    glEnd ();
-
-    glPopAttrib ();
-    glPopMatrix ();
-  }
-
-  int handle (int event)
-  {
-    int retval = Fl_Gl_Window::handle (event);
-
-    switch (event)
-      {
-      case FL_ENTER:
-	window ()->cursor (FL_CURSOR_CROSS);
-	return 1;
-
-      case FL_LEAVE:
-	window ()->cursor (FL_CURSOR_DEFAULT);
-	return 1;
-      }
-
-    return retval;
-  }
-};
-
-class plot_window : public Fl_Window
-{
-public:
-  plot_window (int _x, int _y, int _w, int _h, figure::properties& _fp)
-    : Fl_Window (_x, _y, _w, _h, "octave"), fp (_fp)
-  {
-    callback (window_close, static_cast<void*> (this));
-
-    begin ();
-    {
-      canvas = new
-	OpenGL_fltk (0, 0, _w , _h - status_h, number ());
-
-      autoscale = new
-	Fl_Button (0,
-		   _h - status_h,
-		   status_h,
-		   status_h,
-		   "A");
-      autoscale->callback (button_callback, static_cast<void*> (this));
-
-      togglegrid = new
-	Fl_Button (status_h,
-		   _h - status_h,
-		   status_h,
-		   status_h,
-		   "G");
-      togglegrid->callback (button_callback, static_cast<void*> (this));
-
-      help = new
-	Fl_Button (2*status_h,
-		   _h - status_h,
-		   status_h,
-		   status_h,
-		   "H");
-      help->callback (button_callback, static_cast<void*> (this));
-
-      status = new
-	Fl_Output (3*status_h,
-		   _h - status_h,
-		   _w > 2*status_h ? _w - status_h : 0,
-		   status_h, "");
-
-      status->textcolor (FL_BLACK);
-      status->color (FL_GRAY);
-      status->textfont (FL_COURIER);
-      status->textsize (10);
-      status->box (FL_ENGRAVED_BOX);
-
-      // This allows us to have a valid OpenGL context right away
-      canvas->mode (FL_DEPTH | FL_DOUBLE );
-      show ();
-      canvas->show ();
-      canvas->make_current ();
-    }
-    end ();
-
-    status->show ();
-    autoscale->show ();
-    togglegrid->show ();
-
-    resizable (canvas);
-    size_range (4*status_h, 2*status_h);
-
-    std::stringstream name;
-    name << "octave: figure " << number ();
-    label (name.str ().c_str ());
-  }
-
-  ~plot_window (void)
-  {
-    canvas->hide ();
-    status->hide ();
-    this->hide ();
-    delete canvas;
-    delete status;
-  }
-
-  // FIXME -- this could change
-  double number (void) { return fp.get___myhandle__ ().value (); }
-
-  void mark_modified (void)
-  {
-    damage (FL_DAMAGE_ALL);
-    canvas->damage (FL_DAMAGE_ALL);
-  }
-
-private:
-  // figure properties
-  figure::properties& fp;
-
-  // status area height
-  static const int status_h = 20;
-
-  // window callback
-  static void window_close (Fl_Widget*, void* data)
-  {
-    octave_value_list args;
-    args(0) = static_cast<plot_window*> (data)->number ();
-    feval ("close", args);
-  }
-
-  // button callbacks
-  static void button_callback (Fl_Widget* ww, void* data)
-  {
-    static_cast<plot_window*> (data)->button_press (ww);
-  }
-
-  void button_press (Fl_Widget* widg)
-  {
-    if (widg == autoscale)
-      axis_auto ();
-
-    if (widg == togglegrid)
-      toggle_grid ();
-
-    if (widg == help)
-      fl_message (help_text);
-  }
-
-  OpenGL_fltk* canvas;
-  Fl_Button* autoscale;
-  Fl_Button* togglegrid;
-  Fl_Button* help;
-  Fl_Output* status;
-
-  void axis_auto (void)
-  {
-    octave_value_list args;
-    args(0) = "auto";
-    feval ("axis",args);
-    mark_modified ();
-  }
-
-  void toggle_grid (void)
-  {
-    feval ("grid");
-    mark_modified ();
-  }
-
-  void pixel2pos (int px, int py, double& xx, double& yy) const
-  {
-    graphics_object ax = gh_manager::get_object (fp.get_currentaxes ());
-
-    if (ax && ax.isa ("axes"))
-      {
-	axes::properties& ap =
-	  dynamic_cast<axes::properties&> (ax.get_properties ());
-	ColumnVector pp = ap.pixel2coord (px, py);
-	xx = pp(0);
-	yy = pp(1);
-      }
-  }
-
-  graphics_handle pixel2axes (int /* px */, int /* py */)
-  {
-    Matrix kids = fp.get_children ();
-
-    for (octave_idx_type n = 0; n < kids.numel (); n++)
-      {
-	graphics_object ax = gh_manager::get_object (kids (n));
-	if (ax && ax.isa ("axes"))
-	  {
-#if 0
-	     axes::properties& ap =
-	       dynamic_cast<axes::properties&> (ax.get_properties ());
-
-	     // std::cout << "\npixpos="<<pixpos<<"(px,py)=("<<px<<","<<py<<")\n";
-	     if (px >= pixpos(0) && px <= pixpos(2)
-		 && py >= pixpos(1) && py <= pixpos(3))
-	       return ap.get___myhandle__ ();
-#endif
-	  }
-      }
-
-    return graphics_handle ();
-  }
-
-  void pixel2status (int px0, int py0, int px1 = -1, int py1 = -1)
-  {
-    double x0, y0, x1, y1;
-    std::stringstream cbuf;
-
-    pixel2pos (px0, py0, x0, y0);
-    cbuf << "[" << x0 << ", " << y0 << "]";
-    if (px1 >= 0)
-      {
-	pixel2pos (px1, py1, x1, y1);
-	cbuf << " -> ["<< x1 << ", " << y1 << "]";
-      }
-
-    status->value (cbuf.str ().c_str ());
-    status->redraw ();
-  }
-
-  void resize (int _x,int _y,int _w,int _h)
-  {
-    Fl_Window::resize (_x, _y, _w, _h);
-
-    Matrix pos (1,4,0);
-    pos(0) = _x;
-    pos(1) = _y;
-    pos(2) = _w;
-    pos(3) = _h - status_h;
-
-    fp.set_position (pos);
-  }
-
-  void draw (void)
-  {
-    Matrix pos = fp.get_position ().matrix_value ();
-    Fl_Window::resize (pos(0), pos(1) , pos(2), pos(3) + status_h);
-
-    return Fl_Window::draw ();
-  }
-
-  int handle (int event)
-  {
-    static int px0,py0;
-    static graphics_handle h0 = graphics_handle ();
-
-    int retval = Fl_Window::handle (event);
-
-    // we only handle events which are in the canvas area
-    if (Fl::event_y () >= h() - status_h)
-      return retval;
-
-    switch (event)
-      {
-      case FL_KEYDOWN:
-	switch(Fl::event_key ())
-	  {
-	  case 'a':
-	  case 'A':
-	    axis_auto ();
-	    break;
-
-	  case 'g':
-	  case 'G':
-	    toggle_grid ();
-	    break;
-	  }
-	break;
-
-      case FL_MOVE:
-	pixel2status (Fl::event_x (), Fl::event_y ());
-	break;
-
-      case FL_PUSH:
-	if (Fl::event_button () == 1)
-	  {
-	    px0 = Fl::event_x ();
-	    py0 = Fl::event_y ();
-	    h0 = pixel2axes (Fl::event_x (), Fl::event_y ());
-	    return 1;
-	  }
-	break;
-
-      case FL_DRAG:
-	pixel2status (px0, py0, Fl::event_x (), Fl::event_y ());
-	if (Fl::event_button () == 1)
-	  {
-	    canvas->zoom (true);
-	    Matrix zoom_box (1,4,0);
-	    zoom_box (0) = px0;
-	    zoom_box (1) = py0;
-	    zoom_box (2) =  Fl::event_x ();
-	    zoom_box (3) =  Fl::event_y ();
-	    canvas->set_zoom_box (zoom_box);
-	    canvas->redraw_overlay ();
-	    return 1;
-	  }
-	break;
-
-      case FL_RELEASE:
-	if (Fl::event_button () == 1)
-	  {
-	    // end of drag -- zoom
-	    if (canvas->zoom ())
-	      {
-		canvas->zoom (false);
-		double x0,y0,x1,y1;
-		graphics_object ax =
-		  gh_manager::get_object (fp.get_currentaxes ());
-		if (ax && ax.isa ("axes"))
-		  {
-		    axes::properties& ap =
-		      dynamic_cast<axes::properties&> (ax.get_properties ());
-		    pixel2pos (px0, py0, x0, y0);
-		    pixel2pos (Fl::event_x (), Fl::event_y (), x1, y1);
-		    Matrix xl (1,2,0);
-		    Matrix yl (1,2,0);
-		    if (x0 < x1)
-		      {
-			xl(0) = x0;
-			xl(1) = x1;
-		      }
-		    else
-		      {
-			xl(0) = x1;
-			xl(1) = x0;
-		      }
-
-		    if (y0 < y1)
-		      {
-			yl(0) = y0;
-			yl(1) = y1;
-		      }
-		    else
-		      {
-			yl(0) = y1;
-			yl(1) = y0;
-		      }
-		    ap.zoom (xl, yl);
-		    mark_modified ();
-		  }
-	      }
-	    // one click -- select axes
-	    else if ( Fl::event_clicks () == 0)
-	      {
-		std::cout << "ca="<< h0.value ()<<"\n";
-		if (h0.ok ())
-		  fp.set_currentaxes (h0.value());
-		return 1;
-	      }
-	  }
-	else if (Fl::event_button () == 3)
-	  {
-	    graphics_object ax =
-	      gh_manager::get_object (fp.get_currentaxes ());
-	    if (ax && ax.isa ("axes"))
-	      {
-		axes::properties& ap =
-		  dynamic_cast<axes::properties&> (ax.get_properties ());
-		ap.unzoom ();
-		mark_modified ();
-	      }
-	  }
-	break;
-      }
-
-    return retval;
-  }
-};
-
-class figure_manager
-{
-public:
-
-  static figure_manager& Instance (void)
-  {
-    static figure_manager fm;
-    return fm;
-  }
-
-  ~figure_manager (void)
-  {
-    close_all ();
-  }
-
-  void close_all (void)
-  {
-    wm_iterator win;
-    for (win = windows.begin (); win != windows.end (); win++)
-      delete (*win).second;
-    windows.clear ();
-  }
-
-  void new_window (figure::properties& fp)
-  {
-    int x, y, w, h;
-
-    int idx = figprops2idx (fp);
-    if (idx >= 0 && windows.find (idx) == windows.end ())
-      {
-	default_size(x,y,w,h);
-	idx2figprops (curr_index , fp);
-	windows[curr_index++] = new plot_window (x, y, w, h, fp);
-      }
-  }
-
-  void delete_window (int idx)
-  {
-    wm_iterator win;
-    if ((win = windows.find (idx)) != windows.end ())
-      {
-	delete (*win).second;
-	windows.erase (win);
-      }
-  }
-
-  void delete_window (std::string idx_str)
-  {
-    delete_window (str2idx (idx_str));
-  }
-
-  void mark_modified (int idx)
-  {
-    wm_iterator win;
-    if ((win=windows.find (idx)) != windows.end ())
-      {
-	(*win).second->mark_modified ();
-      }
-  }
-
-  void mark_modified (const graphics_handle& gh)
-  {
-    mark_modified (hnd2idx (gh));
-  }
-
-  Matrix get_size (int idx)
-  {
-    Matrix sz (1, 2, 0.0);
-
-    wm_iterator win;
-    if ((win = windows.find (idx)) != windows.end ())
-      {
-	sz(0) = (*win).second->w ();
-	sz(1) = (*win).second->h ();
-      }
-
-    return sz;
-  }
-
-  Matrix get_size (const graphics_handle& gh)
-  {
-    return get_size (hnd2idx (gh));
-  }
-
-private:
-  figure_manager (void) { }
-  figure_manager (const figure_manager&) { }
-  figure_manager& operator = (const figure_manager&) { return *this; }
-  // singelton -- hide all of the above
-
-  static int curr_index;
-  typedef std::map<int, plot_window*> window_map;
-  typedef window_map::iterator wm_iterator;;
-  window_map windows;
-
-  static std::string fltk_idx_header;
-
-  void default_size (int& x, int& y, int& w, int& h)
-  {
-    x = 10;
-    y = 10;
-    w = 400;
-    h = 300;
-  }
-
-  int str2idx (const caseless_str clstr)
-  {
-    int ind;
-    if (clstr.find (fltk_idx_header,0) == 0)
-      {
-	std::istringstream istr (clstr.substr (fltk_idx_header.size ()));
-	if (istr >> ind)
-	  return ind;
-      }
-    error ("fltk_backend: could not recognize fltk index");
-    return -1;
-  }
-
-  void idx2figprops (int idx, figure::properties& fp)
-  {
-    std::ostringstream ind_str;
-    ind_str << fltk_idx_header << idx;
-    fp.set___plot_stream__ (ind_str.str ());
-  }
-
-  int figprops2idx (const figure::properties& fp)
-  {
-    if (fp.get___backend__ () == FLTK_BACKEND_NAME)
-      {
-	octave_value ps = fp.get___plot_stream__ ();
-	if (ps.is_string ())
-	  return str2idx (ps.string_value ());
-	else
-	  return 0;
-      }
-    error ("fltk_backend:: figure is not fltk");
-    return -1;
-  }
-
-  int hnd2idx (const graphics_handle& fh)
-  {
-    return hnd2idx (fh.value ());
-  }
-
-  int hnd2idx (const double h)
-  {
-    graphics_object fobj = gh_manager::get_object (h);
-    if (fobj &&  fobj.isa ("figure"))
-      {
-	figure::properties& fp =
-	  dynamic_cast<figure::properties&> (fobj.get_properties ());
-	return figprops2idx (fp);
-      }
-    error ("fltk_backend:: not a figure");
-    return -1;
-  }
-};
-
-std::string figure_manager::fltk_idx_header="fltk index=";
-int figure_manager::curr_index = 1;
-
-class fltk_backend : public base_graphics_backend
-{
-public:
-  fltk_backend (void)
-    : base_graphics_backend (FLTK_BACKEND_NAME) { }
-
-  ~fltk_backend (void) { }
-
-  bool is_valid (void) const { return true; }
-
-  void close_figure (const octave_value& ov) const
-  {
-    if (ov.is_string ())
-      figure_manager::Instance ().delete_window (ov.string_value ());
-  }
-
-  void redraw_figure (const graphics_handle& fh) const
-  {
-    figure_manager::Instance ().mark_modified (fh);
-  }
-
-  void print_figure (const graphics_handle& /*fh*/,
-		     const std::string& /*term*/,
-		     const std::string& /*file*/, bool /*mono*/,
-		     const std::string& /*debug_file*/) const { }
-
-  Matrix get_canvas_size (const graphics_handle& fh) const
-  {
-    return figure_manager::Instance ().get_size (fh);
-  }
-
-  double get_screen_resolution (void) const
-  {
-    // FLTK doesn't give this info
-    return 72.0;
-  }
-
-  Matrix get_screen_size (void) const
-  {
-    Matrix sz (1, 2, 0.0);
-    sz(0) = Fl::w ();
-    sz(1) = Fl::h ();
-    return sz;
-  }
-};
-
-static bool backend_registered = false;
-// call this to init the fltk backend
-DEFUN_DLD (__init_fltk__, , , "")
-{
-  graphics_backend::register_backend (new fltk_backend);
-  backend_registered = true;
-
-  octave_value retval;
-  return retval;
-}
-
-
-// call this to delete the fltk backend
-DEFUN_DLD (__remove_fltk__, , , "")
-{
-  figure_manager::Instance ().close_all ();
-  graphics_backend::unregister_backend (FLTK_BACKEND_NAME);
-  backend_registered = false;
-
-  // FIXME ???
-  // give FLTK 10 seconds to wrap it up
-  Fl::wait(10);	
-  octave_value retval;
-  return retval;	
-}
-
-// give FLTK no more than 0.01 sec to do it's stuff
-static double fltk_maxtime = 1e-2;
-
-// call this to delete the fltk backend
-DEFUN_DLD (__fltk_maxtime__, args, ,"")
-{
-  octave_value retval = fltk_maxtime;
-
-  if (args.length () == 1)
-    {
-      if (args(0).is_real_scalar ())
-      fltk_maxtime = args(0).double_value ();
-    else
-      error ("argument must be a real scalar");
-    }
-
-  return retval;
-}
-
-// call this from the idle_callback to refresh windows
-DEFUN_DLD (__fltk_redraw__, , ,
-  "internal function for the fltk backend")
-{
-  octave_value retval;
-
-  if (!backend_registered)
-    return retval;
-
-  // we scan all figures and add those which use FLTK as a backend
-  graphics_object obj = gh_manager::get_object (0);
-  if (obj && obj.isa ("root_figure"))
-    {
-      base_properties& props = obj.get_properties ();
-      Matrix children = props.get_children ();
-
-      for (octave_idx_type n = 0; n < children.numel (); n++)
-        {
-          graphics_object fobj = gh_manager::get_object (children (n));
-          if (fobj && fobj.isa ("figure"))
-	    {
-	      figure::properties& fp =
-		dynamic_cast<figure::properties&> (fobj.get_properties ());
-	      if (fp.get___backend__ () == FLTK_BACKEND_NAME)
-		figure_manager::Instance ().new_window (fp);
-	    }
-        }
-    }
-
-  Fl::wait (fltk_maxtime);	
-
-  return retval;	
-}
-
-/* to init
-autoload("__init_fltk__",[pwd(),"/fltk_backend.oct"])
-autoload("__remove_fltk__",[pwd(),"/fltk_backend.oct"])
-autoload("__fltk_redraw__",[pwd(),"/fltk_backend.oct"])
-autoload("__fltk_maxtime__",[pwd(),"/fltk_backend.oct"])
-input_event_hook ("__fltk_redraw__");
-__init_fltk__ ();
-set(gcf(),"__backend__","fltk")
-plot(randn(1e3,1));
-
-
-*/
--- a/src/graphics/opengl/Makefile.in	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-# Makefile for octave's src/graphics/opengl directory
-#
-# Copyright (C) 1998, 2007 John W. Eaton
-#
-# This file is part of Octave.
-# 
-# Octave is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 3 of the License, or (at
-# your option) any later version.
-# 
-# Octave is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with Octave; see the file COPYING.  If not, see
-# <http://www.gnu.org/licenses/>.
-
-TOPDIR = ../../..
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-
-EXTERNAL_DISTFILES = $(DISTFILES)
-
-GRAPHICS_NAME = octgraphics_gl
-
-GRAPHICS_SRC = gl-render.cc
-
-GRAPHICS_INCS = gl-render.h
-
-GRAPHICS_EXTRA_LIBS = $(OPENGL_LIBS)
-
-include ../Makerules
--- a/src/graphics/opengl/gl-render.cc	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2777 +0,0 @@
-/*
-
-Copyright (C) 2008 Michael Goffioul
-
-This file is part of Octave.
-
-Octave is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 3 of the License, or (at your
-option) any later version.
-
-Octave is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with Octave; see the file COPYING.  If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <lo-mappers.h>
-#include "gl-render.h"
-
-#define LIGHT_MODE GL_FRONT_AND_BACK
-
-// Win32 API requires the CALLBACK attributes for
-// GLU callback functions. Define it to empty on
-// other platforms.
-#ifndef CALLBACK
-#define CALLBACK
-#endif
-
-enum {
-  AXE_ANY_DIR   = 0,
-  AXE_DEPTH_DIR = 1,
-  AXE_HORZ_DIR  = 2,
-  AXE_VERT_DIR  = 3
-};
-
-static octave_idx_type
-xmin (octave_idx_type x, octave_idx_type y)
-{
-  return x < y ? x : y;
-}
-
-class
-opengl_texture
-{
-protected:
-  class texture_rep
-  {
-  public:
-    texture_rep (void) : valid (false), count (1) { }
-
-    texture_rep (GLuint _id, int _w, int _h, int _tw, int _th)
-	: id (_id), w (_w), h (_h), tw (_tw), th (_th),
-	  tx (double(w)/tw), ty (double(h)/th), valid (true),
-	  count (1) { }
-
-    ~texture_rep (void)
-      {
-	if (valid)
-	  glDeleteTextures (1, &id);
-      }
-
-    void bind (int mode) const
-      { if (valid) glBindTexture (mode, id); }
-
-    void tex_coord (double q, double r) const
-      { if (valid) glTexCoord2d (q*tx, r*ty); }
-
-    GLuint id;
-    int w, h;
-    int tw, th;
-    double tx, ty;
-    bool valid;
-    int count;
-  };
-
-  texture_rep *rep;
-
-private:
-  opengl_texture (texture_rep *_rep) : rep (_rep) { }
-
-public:
-  opengl_texture (void) : rep (new texture_rep ()) { }
-
-  opengl_texture (const opengl_texture& tx)
-      : rep (tx.rep)
-    {
-      rep->count++;
-    }
-
-  ~opengl_texture (void)
-    {
-      if (--rep->count == 0)
-	delete rep;
-    }
-
-  opengl_texture& operator = (const opengl_texture& tx)
-    {
-      if (--rep->count == 0)
-	delete rep;
-
-      rep = tx.rep;
-      rep->count++;
-
-      return *this;
-    }
-
-  static opengl_texture create (const octave_value& data);
-
-  void bind (int mode = GL_TEXTURE_2D) const
-    { rep->bind (mode); }
-
-  void tex_coord (double q, double r) const
-    { rep->tex_coord (q, r); }
-  
-  bool is_valid (void) const
-    { return rep->valid; }
-};
-
-static int
-next_power_of_2 (int n)
-{
-  int m = 1;
-
-  while (m < n && m < INT_MAX)
-    m <<= 1;
-
-  return m;
-}
-
-opengl_texture
-opengl_texture::create (const octave_value& data)
-{
-  opengl_texture retval;
-
-  dim_vector dv (data.dims ());
-
-  // Expect RGB data
-  if (dv.length () == 3 && dv(2) == 3)
-    {
-      int h = dv(0), w = dv(1), tw, th;
-      GLuint id;
-      bool ok = true;
-
-      tw = next_power_of_2 (w);
-      th = next_power_of_2 (w);
-
-      glGenTextures (1, &id);
-      glBindTexture (GL_TEXTURE_2D, id);
-
-      if (data.is_double_type ())
-	{
-	  NDArray _a = data.array_value ();
-
-	  OCTAVE_LOCAL_BUFFER (float, 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]   = _a(i,j,0);
-		a[idx+1] = _a(i,j,1);
-		a[idx+2] = _a(i,j,2);
-	      }
-
-	  glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
-			GL_RGB, GL_FLOAT, a);
-	}
-      else if (data.is_uint8_type ())
-	{
-	  uint8NDArray _a = data.uint8_array_value ();
-
-	  OCTAVE_LOCAL_BUFFER (octave_uint8, 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]   = _a(i,j,0);
-		a[idx+1] = _a(i,j,1);
-		a[idx+2] = _a(i,j,2);
-	      }
-
-	  glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
-			GL_RGB, GL_UNSIGNED_BYTE, a);
-	}
-      else
-	{
-	  ok = false;
-	  warning ("opengl_texture::create: invalid texture data type (expected double or uint8)");
-	}
-
-      if (ok)
-	{
-	  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-	  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-	  if (glGetError () != GL_NO_ERROR)
-	    warning ("opengl_texture::create: OpenGL error while generating texture data");
-	  else
-	    retval = opengl_texture (new texture_rep (id, w, h, tw, th));
-	}
-    }
-  else
-    warning ("opengl_texture::create: invalid texture data size");
-
-  return retval;
-}
-
-class
-opengl_tesselator
-{
-public:
-  typedef void (CALLBACK *fcn) (void);
-
-public:
-
-  opengl_tesselator (void) : glu_tess (0) { init (); }
-
-  virtual ~opengl_tesselator (void)
-    { if (glu_tess) gluDeleteTess (glu_tess); }
-
-  void begin_polygon (bool filled = true)
-    {
-      gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY,
-		       (filled ? GL_FALSE : GL_TRUE));
-      fill = filled;
-      gluTessBeginPolygon (glu_tess, this);
-    }
-
-  void end_polygon (void) const
-    { gluTessEndPolygon (glu_tess); }
-
-  void begin_contour (void) const
-    { gluTessBeginContour (glu_tess); }
-
-  void end_contour (void) const
-    { gluTessEndContour (glu_tess); }
-
-  void add_vertex (double *loc, void *data) const
-    { gluTessVertex (glu_tess, loc, data); }
-
-protected:
-  virtual void begin (GLenum /*type*/) { }
-
-  virtual void end (void) { }
-
-  virtual void vertex (void */*data*/) { }
-
-  virtual void combine (GLdouble /*c*/[3], void */*data*/[4],
-			GLfloat /*w*/[4], void **/*out_data*/) { }
-
-  virtual void edge_flag (GLboolean /*flag*/) { }
-
-  virtual void error (GLenum err)
-    { ::error ("OpenGL tesselation error (%d)", err); }
-
-  virtual void init (void)
-    {
-      glu_tess = gluNewTess ();
-
-      gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA,
-		       reinterpret_cast<fcn> (tess_begin));
-      gluTessCallback (glu_tess, GLU_TESS_END_DATA,
-		       reinterpret_cast<fcn> (tess_end));
-      gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA,
-		       reinterpret_cast<fcn> (tess_vertex));
-      gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA,
-		       reinterpret_cast<fcn> (tess_combine));
-      gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA,
-		       reinterpret_cast<fcn> (tess_edge_flag));
-      gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA,
-		       reinterpret_cast<fcn> (tess_error));
-    }
-
-  bool is_filled (void) const { return fill; }
-
-private:
-  static void CALLBACK tess_begin (GLenum type, void *t)
-    { reinterpret_cast<opengl_tesselator *> (t)->begin (type); }
-  
-  static void CALLBACK tess_end (void *t)
-    { reinterpret_cast<opengl_tesselator *> (t)->end (); }
-  
-  static void CALLBACK tess_vertex (void *v, void *t)
-    { reinterpret_cast<opengl_tesselator *> (t)->vertex (v); }
-  
-  static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4],
-				     void **out,  void *t)
-    { reinterpret_cast<opengl_tesselator *> (t)->combine (c, v, w, out); }
-  
-  static void CALLBACK tess_edge_flag (GLboolean flag, void *t)
-    { reinterpret_cast<opengl_tesselator *> (t)->edge_flag (flag); }
-  
-  static void CALLBACK tess_error (GLenum err, void *t)
-    { reinterpret_cast<opengl_tesselator *> (t)->error (err); }
-
-private:
-  GLUtesselator *glu_tess;
-  bool fill;
-};
-
-class
-vertex_data
-{
-public:
-  class vertex_data_rep
-  {
-  public:
-    Matrix coords;
-    Matrix color;
-    Matrix normal;
-    double alpha;
-    float ambient;
-    float diffuse;
-    float specular;
-    float specular_exp;
-
-    // reference counter
-    int count;
-
-    vertex_data_rep (void) { }
-
-    vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& n,
-		     double a, float as, float ds, float ss, float se)
-	: coords (c), color (col), normal (n), alpha (a),
-	  ambient (as), diffuse (ds), specular (ss), specular_exp (se),
-	  count (1) { }
-  };
-
-private:
-  vertex_data_rep *rep;
-
-  vertex_data_rep *nil_rep (void) const
-    {
-      static vertex_data_rep *nr = new vertex_data_rep ();
-
-      return nr;
-    }
-
-public:
-  vertex_data (void) : rep (nil_rep ()) { }
-
-  vertex_data (const vertex_data& v) : rep (v.rep)
-    { rep->count++; }
-
-  vertex_data (const Matrix& c, const Matrix& col, const Matrix& n,
-	       double a, float as, float ds, float ss, float se)
-      : rep (new vertex_data_rep (c, col, n, a, as, ds, ss, se))
-    {
-      rep->count++;
-    }
-
-  vertex_data (vertex_data_rep *new_rep)
-      : rep (new_rep) { }
-
-  ~vertex_data (void)
-    {
-      if (--rep->count == 0)
-	delete rep;
-    }
-
-  vertex_data& operator = (const vertex_data& v)
-    {
-      if (--rep->count == 0)
-	delete rep;
-
-      rep = v.rep;
-      rep->count++;
-
-      return *this;
-    }
-
-  vertex_data_rep *get_rep (void) const { return rep; }
-};
-
-#include <Array.cc>
-
-class
-opengl_renderer::patch_tesselator : public opengl_tesselator
-{
-public:
-  patch_tesselator (opengl_renderer *r, int cmode, int lmode, int idx = 0)
-      : opengl_tesselator (), renderer (r),
-        color_mode (cmode), light_mode (lmode), index (idx),
-        first (true) { }
-
-protected:
-  void begin (GLenum type)
-    {
-      //printf("patch_tesselator::begin (%d)\n", type);
-      first = true;
-
-      if (color_mode == 2 || light_mode == 2)
-	glShadeModel (GL_SMOOTH);
-      else
-	glShadeModel (GL_FLAT);
-
-      if (is_filled ())
-	renderer->set_polygon_offset (true, 1+index);
-
-      glBegin (type);
-    }
-
-  void end (void)
-    {
-      //printf("patch_tesselator::end\n");
-      glEnd ();
-      renderer->set_polygon_offset (false);
-    }
-
-  void vertex (void *data)
-    {
-      vertex_data::vertex_data_rep *v
-	  = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
-      //printf("patch_tesselator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->coords(2));
-
-      // FIXME: why did I need to keep the first vertex of the face
-      // in JHandles? I think it's related to the fact that the 
-      // tessellation process might re-order the vertices, such that
-      // the first one you get here might not be the first one of the face;
-      // but I can't figure out the actual reason.
-      if (color_mode > 0 && (first || color_mode == 2))
-	{
-	  Matrix col = v->color;
-
-	  if (col.numel () == 3)
-	    {
-	      glColor3dv (col.data ());
-	      if (light_mode > 0)
-		{
-		  float buf[4] = { 0, 0, 0, 1 };
-
-		  for (int k = 0; k < 3; k++)
-		    buf[k] = (v->ambient * col(k));
-		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
-
-		  for (int k = 0; k < 3; k++)
-		    buf[k] = (v->diffuse * col(k));
-		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
-		}
-	    }
-	}
-
-      if (light_mode > 0 && (first || light_mode == 2))
-	glNormal3dv (v->normal.data ());
-
-      glVertex3dv (v->coords.data ());
-
-      first = false;
-    }
-
-  void combine (GLdouble xyz[3], void *data[4], GLfloat w[4],
-		void **out_data)
-    {
-      //printf("patch_tesselator::combine\n");
-
-      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 nn (1, 3, 0.0);
-      double aa = 0.0;
-
-      vv(0) = xyz[0];
-      vv(1) = xyz[1];
-      vv(2) = xyz[2];
-
-      if (v[0]->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]->color(ic));
-	}
-
-      if (v[0]->normal.numel () > 0)
-	{
-	  for (int in = 0; in < 3; in++)
-	    for (int iv = 0; iv < vmax; iv++)
-	      nn(in) += (w[iv] * v[iv]->normal(in));
-	}
-
-      for (int iv = 0; iv < vmax; iv++)
-	aa += (w[iv] * v[iv]->alpha);
-
-      vertex_data new_v (vv, cc, nn, aa, v[0]->ambient, v[0]->diffuse,
-			 v[0]->specular, v[0]->specular_exp);
-      tmp_vdata.push_back (new_v);
-
-      *out_data = new_v.get_rep ();
-    }
-
-private:
-  opengl_renderer *renderer;
-  int color_mode;	// 0: uni,  1: flat, 2: interp
-  int light_mode;	// 0: none, 1: flat, 2: gouraud
-  int index;
-  bool first;
-  std::list<vertex_data> tmp_vdata;
-};
-
-void
-opengl_renderer::draw (const graphics_object& go)
-{
-  if (! go.valid_object ())
-    return;
-
-  const base_properties& props = go.get_properties ();
-
-  if (go.isa ("figure"))
-    draw (dynamic_cast<const figure::properties&> (props));
-  else if (go.isa ("axes"))
-    draw (dynamic_cast<const axes::properties&> (props));
-  else if (go.isa ("line"))
-    draw (dynamic_cast<const line::properties&> (props));
-  else if (go.isa ("surface"))
-    draw (dynamic_cast<const surface::properties&> (props));
-  else if (go.isa ("patch"))
-    draw (dynamic_cast<const patch::properties&> (props));
-  else if (go.isa ("hggroup"))
-    draw (dynamic_cast<const hggroup::properties&> (props));
-  else
-    warning ("opengl_renderer: cannot render object of type `%s'",
-	     props.graphics_object_name ().c_str ());
-}
-
-void
-opengl_renderer::draw (const figure::properties& props)
-{
-  backend = props.get_backend ();
-
-  // Initialize OpenGL context
-
-  glEnable (GL_DEPTH_TEST);
-  glDepthFunc (GL_LEQUAL);
-  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  glEnable (GL_NORMALIZE);
-
-  // Clear background
-
-  Matrix c = props.get_color_rgb ();
-
-  if (c.length() >= 3)
-    {
-      glClearColor (c(0), c(1), c(2), 1);
-      glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-    }
-
-  // Draw children
-
-  draw (props.get_children ());
-}
-
-void
-opengl_renderer::draw (const axes::properties& props)
-{
-  // setup OpenGL transformation
-
-  Matrix x_zlim = props.get_transform_zlim ();
-  Matrix x_mat1 = props.get_opengl_matrix_1 ();
-  Matrix x_mat2 = props.get_opengl_matrix_2 ();
-  
-  xZ1 = x_zlim(0)-(x_zlim(1)-x_zlim(0))/2;
-  xZ2 = x_zlim(1)+(x_zlim(1)-x_zlim(0))/2;
-
-  int vw[4];
-  glGetIntegerv (GL_VIEWPORT, vw);
-
-  glMatrixMode (GL_MODELVIEW);
-  glLoadIdentity ();
-  glScaled(1, 1, -1);
-  glMultMatrixd (x_mat1.data ());
-  glMatrixMode (GL_PROJECTION);
-  glLoadIdentity ();
-  glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
-  glMultMatrixd (x_mat2.data ());
-  glMatrixMode (GL_MODELVIEW);
-
-  glClear (GL_DEPTH_BUFFER_BIT);
-
-  // store axes transformation data
-
-  xform = props.get_transform ();
-  
-  // draw axes object
-  
-  Matrix xlim = xform.xscale (props.get_xlim ().matrix_value ());
-  Matrix ylim = xform.yscale (props.get_ylim ().matrix_value ());
-  Matrix zlim = xform.zscale (props.get_zlim ().matrix_value ());
-  double x_min = xlim(0), x_max = xlim(1);
-  double y_min = ylim(0), y_max = ylim(1);
-  double z_min = zlim(0), z_max = zlim(1);
-  
-  double xd = (props.xdir_is ("normal") ? 1 : -1);
-  double yd = (props.ydir_is ("normal") ? 1 : -1);
-  double zd = (props.zdir_is ("normal") ? 1 : -1);
-
-  ColumnVector p1, p2, xv (3), yv (3), zv (3);
-  int xstate, ystate, zstate;
-
-  xstate = ystate = zstate = AXE_ANY_DIR;
-
-  p1 = xform.transform (x_min, (y_min+y_max)/2, (z_min+z_max)/2, false);
-  p2 = xform.transform (x_max, (y_min+y_max)/2, (z_min+z_max)/2, false);
-  xv(0) = xround (p2(0)-p1(0));
-  xv(1) = xround (p2(1)-p1(1));
-  xv(2) = (p2(2)-p1(2));
-  if (xv(0) == 0 && xv(1) == 0)
-    xstate = AXE_DEPTH_DIR;
-  else if (xv(2) == 0)
-    {
-      if (xv(0) == 0)
-        xstate = AXE_VERT_DIR;
-      else if (xv(1) == 0)
-        xstate = AXE_HORZ_DIR;
-    }
-  double xPlane;
-  if (xv(2) == 0)
-    if (xv(1) == 0)
-      xPlane = (xv(0) > 0 ? x_max : x_min);
-    else
-      xPlane = (xv(1) < 0 ? x_max : x_min);
-  else
-    xPlane = (xv(2) < 0 ? x_min : x_max);
-  double xPlaneN = (xPlane == x_min ? x_max : x_min);
-  double fx = (x_max-x_min)/sqrt(xv(0)*xv(0)+xv(1)*xv(1));
-
-  p1 = xform.transform ((x_min+x_max)/2, y_min, (z_min+z_max)/2, false);
-  p2 = xform.transform ((x_min+x_max)/2, y_max, (z_min+z_max)/2, false);
-  yv(0) = xround (p2(0)-p1(0));
-  yv(1) = xround (p2(1)-p1(1));
-  yv(2) = (p2(2)-p1(2));
-  if (yv(0) == 0 && yv(1) == 0)
-    ystate = AXE_DEPTH_DIR;
-  else if (yv(2) == 0)
-    {
-      if (yv(0) == 0)
-        ystate = AXE_VERT_DIR;
-      else if (yv(1) == 0)
-        ystate = AXE_HORZ_DIR;
-    }
-  double yPlane;
-  if (yv(2) == 0)
-    if (yv(1) == 0)
-      yPlane = (yv(0) > 0 ? y_max : y_min);
-    else
-      yPlane = (yv(1) < 0 ? y_max : y_min);
-  else
-    yPlane = (yv(2) < 0 ? y_min : y_max);
-  double yPlaneN = (yPlane == y_min ? y_max : y_min);
-  double fy = (y_max-y_min)/sqrt(yv(0)*yv(0)+yv(1)*yv(1));
-
-  p1 = xform.transform((x_min+x_max)/2, (y_min+y_max)/2, z_min, false);
-  p2 = xform.transform((x_min+x_max)/2, (y_min+y_max)/2, z_max, false);
-  zv(0) = xround(p2(0)-p1(0));
-  zv(1) = xround (p2(1)-p1(1));
-  zv(2) = (p2(2)-p1(2));
-  if (zv(0) == 0 && zv(1) == 0)
-    zstate = AXE_DEPTH_DIR;
-  else if (zv(2) == 0)
-  {
-    if (zv(0) == 0)
-      zstate = AXE_VERT_DIR;
-    else if (zv(1) == 0)
-      zstate = AXE_HORZ_DIR;
-  }
-  double zPlane;
-  if (zv(2) == 0)
-    if (zv(1) == 0)
-      zPlane = (zv(0) > 0 ? z_min : z_max);
-    else
-      zPlane = (zv(1) < 0 ? z_min : z_max);
-  else
-    zPlane = (zv(2) < 0 ? z_min : z_max);
-  double zPlaneN = (zPlane == z_min ? z_max : z_min);
-  double fz = (z_max-z_min)/sqrt(zv(0)*zv(0)+zv(1)*zv(1));
-
-  bool mode2d = (((xstate > AXE_DEPTH_DIR ? 1 : 0) +
-        (ystate > AXE_DEPTH_DIR ? 1 : 0) +
-        (zstate > AXE_DEPTH_DIR ? 1 : 0)) == 2);
-  if (props.tickdirmode_is ("auto"))
-  {
-    // FIXME: tickdir should be updated (code below comes
-    //        from JHandles)
-    //autoMode++;
-    //TickDir.set(mode2d ? "in" : "out", true);
-    //autoMode--;
-  }
-
-  // FIXME: use ticklength property
-  double xticklen = 7, yticklen = 7, zticklen = 7;
-
-  //double tickdir = (props.tickdir_is ("in") ? -1 : 1);
-  double tickdir = (props.tickdirmode_is ("auto") ?
-		    (mode2d ? -1 : 1) :
-		    (props.tickdir_is ("in") ? -1 : 1));
-  double xtickoffset = (mode2d && tickdir < 0 ? 0 : xticklen) + 5;
-  double ytickoffset = (mode2d && tickdir < 0 ? 0 : yticklen) + 5;
-  double ztickoffset = (mode2d && tickdir < 0 ? 0 : zticklen) + 5;
-
-  bool xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
-  bool x2Dtop = false;
-  bool y2Dright = false;
-  double zpTick = zPlane;
-
-  /* 2D mode */
-  if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
-  {
-    if (props.xaxislocation_is ("top"))
-    {
-      double tmp = yPlane;
-      yPlane = yPlaneN;
-      yPlaneN = tmp;
-      x2Dtop = true;
-    }
-    if (props.yaxislocation_is ("right"))
-    {
-      double tmp = xPlane;
-      xPlane = xPlaneN;
-      xPlaneN = tmp;
-      y2Dright = true;
-    }
-    if (props.layer_is ("top"))
-      zpTick = zPlaneN;
-  }
-
-  Matrix axe_color = props.get_color_rgb ();
-  bool visible = props.is_visible ();
-  bool box = props.is_box ();
-
-  // Axes planes
-
-  if (axe_color.numel () > 0 && visible)
-    {
-      set_color (axe_color);
-      set_polygon_offset (true, 2.5);
-
-      glBegin (GL_QUADS);
-
-      // X plane
-      glVertex3d (xPlane, y_min, z_min);
-      glVertex3d (xPlane, y_max, z_min);
-      glVertex3d (xPlane, y_max, z_max);
-      glVertex3d (xPlane, y_min, z_max);
-
-      // Y plane
-      glVertex3d (x_min, yPlane, z_min);
-      glVertex3d (x_max, yPlane, z_min);
-      glVertex3d (x_max, yPlane, z_max);
-      glVertex3d (x_min, yPlane, z_max);
-
-      // Z plane
-      glVertex3d (x_min, y_min, zPlane);
-      glVertex3d (x_max, y_min, zPlane);
-      glVertex3d (x_max, y_max, zPlane);
-      glVertex3d (x_min, y_max, zPlane);
-
-      glEnd ();
-
-      set_polygon_offset (false);
-    }
-
-  // Axes box
-
-  set_linestyle ("-", true);
-  set_linewidth (props.get_linewidth ());
-
-  if (visible)
-    {
-      glBegin (GL_LINES);
-
-      // X box
-      set_color (props.get_xcolor_rgb ());
-      glVertex3d (xPlaneN, yPlaneN, zPlane);
-      glVertex3d (xPlane, yPlaneN, zPlane);
-      if (box)
-        {
-          glVertex3d (xPlaneN, yPlane, zPlane);
-          glVertex3d (xPlane, yPlane, zPlane);
-          glVertex3d (xPlaneN, yPlane, zPlaneN);
-          glVertex3d (xPlane, yPlane, zPlaneN);
-          glVertex3d (xPlaneN, yPlaneN, zPlaneN);
-          glVertex3d (xPlane, yPlaneN, zPlaneN);
-        }
-
-      // Y box
-      set_color (props.get_ycolor_rgb ());
-      glVertex3d (xPlaneN, yPlaneN, zPlane);
-      glVertex3d (xPlaneN, yPlane, zPlane);
-      if (box)
-        {
-          glVertex3d (xPlane, yPlaneN, zPlane);
-          glVertex3d (xPlane, yPlane, zPlane);
-          glVertex3d (xPlane, yPlaneN, zPlaneN);
-          glVertex3d (xPlane, yPlane, zPlaneN);
-          glVertex3d (xPlaneN, yPlaneN, zPlaneN);
-          glVertex3d (xPlaneN, yPlane, zPlaneN);
-        }
-
-      // Z box
-      set_color (props.get_zcolor_rgb ());
-      if (xySym)
-        {
-          glVertex3d (xPlaneN, yPlane, zPlaneN);
-          glVertex3d (xPlaneN, yPlane, zPlane);
-        }
-      else
-        {
-          glVertex3d (xPlane, yPlaneN, zPlaneN);
-          glVertex3d (xPlane, yPlaneN, zPlane);
-        }
-      if (box)
-        {
-          glVertex3d (xPlane, yPlane, zPlaneN);
-          glVertex3d (xPlane, yPlane, zPlane);
-          if (xySym)
-            {
-              glVertex3d (xPlane, yPlaneN, zPlaneN);
-              glVertex3d (xPlane, yPlaneN, zPlane);
-            }
-          else
-            {
-              glVertex3d (xPlaneN, yPlane, zPlaneN);
-              glVertex3d (xPlaneN, yPlane, zPlane);
-            }
-          glVertex3d (xPlaneN, yPlaneN, zPlaneN);
-          glVertex3d (xPlaneN, yPlaneN, zPlane);
-        }
-
-      glEnd ();
-    }
-
-  std::string gridstyle = props.get_gridlinestyle ();
-  std::string minorgridstyle = props.get_minorgridlinestyle ();
-
-  // X grid
-
-  if (visible && xstate != AXE_DEPTH_DIR)
-    {
-      bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
-      bool do_xminorgrid = (props.is_xminorgrid () && (minorgridstyle != "none"));
-      bool do_xminortick = props.is_xminortick ();
-      Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ());
-      // FIXME: use pre-computed minor ticks
-      Matrix xmticks;
-      // FIXME: use xticklabels property
-      string_vector xticklabels;
-      int wmax = 0, hmax = 0;
-      bool tick_along_z = xisinf (fy);
-      Matrix tickpos (xticks.numel (), 3);
-
-      set_color (props.get_xcolor_rgb ());
-
-      // grid lines
-      if (do_xgrid)
-        {
-          set_linestyle (gridstyle, true);
-          glBegin (GL_LINES);
-          for (int i = 0; i < xticks.numel (); i++)
-            {
-              double xval = xticks(i);
-
-              glVertex3d (xval, yPlaneN, zpTick);
-              glVertex3d (xval, yPlane, zpTick);
-              if (zstate != AXE_DEPTH_DIR)
-                {
-                  glVertex3d (xval, yPlane, zPlaneN);
-                  glVertex3d (xval, yPlane, zPlane);
-                }
-            }
-          glEnd ();
-          set_linestyle ("-", true);
-        }
-
-      // tick marks
-      if (tick_along_z)
-        {
-          glBegin (GL_LINES);
-          for (int i = 0; i < xticks.numel (); i++)
-            {
-              double xval = xticks(i);
-
-              glVertex3d (xval, yPlaneN, zPlane);
-              glVertex3d (xval, yPlaneN, zPlane+signum(zPlane-zPlaneN)*fz*xticklen*tickdir);
-              if (box && xstate != AXE_ANY_DIR)
-                {
-                  glVertex3d (xval, yPlaneN, zPlaneN);
-                  glVertex3d (xval, yPlaneN,
-                        zPlaneN+signum(zPlaneN-zPlane)*fz*xticklen*tickdir);
-                }
-              tickpos(i,0) = xval;
-              tickpos(i,1) = yPlaneN;
-              tickpos(i,2) = zPlane+signum(zPlane-zPlaneN)*fz*xtickoffset;
-            }
-          glEnd ();
-        }
-      else
-        {
-          glBegin (GL_LINES);
-          for (int i = 0; i < xticks.numel (); i++)
-            {
-              double xval = xticks(i);
-
-              glVertex3d (xval, yPlaneN, zpTick);
-              glVertex3d (xval, yPlaneN+signum(yPlaneN-yPlane)*fy*xticklen*tickdir, zpTick);
-              if (box && xstate != AXE_ANY_DIR)
-                {
-                  glVertex3d (xval, yPlane, zpTick);
-                  glVertex3d (xval,
-                        yPlane+signum(yPlane-yPlaneN)*fy*xticklen*tickdir, zpTick);
-                }
-              tickpos(i,0) = xval;
-              tickpos(i,1) = yPlaneN+signum(yPlaneN-yPlane)*fy*xtickoffset;
-              tickpos(i,2) = zPlane;
-            }
-          glEnd ();
-        }
-
-      // FIXME: tick texts
-
-      // minor grid lines
-      if (do_xminorgrid)
-        {
-          set_linestyle (minorgridstyle, true);
-          glBegin (GL_LINES);
-          for (int i = 0; i < xmticks.numel (); i++)
-            {
-              double xval = xmticks(i);
-
-              glVertex3d (xval, yPlaneN, zpTick);
-              glVertex3d (xval, yPlane, zpTick);
-              if (zstate != AXE_DEPTH_DIR)
-                {
-                  glVertex3d (xval, yPlane, zPlaneN);
-                  glVertex3d (xval, yPlane, zPlane);
-                }
-            }
-          glEnd ();
-          set_linestyle ("-", true);
-        }
-			
-      // minor tick marks
-      if (do_xminortick)
-        {
-          if (tick_along_z)
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < xmticks.numel (); i++)
-                {
-                  double xval = xmticks(i);
-
-                  glVertex3d (xval, yPlaneN, zPlane);
-                  glVertex3d (xval, yPlaneN,
-                      zPlane+signum(zPlane-zPlaneN)*fz*xticklen/2*tickdir);
-                  if (box && xstate != AXE_ANY_DIR)
-                    {
-                      glVertex3d (xval, yPlaneN, zPlaneN);
-                      glVertex3d (xval, yPlaneN,
-                          zPlaneN+signum(zPlaneN-zPlane)*fz*xticklen/2*tickdir);
-                    }
-                }
-              glEnd ();
-            }
-          else
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < xmticks.numel (); i++)
-                {
-                  double xval = xmticks(i);
-
-                  glVertex3d (xval, yPlaneN, zpTick);
-                  glVertex3d (xval,
-                        yPlaneN+signum(yPlaneN-yPlane)*fy*xticklen/2*tickdir, zpTick);
-                  if (box && xstate != AXE_ANY_DIR)
-                    {
-                      glVertex3d (xval, yPlane, zpTick);
-                      glVertex3d (xval,
-                            yPlane+signum(yPlane-yPlaneN)*fy*xticklen/2*tickdir, zpTick);
-                    }
-                }
-              glEnd ();
-            }
-        }
-
-      text::properties& xlabel_props =
-        reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_xlabel ()).get_properties ());
-
-      // FIXME: auto-positioning should be disabled if the 
-      //        label has been positioned manually
-      if (! xlabel_props.get_string ().empty ())
-        {
-          xlabel_props.set_horizontalalignment (xstate > AXE_DEPTH_DIR ? "center" : (xySym ? "left" : "right"));
-	  xlabel_props.set_verticalalignment (xstate == AXE_VERT_DIR ? "bottom" : (zd*zv(2) <= 0 ? "top" : "bottom"));
-
-          double angle = 0;
-          ColumnVector p = graphics_xform::xform_vector ((xmin+xmax)/2, yPlaneN, zPlane);
-
-          if (tick_along_z)
-            p(2) += (signum(zPlane-zPlaneN)*fz*xtickoffset);
-          else
-            p(1) += (signum(yPlaneN-yPlane)*fy*xtickoffset);
-          p = xform.transform (p(0), p(1), p(2), false);
-          switch (xstate)
-            {
-              case AXE_ANY_DIR:
-                p(0) += (xySym ? wmax : -wmax);
-                p(1) += (zd*zv(2) <= 0 ? hmax : -hmax);
-                break;
-              case AXE_VERT_DIR:
-                p(0) -= wmax;
-                angle = 90;
-                break;
-              case AXE_HORZ_DIR:
-                p(1) += hmax;
-                break;
-            }
-          p = xform.untransform (p(0), p(1), p(2), true);
-          xlabel_props.set_position (p.extract_n (0, 3).transpose ());
-          xlabel_props.set_rotation (angle);
-        }
-    }
-
-  // Y grid
-		
-  if (ystate != AXE_DEPTH_DIR && visible)
-    {
-      bool do_ygrid = (props.is_ygrid () && (gridstyle != "none"));
-      bool do_yminorgrid = (props.is_yminorgrid () && (minorgridstyle != "none"));
-      bool do_yminortick = props.is_yminortick ();
-      Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ());
-      // FIXME: use pre-computed minor ticks
-      Matrix ymticks;
-      // FIXME: use yticklabels property
-      string_vector yticklabels;
-      int wmax = 0, hmax = 0;
-      bool tick_along_z = xisinf (fx);
-      Matrix tickpos (yticks.numel (), 3);
-
-      set_color (props.get_ycolor_rgb ());
-
-      // grid lines
-      if (do_ygrid)
-        {
-          set_linestyle (gridstyle, true);
-          glBegin (GL_LINES);
-          for (int i = 0; i < yticks.numel (); i++)
-            {
-              double yval = yticks(i);
-
-              glVertex3d (xPlaneN, yval, zpTick);
-              glVertex3d (xPlane, yval, zpTick);
-              if (zstate != AXE_DEPTH_DIR)
-                {
-                  glVertex3d (xPlane, yval, zPlaneN);
-                  glVertex3d (xPlane, yval, zPlane);
-                }
-            }
-          glEnd ();
-          set_linestyle ("-", true);
-        }
-
-      // tick marks
-      if (tick_along_z)
-        {
-          glBegin (GL_LINES);
-          for (int i = 0; i < yticks.numel (); i++)
-            {
-              double yval = yticks(i);
-
-              glVertex3d (xPlaneN, yval, zPlane);
-              glVertex3d (xPlaneN, yval, zPlane+signum(zPlane-zPlaneN)*fz*yticklen*tickdir);
-              if (box && ystate != AXE_ANY_DIR)
-                {
-                  glVertex3d (xPlaneN, yval, zPlaneN);
-                  glVertex3d (xPlaneN, yval,
-                        zPlaneN+signum(zPlaneN-zPlane)*fz*yticklen*tickdir);
-                }
-              tickpos(i,0) = xPlaneN;
-              tickpos(i,1) = yval;
-              tickpos(i,2) = zPlane+signum(zPlane-zPlaneN)*fz*ytickoffset;
-            }
-          glEnd ();
-        }
-      else
-        {
-          glBegin (GL_LINES);
-          for (int i = 0; i < yticks.numel (); i++)
-            {
-              double yval = yticks(i);
-
-              glVertex3d (xPlaneN, yval, zpTick);
-              glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*yticklen*tickdir, yval, zpTick);
-              if (box && ystate != AXE_ANY_DIR)
-                {
-                  glVertex3d (xPlane, yval, zpTick);
-                  glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*yticklen*tickdir,
-                        yval, zpTick);
-                }
-              tickpos(i,0) = xPlaneN+signum(xPlaneN-xPlane)*fx*ytickoffset;
-              tickpos(i,1) = yval;
-              tickpos(i,2) = zPlane;
-            }
-          glEnd ();
-        }
-
-      // FIXME: tick texts
-
-      // minor grid lines
-      if (do_yminorgrid)
-        {
-          set_linestyle (minorgridstyle, true);
-          glBegin (GL_LINES);
-          for (int i = 0; i < ymticks.numel (); i++)
-            {
-              double yval = ymticks(i);
-
-              glVertex3d (xPlaneN, yval, zpTick);
-              glVertex3d (xPlane, yval, zpTick);
-              if (zstate != AXE_DEPTH_DIR)
-                {
-                  glVertex3d (xPlane, yval, zPlaneN);
-                  glVertex3d (xPlane, yval, zPlane);
-                }
-            }
-          glEnd ();
-          set_linestyle ("-", true);
-        }
-
-      // minor tick marks
-      if (do_yminortick)
-        {
-          if (tick_along_z)
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < ymticks.numel (); i++)
-                {
-                  double yval = ymticks(i);
-
-                  glVertex3d (xPlaneN, yval, zPlane);
-                  glVertex3d (xPlaneN, yval,
-                        zPlane+signum(zPlane-zPlaneN)*fz*yticklen/2*tickdir);
-                  if (box && ystate != AXE_ANY_DIR)
-                    {
-                      glVertex3d (xPlaneN, yval, zPlaneN);
-                      glVertex3d (xPlaneN, yval,
-                            zPlaneN+signum(zPlaneN-zPlane)*fz*yticklen/2*tickdir);
-                    }
-                }
-              glEnd ();
-            }
-          else
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < ymticks.numel (); i++)
-                {
-                  double yval = ymticks(i);
-
-                  glVertex3d (xPlaneN, yval, zpTick);
-                  glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*yticklen/2*tickdir,
-                        yval, zpTick);
-                  if (box && ystate != AXE_ANY_DIR)
-                    {
-                      glVertex3d (xPlane, yval, zpTick);
-                      glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*yticklen/2*tickdir,
-                            yval, zpTick);
-                    }
-                }
-              glEnd ();
-            }
-        }
-
-      text::properties& ylabel_props =
-        reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_ylabel ()).get_properties ());
-
-      // FIXME: auto-positioning should be disabled if the 
-      //        label has been positioned manually
-      if (! ylabel_props.get_string ().empty ())
-        {
-          ylabel_props.set_horizontalalignment (ystate > AXE_DEPTH_DIR ? "center" : (!xySym ? "left" : "right"));
-	  ylabel_props.set_verticalalignment (ystate == AXE_VERT_DIR ? "bottom" : (zd*zv(2) <= 0 ? "top" : "bottom"));
-
-          double angle = 0;
-          ColumnVector p = graphics_xform::xform_vector (xPlaneN, (ymin+ymax)/2, zPlane);
-
-          if (tick_along_z)
-            p(2) += (signum(zPlane-zPlaneN)*fz*ytickoffset);
-          else
-            p(0) += (signum(xPlaneN-xPlane)*fx*ytickoffset);
-          p = xform.transform (p(0), p(1), p(2), false);
-          switch (ystate)
-            {
-              case AXE_ANY_DIR:
-                p(0) += (!xySym ? wmax : -wmax);
-                p(1) += (zd*zv(2) <= 0 ? hmax : -hmax);
-                break;
-              case AXE_VERT_DIR:
-                p(0) -= wmax;
-                angle = 90;
-                break;
-              case AXE_HORZ_DIR:
-                p(1) += hmax;
-                break;
-            }
-          p = xform.untransform(p(0), p(1), p(2), true);
-          ylabel_props.set_position (p.extract_n (0, 3).transpose ());
-          ylabel_props.set_rotation (angle);
-        }
-    }
-		
-  // Z Grid
-
-  if (zstate != AXE_DEPTH_DIR && visible)
-    {
-      bool do_zgrid = (props.is_zgrid () && (gridstyle != "none"));
-      bool do_zminorgrid = (props.is_zminorgrid () && (minorgridstyle != "none"));
-      bool do_zminortick = props.is_zminortick ();
-      Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ());
-      // FIXME: use pre-computed minor ticks
-      Matrix zmticks;
-      // FIXME: use zticklabels property
-      string_vector zticklabels;
-      int wmax = 0, hmax = 0;
-      Matrix tickpos (zticks.numel (), 3);
-
-      set_color (props.get_zcolor_rgb ());
-
-      // grid lines
-      if (do_zgrid)
-        {
-          set_linestyle (gridstyle, true);
-          glBegin (GL_LINES);
-          for (int i = 0; i < zticks.numel (); i++)
-            {
-              double zval = zticks(i);
-
-              glVertex3d (xPlaneN, yPlane, zval);
-              glVertex3d (xPlane, yPlane, zval);
-              glVertex3d (xPlane, yPlaneN, zval);
-              glVertex3d (xPlane, yPlane, zval);
-            }
-          glEnd ();
-          set_linestyle ("-", true);
-        }
-
-      // tick marks
-      if (xySym)
-        {
-          if (xisinf (fy))
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < zticks.numel (); i++)
-                {
-                  double zval = zticks(i);
-
-                  glVertex3d (xPlaneN, yPlane, zval);
-                  glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*zticklen*tickdir,
-                        yPlane, zval);
-                  if (box && zstate != AXE_ANY_DIR)
-                    {
-                      glVertex3d (xPlane, yPlane, zval);
-                      glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen*tickdir,
-                            yPlane, zval);
-                    }
-                  tickpos(i,0) = xPlaneN+signum(xPlaneN-xPlane)*fx*ztickoffset;
-                  tickpos(i,1) = yPlane;
-                  tickpos(i,2) = zval;
-                }
-              glEnd ();
-            }
-          else
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < zticks.numel (); i++)
-                {
-                  double zval = zticks(i);
-
-                  glVertex3d (xPlaneN, yPlane, zval);
-                  glVertex3d (xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*zticklen*tickdir, zval);
-                  tickpos(i,0) = xPlaneN;
-                  tickpos(i,1) = yPlane+signum(yPlane-yPlaneN)*fy*ztickoffset;
-                  tickpos(i,2) = zval;
-                }
-              glEnd ();
-            }
-        }
-      else
-        {
-          if (xisinf (fx))
-            {
-              glBegin (GL_LINES);
-              for (int i = 0; i < zticks.numel (); i++)
-                {
-                  double zval = zticks(i);
-
-                  glVertex3d (xPlane, yPlaneN, zval);
-                  glVertex3d (xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*zticklen*tickdir, zval);
-                  if (box && zstate != AXE_ANY_DIR)
-                    {
-                      glVertex3d (xPlane, yPlane, zval);
-                      glVertex3d (xPlane, yPlane+signum(yPlane-yPlaneN)*fy*zticklen*tickdir, zval);
-                    }
-                  tickpos(i,0) = xPlane;
-                  tickpos(i,1) = yPlaneN+signum(yPlaneN-yPlane)*fy*ztickoffset;
-                  tickpos(i,2) = zval;
-                }
-              glEnd ();
-            }
-          else
-          {
-            glBegin (GL_LINES);
-            for (int i = 0; i < zticks.numel (); i++)
-              {
-                double zval = zticks(i);
-
-                glVertex3d (xPlane, yPlaneN, zval);
-                glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen*tickdir, yPlaneN, zval);
-                tickpos(i,0) = xPlane+signum(xPlane-xPlaneN)*fx*ztickoffset;
-                tickpos(i,1) = yPlaneN;
-                tickpos(i,2) = zval;
-              }
-            glEnd ();
-          }
-        }
-
-      // FIXME: tick texts
-
-      // minor grid lines
-      if (do_zminorgrid)
-        {
-          set_linestyle (minorgridstyle, true);
-          glBegin (GL_LINES);
-          for (int i = 0; i < zmticks.numel (); i++)
-            {
-              double zval = zmticks(i);
-
-              glVertex3d (xPlaneN, yPlane, zval);
-              glVertex3d (xPlane, yPlane, zval);
-              glVertex3d (xPlane, yPlaneN, zval);
-              glVertex3d (xPlane, yPlane, zval);
-            }
-          glEnd ();
-          set_linestyle ("-", true);
-        }
-
-      // minor tick marks
-      if (do_zminortick)
-        {
-          if (xySym)
-            {
-              if (xisinf (fy))
-                {
-                  glBegin (GL_LINES);
-                  for (int i = 0; i < zmticks.numel (); i++)
-                    {
-                      double zval = zmticks(i);
-
-                      glVertex3d (xPlaneN, yPlane, zval);
-                      glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*zticklen/2*tickdir,
-                            yPlane, zval);
-                      if (box && zstate != AXE_ANY_DIR)
-                        {
-                          glVertex3d (xPlane, yPlane, zval);
-                          glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir,
-                                yPlane, zval);
-                        }
-                    }
-                  glEnd ();
-                }
-              else
-                {
-                  glBegin (GL_LINES);
-                  for (int i = 0; i < zmticks.numel (); i++)
-                    {
-                      double zval = zmticks(i);
-
-                      glVertex3d (xPlaneN, yPlane, zval);
-                      glVertex3d (xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, zval);
-                    }
-                  glEnd ();
-                }
-            }
-          else
-            {
-              if (xisinf (fx))
-                {
-                  glBegin (GL_LINES);
-                  for (int i = 0; i < zmticks.numel (); i++)
-                    {
-                      double zval = zmticks(i);
-
-                      glVertex3d (xPlane, yPlaneN, zval);
-                      glVertex3d (xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*zticklen/2*tickdir, zval);
-                      if (box && zstate != AXE_ANY_DIR)
-                        {
-                          glVertex3d (xPlane, yPlane, zval);
-                          glVertex3d (xPlane, yPlane+signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, zval);
-                        }
-                    }
-                  glEnd ();
-                }
-              else
-                {
-                  glBegin (GL_LINES);
-                  for (int i = 0; i < zmticks.numel (); i++)
-                    {
-                      double zval = zmticks(i);
-
-                      glVertex3d (xPlane, yPlaneN, zval);
-                      glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir, yPlaneN, zval);
-                    }
-                  glEnd ();
-                }
-            }
-        }
-
-      text::properties& zlabel_props =
-        reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_zlabel ()).get_properties ());
-
-      // FIXME: auto-positioning should be disabled if the 
-      //        label has been positioned manually
-      if (! zlabel_props.get_string ().empty ())
-        {
-          bool camAuto = props.cameraupvectormode_is ("auto");
-
-          zlabel_props.set_horizontalalignment ((zstate > AXE_DEPTH_DIR || camAuto) ? "center" : "right");
-	  zlabel_props.set_verticalalignment(zstate == AXE_VERT_DIR ? "bottom" : ((zd*zv(2) < 0 || camAuto) ? "bottom" : "top"));
-
-          double angle = 0;
-          ColumnVector p;
-
-          if (xySym)
-            {
-              p = graphics_xform::xform_vector (xPlaneN, yPlane, (zmin+zmax)/2);
-              if (xisinf (fy))
-                p(0) += (signum(xPlaneN-xPlane)*fx*ztickoffset);
-              else
-                p(1) += (signum(yPlane-yPlaneN)*fy*ztickoffset);
-            }
-          else
-            {
-              p = graphics_xform::xform_vector (xPlane, yPlaneN, (zmin+zmax)/2);
-              if (xisinf (fx))
-                p(1) += (signum(yPlaneN-yPlane)*fy*ztickoffset);
-              else
-                p(0) += (signum(xPlane-xPlaneN)*fx*ztickoffset);
-            }
-          p = xform.transform (p(0), p(1), p(2), false);
-          switch (zstate)
-            {
-              case AXE_ANY_DIR:
-                if (camAuto)
-                  {
-                    p(0) -= wmax;
-                    angle = 90;
-                  }
-                /* FIXME: what's the correct offset?
-                   p[0] += (!xySym ? wmax : -wmax);
-                   p[1] += (zd*zv[2] <= 0 ? hmax : -hmax);
-                   */
-                break;
-              case AXE_VERT_DIR:
-                p(0) -= wmax;
-                angle = 90;
-                break;
-              case AXE_HORZ_DIR:
-                p(1) += hmax;
-                break;
-            }
-          p = xform.untransform (p(0), p(1), p(2), true);
-          zlabel_props.set_position (p.extract_n (0, 3).transpose ());
-          zlabel_props.set_rotation (angle);
-        }
-    }
-
-  set_linestyle ("-");
-
-  // Title
-
-  text::properties& title_props =
-    reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_title ()).get_properties ());
-      
-  // FIXME: auto-positioning should be disabled if the 
-  //        title has been positioned manually
-  if (! title_props.get_string ().empty ())
-    {
-      Matrix bb = props.get_boundingbox (true);
-      ColumnVector p = xform.untransform (bb(0)+bb(2)/2, (bb(1)-10),
-          (x_zlim(0)+x_zlim(1))/2, true);
-      title_props.set_position (p.extract_n(0, 3).transpose ());
-    }
-
-  set_clipbox (xmin, xmax, ymin, ymax, zmin, zmax);
-
-  // Children
-
-  Matrix children = props.get_children ();
-  std::list<graphics_object> obj_list;
-  std::list<graphics_object>::iterator it;
-
-  // 1st pass: draw light objects
-
-  for (int i = 0; i < children.numel (); i++)
-    {
-      graphics_object go = gh_manager::get_object (children (i));
-
-      if (go.get_properties ().is_visible ())
-        {
-          if (go.isa ("light"))
-	    draw (go);
-          else
-            obj_list.push_back (go);
-        }
-    }
-
-  // 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
-
-  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
-}
-
-void
-opengl_renderer::draw (const line::properties& props)
-{
-  Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
-  Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
-  Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
-
-  bool has_z = (z.numel () > 0);
-  int n = static_cast<int> (::xmin (::xmin (x.numel (), y.numel ()), (has_z ? z.numel () : INT_MAX)));
-  octave_uint8 clip_mask = (props.is_clipping () ? 0x7F : 0x40), clip_ok (0x40);
-
-  std::vector<octave_uint8> 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 = (zmin+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"))
-    {
-      set_color (props.get_color_rgb ());
-      set_linestyle (props.get_linestyle (), false);
-      set_linewidth (props.get_linewidth ());
-
-      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;
-		      glBegin (GL_LINE_STRIP);
-		      glVertex3d (x(i-1), y(i-1), z(i-1));
-		    }
-		  glVertex3d (x(i), y(i), z(i));
-		}
-	      else if (flag)
-		{
-		  flag = false;
-		  glEnd ();
-		}
-	    }
-
-	  if (flag)
-	    glEnd ();
-	}
-      else
-	{
-	  bool flag = false;
-
-	  for (int i = 1; i < n; i++)
-	    {
-	      if ((clip[i-1] & clip[i]) == clip_ok)
-		{
-		  if (! flag)
-		    {
-		      flag = true;
-		      glBegin (GL_LINE_STRIP);
-		      glVertex2d (x(i-1), y(i-1));
-		    }
-		  glVertex2d (x(i), y(i));
-		}
-	      else if (flag)
-		{
-		  flag = false;
-		  glEnd ();
-		}
-	    }
-
-	  if (flag)
-	    glEnd ();
-	}
-	  
-      set_linewidth (0.5);
-      set_linestyle ("-");
-    }
-
-  set_clipping (false);
-
-  if (! props.marker_is ("none") &&
-      ! (props.markeredgecolor_is ("none")
-	 && props.markerfacecolor_is ("none")))
-    {
-      Matrix lc, fc;
-
-      if (props.markeredgecolor_is ("auto"))
-	lc = props.get_color_rgb ();
-      else if (! props.markeredgecolor_is ("none"))
-	lc = props.get_markeredgecolor_rgb ();
-
-      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), lc, fc);
-        }
-
-      end_marker ();
-    }
-
-  set_clipping (props.is_clipping ());
-}
-
-void
-opengl_renderer::draw (const surface::properties& props)
-{
-  Matrix x = xform.xscale (props.get_xdata ().matrix_value ());
-  Matrix y = xform.yscale (props.get_ydata ().matrix_value ());
-  Matrix z = xform.zscale (props.get_zdata ().matrix_value ());
-
-  int zr = z.rows (), zc = z.columns ();
-
-  NDArray c;
-  NDArray n = props.get_vertexnormals ().array_value ();
-
-  // FIXME: handle transparency
-  Matrix a;
-
-  if (props.facelighting_is ("phong") || props.edgelighting_is ("phong"))
-    warning ("opengl_renderer::draw: phong light model not supported");
-
-  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") ? 1 : 2));
-  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") ? 1 : 2));
-  int ea_mode = (props.edgealpha_is_double () ? 0 :
-		 (props.edgealpha_is ("flat") ? 1 : 2));
-
-  Matrix fcolor = (fc_mode == 3 ? Matrix (1, 3, 1.0) : 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 ();
-  float cb[4] = { 0, 0, 0, 1 };
-
-  opengl_texture tex;
-
-  int i1, i2, j1, j2;
-  bool x_mat = (x.rows () == z.rows ());
-  bool y_mat = (y.columns () == z.columns ());
-
-  i1 = i2 = j1 = j2 = 0;
-
-  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 ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0)
-    c = props.get_color_data ().array_value ();
-
-  if (fa_mode > 0 || ea_mode > 0)
-    {
-      // FIXME: implement alphadata conversion
-      //a = props.get_alpha_data ();
-    }
-
-  if (fl_mode > 0 || el_mode > 0)
-    {
-      float buf[4] = { ss, ss, ss, 1 };
-
-      glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
-      glMaterialf (LIGHT_MODE, GL_SHININESS, se);
-    }
-
-  // FIXME: good candidate for caching, transfering pixel
-  // data to OpenGL is time consuming.
-  if (fc_mode == 3)
-    tex = opengl_texture::create (props.get_color_data ());
-
-  if (! props.facecolor_is ("none"))
-    {
-      if (props.get_facealpha_double () == 1)
-	{
-	  if (fc_mode == 0 || fc_mode == 3)
-	    {
-	      glColor3dv (fcolor.data ());
-	      if (fl_mode > 0)
-		{
-		  for (int i = 0; i < 3; i++)
-		    cb[i] = (as * fcolor(i));
-		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-		  for (int i = 0; i < 3; i++)
-		    cb[i] *= (ds / as);
-		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-		}
-	    }
-
-	  if (fl_mode > 0)
-	    glEnable (GL_LIGHTING);
-	  glShadeModel ((fc_mode == 2 || fl_mode == 2) ? GL_SMOOTH : GL_FLAT);
-	  set_polygon_offset (true, 1);
-	  if (fc_mode == 3)
-	    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 (x_mat)
-		    {
-		      j1 = j-1;
-		      j2 = j;
-		    }
-
-		  glBegin (GL_QUADS);
-
-		  // Vertex 1
-		  if (fc_mode == 3)
-		    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);
-		      glColor3fv (cb);
-
-		      if (fl_mode > 0)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= as;
-			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-			  
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= (ds / as);
-			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			}
-		    }
-		  if (fl_mode > 0)
-		    glNormal3d (n(j-1,i-1,0), n(j-1,i-1,1), n(j-1,i-1,2));
-		  glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
-
-		  // Vertex 2
-		  if (fc_mode == 3)
-		    tex.tex_coord (double (i) / (zc-1), double (j-1) / (zr-1));
-		  else if (fc_mode == 2)
-		    {
-		      for (int k = 0; k < 3; k++)
-			cb[k] = c(j-1, i, k);
-		      glColor3fv (cb);
-
-		      if (fl_mode > 0)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= as;
-			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-			  
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= (ds / as);
-			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			}
-		    }
-		  if (fl_mode == 2)
-		    glNormal3d (n(j-1,i,0), n(j-1,i,1), n(j-1,i,2));
-		  glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
-		  
-		  // Vertex 3
-		  if (fc_mode == 3)
-		    tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1));
-		  else if (fc_mode == 2)
-		    {
-		      for (int k = 0; k < 3; k++)
-			cb[k] = c(j, i, k);
-		      glColor3fv (cb);
-
-		      if (fl_mode > 0)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= as;
-			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-			  
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= (ds / as);
-			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			}
-		    }
-		  if (fl_mode == 2)
-		    glNormal3d (n(j,i,0), n(j,i,1), n(j,i,2));
-		  glVertex3d (x(j2,i), y(j,i2), z(j,i));
-		  
-		  // Vertex 4
-		  if (fc_mode == 3)
-		    tex.tex_coord (double (i-1) / (zc-1), double (j) / (zr-1));
-		  else if (fc_mode == 2)
-		    {
-		      for (int k = 0; k < 3; k++)
-			cb[k] = c(j, i-1, k);
-		      glColor3fv (cb);
-
-		      if (fl_mode > 0)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= as;
-			  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-			  
-			  for (int k = 0; k < 3; k++)
-			    cb[k] *= (ds / as);
-			  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			}
-		    }
-		  if (fl_mode == 2)
-		    glNormal3d (n(j,i-1,0), n(j,i-1,1), n(j,i-1,2));
-		  glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
-
-		  glEnd ();
-		}
-	    }
-
-	  set_polygon_offset (false);
-	  if (fc_mode == 3)
-	    glDisable (GL_TEXTURE_2D);
-
-	  if (fl_mode > 0)
-	    glDisable (GL_LIGHTING);
-	}
-      else
-	{
-	  // FIXME: implement transparency
-	}
-    }
-
-  if (! props.edgecolor_is ("none"))
-    {
-      if (props.get_edgealpha_double () == 1)
-	{
-	  if (ec_mode == 0)
-	    {
-	      glColor3dv (ecolor.data ());
-	      if (fl_mode > 0)
-		{
-		  for (int i = 0; i < 3; i++)
-		    cb[i] = (as * ecolor(i));
-		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-		  for (int i = 0; i < 3; i++)
-		    cb[i] *= (ds / as);
-		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-		}
-	    }
-
-	  if (el_mode > 0)
-	    glEnable (GL_LIGHTING);
-	  glShadeModel ((ec_mode == 2 || el_mode == 2) ? GL_SMOOTH : GL_FLAT);
-
-	  set_linestyle (props.get_linestyle (), false);
-	  set_linewidth (props.get_linewidth ());
-
-	  // 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 (x_mat)
-			{
-			  j1 = j-1;
-			  j2 = j;
-			}
-
-		      glBegin (GL_LINES);
-
-		      // Vertex 1
-		      if (ec_mode > 0)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] = c(j-1, i, k);
-			  glColor3fv (cb);
-
-			  if (fl_mode > 0)
-			    {
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= as;
-			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= (ds / as);
-			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			    }
-			}
-		      if (el_mode > 0)
-			glNormal3d (n(j-1,i,0), n(j-1,i,1), n(j-1,i,2));
-		      glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
-
-		      // Vertex 2
-		      if (ec_mode == 2)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] = c(j, i, k);
-			  glColor3fv (cb);
-
-			  if (fl_mode > 0)
-			    {
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= as;
-			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= (ds / as);
-			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			    }
-			}
-		      if (el_mode == 2)
-			glNormal3d (n(j,i,0), n(j,i,1), n(j,i,2));
-		      glVertex3d (x(j2,i), y(j,i2), z(j,i));
-
-		      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 (y_mat)
-			{
-			  i1 = i-1;
-			  i2 = i;
-			}
-
-		      glBegin (GL_LINES);
-
-		      // Vertex 1
-		      if (ec_mode > 0)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] = c(j, i-1, k);
-			  glColor3fv (cb);
-
-			  if (fl_mode > 0)
-			    {
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= as;
-			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= (ds / as);
-			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			    }
-			}
-		      if (el_mode > 0)
-			glNormal3d (n(j,i-1,0), n(j,i-1,1), n(j,i-1,2));
-		      glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
-		      
-		      // Vertex 2
-		      if (ec_mode == 2)
-			{
-			  for (int k = 0; k < 3; k++)
-			    cb[k] = c(j, i, k);
-			  glColor3fv (cb);
-
-			  if (fl_mode > 0)
-			    {
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= as;
-			      glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-			      for (int k = 0; k < 3; k++)
-				cb[k] *= (ds / as);
-			      glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-			    }
-			}
-		      if (el_mode == 2)
-			glNormal3d (n(j,i,0), n(j,i,1), n(j,i,2));
-		      glVertex3d (x(j2,i), y(j,i2), z(j,i));
-		      
-		      glEnd ();
-		    }
-		}
-	    }
-
-	  set_linestyle ("-");
-	  set_linewidth (0.5);
-
-	  if (el_mode > 0)
-	    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 = ! props.markeredgecolor_is ("none");
-      bool do_face = ! props.markerfacecolor_is ("none");
-
-      Matrix mecolor = props.get_markeredgecolor_rgb ();
-      Matrix mfcolor = props.get_markerfacecolor_rgb ();
-      Matrix cc (1, 3, 0.0);
-
-      if (mecolor.numel () == 0 && props.markeredgecolor_is ("auto"))
-	{
-	  mecolor = props.get_edgecolor_rgb ();
-	  do_edge = ! props.edgecolor_is ("none");
-	}
-
-      if (mfcolor.numel () == 0 && props.markerfacecolor_is ("auto"))
-	{
-	  mfcolor = props.get_facecolor_rgb ();
-	  do_face = ! props.facecolor_is ("none");
-	}
-
-      if ((mecolor.numel () == 0 || mfcolor.numel () == 0)
-	  && c.numel () == 0)
-	c = props.get_color_data ().array_value ();
-
-      init_marker (props.get_marker (), props.get_markersize (),
-		   props.get_linewidth ());
-
-      for (int i = 0; i < zc; i++)
-	{
-	  if (y_mat)
-	    i1 = i;
-	  
-	  for (int j = 0; j < zr; j++)
-	    {
-	      if (clip(j,i))
-		continue;
-
-	      if (x_mat)
-		j1 = j;
-
-	      if ((do_edge && mecolor.numel () == 0)
-		  || (do_face && mfcolor.numel () == 0))
-		{
-		  for (int k = 0; k < 3; k++)
-		    cc(k) = c(j,i,k);
-		}
-
-	      Matrix lc = (do_edge ? (mecolor.numel () == 0 ? cc : mecolor) : Matrix ());
-	      Matrix fc = (do_face ? (mfcolor.numel () == 0 ? cc : mfcolor) : Matrix ());
-
-	      draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc);
-	    }
-	}
-
-      end_marker ();
-    }
-}
-
-// FIXME: global optimization (rendering, data structures...), there
-// is probably a smarter/faster/less-memory-consuming way to do this.
-void
-opengl_renderer::draw (const patch::properties &props)
-{
-  Matrix f = props.get_faces ().matrix_value ();
-  Matrix v = xform.scale (props.get_vertices ().matrix_value ());
-  Matrix c;
-  Matrix n = props.get_vertexnormals ().matrix_value ();
-  Matrix a;
-
-  int nv = v.rows ();
-  // int vmax = v.columns ();
-  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_rgb () ? 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_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));
-
-  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 ();
-
-  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 (nf, 0);
-
-  for (int i = 0; i < nf; i++)
-    {
-      bool fclip = false;
-      int count = 0;
-
-      for (int j = 0; j < fcmax && ! xisnan (f(i,j)); j++, count++)
-	fclip = (fclip || clip(int (f(i,j) - 1)));
-
-      clip_f(i) = fclip;
-      count_f(i) = count;
-    }
-
-  if (fc_mode > 0 || ec_mode > 0)
-    {
-      c = props.get_color_data ().matrix_value ();
-
-      if (c.rows () == 1)
-	{
-	  // Single color specifications, we can simplify a little bit
-	  
-	  if (fc_mode > 0)
-	    {
-	      fcolor = c;
-	      fc_mode = 0;
-	    }
-
-	  if (ec_mode > 0)
-	    {
-	      ecolor = c;
-	      ec_mode = 0;
-	    }
-
-	  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 ()));
-    }
-
-  Array2<vertex_data> vdata (f.dims ());
-
-  for (int i = 0; i < nf; i++)
-    for (int j = 0; j < count_f(i); j++)
-      {
-	int idx = int (f(i,j) - 1);
-
-	Matrix vv (1, 3, 0.0);
-	Matrix cc;
-	Matrix nn(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);
-	// FIXME: uncomment when patch object has normal computation
-	//nn(0) = n(idx,0); nn(1) = n(idx,1); nn(2) = n(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 (a.numel () > 0)
-	  {
-	    if (has_facealpha)
-	      aa = a(i);
-	    else
-	      aa = a(idx);
-	  }
-
-	vdata(i,j) =
-	    vertex_data (vv, cc, nn, aa, as, ds, ss, se);
-      }
-
-  if (fl_mode > 0 || el_mode > 0)
-    {
-      float buf[4] = { ss, ss, ss, 1 };
-
-      glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
-      glMaterialf (LIGHT_MODE, GL_SHININESS, se);
-    }
-
-  if (! props.facecolor_is ("none"))
-    {
-      // FIXME: adapt to double-radio property
-      if (props.get_facealpha_double () == 1)
-	{
-	  if (fc_mode == 0)
-	    {
-	      glColor3dv (fcolor.data ());
-	      if (fl_mode > 0)
-		{
-		  float cb[4] = { 0, 0, 0, 1 };
-
-		  for (int i = 0; i < 3; i++)
-		    cb[i] = (as * fcolor(i));
-		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-		  for (int i = 0; i < 3; i++)
-		    cb[i] *= (ds / as);
-		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-		}
-	    }
-
-	  if (fl_mode > 0)
-	    glEnable (GL_LIGHTING);
-
-	  // FIXME: use __index__ property from patch object
-	  patch_tesselator tess (this, fc_mode, fl_mode, 0);
-
-	  for (int i = 0; i < nf; i++)
-	    {
-	      if (clip_f(i))
-		continue;
-
-	      tess.begin_polygon (true);
-	      tess.begin_contour ();
-
-	      for (int j = 0; j < count_f(i); j++)
-		{
-		  vertex_data::vertex_data_rep *vv = vdata(i,j).get_rep ();
-	
-		  tess.add_vertex (vv->coords.fortran_vec (), vv);
-		}
-
-	      tess.end_contour ();
-	      tess.end_polygon ();
-	    }
-
-	  if (fl_mode > 0)
-	    glDisable (GL_LIGHTING);
-	}
-      else
-	{
-	  // FIXME: implement transparency
-	}
-    }
-
-  if (! props.edgecolor_is ("none"))
-    {
-      // FIXME: adapt to double-radio property
-      if (props.get_edgealpha_double () == 1)
-	{
-	  if (ec_mode == 0)
-	    {
-	      glColor3dv (ecolor.data ());
-	      if (el_mode > 0)
-		{
-		  float cb[4] = { 0, 0, 0, 1 };
-
-		  for (int i = 0; i < 3; i++)
-		    cb[i] = (as * ecolor(i));
-		  glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
-
-		  for (int i = 0; i < 3; i++)
-		    cb[i] *= (ds / as);
-		  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
-		}
-	    }
-
-	  if (el_mode > 0)
-	    glEnable (GL_LIGHTING);
-
-	  set_linestyle (props.get_linestyle (), false);
-	  set_linewidth (props.get_linewidth ());
-
-	  // FIXME: use __index__ property from patch object; should we
-	  // offset patch contour as well?
-	  patch_tesselator tess (this, ec_mode, el_mode);
-
-	  for (int i = 0; i < nf; i++)
-	    {
-	      if (clip_f(i))
-		continue;
-
-	      tess.begin_polygon (false);
-	      tess.begin_contour ();
-
-	      for (int j = 0; j < count_f(i); j++)
-		{
-		  vertex_data::vertex_data_rep *vv = vdata(i,j).get_rep ();
-	
-		  tess.add_vertex (vv->coords.fortran_vec (), vv);
-		}
-
-	      tess.end_contour ();
-	      tess.end_polygon ();
-	    }
-
-	  set_linestyle ("-");
-	  set_linewidth (0.5);
-
-	  if (el_mode > 0)
-	    glDisable (GL_LIGHTING);
-	}
-      else
-	{
-	  // FIXME: implement transparency
-	}
-    }
-
-  if (! props.marker_is ("none") &&
-      ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none")))
-    {
-      bool do_edge = ! props.markeredgecolor_is ("none");
-      bool do_face = ! props.markerfacecolor_is ("none");
-
-      Matrix mecolor = props.get_markeredgecolor_rgb ();
-      Matrix mfcolor = props.get_markerfacecolor_rgb ();
-      Matrix cc (1, 3, 0.0);
-
-      if (mecolor.numel () == 0 && props.markeredgecolor_is ("auto"))
-	{
-	  mecolor = props.get_edgecolor_rgb ();
-	  do_edge = ! props.edgecolor_is ("none");
-	}
-
-      if (mfcolor.numel () == 0 && props.markerfacecolor_is ("auto"))
-	{
-	  mfcolor = props.get_facecolor_rgb ();
-	  do_face = ! props.facecolor_is ("none");
-	}
-
-      init_marker (props.get_marker (), props.get_markersize (),
-		   props.get_linewidth ());
-
-      for (int i = 0; i < nf; i++)
-	for (int j = 0; j < count_f(i); j++)
-	  {
-	    int idx = int (f(i,j) - 1);
-
-	    if (clip(idx))
-	      continue;
-
-	    Matrix lc = (do_edge ? (mecolor.numel () == 0 ?
-				    vdata(i,j).get_rep ()->color : mecolor)
-			 : Matrix ());
-	    Matrix fc = (do_face ? (mfcolor.numel () == 0 ?
-				    vdata(i,j).get_rep ()->color : mfcolor)
-			 : Matrix ());
-
-	    draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc);
-	  }
-
-      end_marker ();
-    }
-}
-
-void
-opengl_renderer::draw (const hggroup::properties &props)
-{
-  draw (props.get_children ());
-}
-
-void
-opengl_renderer::set_viewport (int w, int h)
-{
-  glViewport (0, 0, w, h);
-}
-
-void
-opengl_renderer::set_color (const Matrix& c)
-{
-  glColor3dv (c.data ());
-}
-
-void
-opengl_renderer::set_polygon_offset (bool on, double offset)
-{
-  if (on)
-    {
-      glPolygonOffset (offset, offset);
-      glEnable (GL_POLYGON_OFFSET_FILL);
-    }
-  else
-    glDisable (GL_POLYGON_OFFSET_FILL);
-}
-
-void
-opengl_renderer::set_linewidth (float w)
-{
-  glLineWidth (w);
-}
-
-void
-opengl_renderer::set_linestyle (const std::string& s, bool use_stipple)
-{
-  bool solid = false;
-
-  if (s == "-")
-    {
-      glLineStipple (1, static_cast<unsigned short> (0xFFFF));
-      solid = true;
-    }
-  else if (s == ":")
-    glLineStipple (1, static_cast<unsigned short> (0x8888));
-  else if (s == "--")
-    glLineStipple (1, static_cast<unsigned short> (0x0FFF));
-  else if (s == "-.")
-    glLineStipple (1, static_cast<unsigned short> (0x020F));
-  else
-    glLineStipple (1, static_cast<unsigned short> (0x0000));
-
-  if (solid && ! use_stipple)
-    glDisable (GL_LINE_STIPPLE);
-  else
-    glEnable (GL_LINE_STIPPLE);
-}
-
-void
-opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2,
-			      double z1, double 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;
-  glClipPlane (GL_CLIP_PLANE0, p.data ());
-  p(0) = 1; p(3) = -x1;
-  glClipPlane (GL_CLIP_PLANE1, p.data ());
-  p(0) = 0; p(1) = -1; p(3) = y2;
-  glClipPlane (GL_CLIP_PLANE2, p.data ());
-  p(1) = 1; p(3) = -y1;
-  glClipPlane (GL_CLIP_PLANE3, p.data ());
-  p(1) = 0; p(2) = -1; p(3) = z2;
-  glClipPlane (GL_CLIP_PLANE4, p.data ());
-  p(2) = 1; p(3) = -z1;
-  glClipPlane (GL_CLIP_PLANE5, p.data ());
-
-  xmin = x1; xmax = x2;
-  ymin = y1; ymax = y2;
-  zmin = z1; zmax = z2;
-}
-
-void
-opengl_renderer::set_clipping (bool enable)
-{
-  bool has_clipping = (glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
-
-  if (enable != has_clipping)
-    {
-      if (enable)
-        for (int i = 0; i < 6; i++)
-          glEnable (GL_CLIP_PLANE0+i);
-      else
-        for (int i = 0; i < 6; i++)
-          glDisable (GL_CLIP_PLANE0+i);
-    }
-}
-
-void
-opengl_renderer::init_marker (const std::string& m, double size, float width)
-{
-  int vw[4];
-
-  glGetIntegerv (GL_VIEWPORT, vw);
-
-  glMatrixMode (GL_PROJECTION);
-  glPushMatrix ();
-  glLoadIdentity ();
-  glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
-  glMatrixMode (GL_MODELVIEW);
-  glPushMatrix ();
-
-  set_clipping (false);
-  set_linewidth (width);
-
-  marker_id = make_marker_list (m, size, false);
-  filled_marker_id = make_marker_list (m, size, true);
-}
-
-void
-opengl_renderer::end_marker (void)
-{
-  glDeleteLists (marker_id, 1);
-  glDeleteLists (filled_marker_id, 1);
-
-  glMatrixMode (GL_MODELVIEW);
-  glPopMatrix ();
-  glMatrixMode (GL_PROJECTION);
-  glPopMatrix ();
-  set_linewidth (0.5f);
-}
-
-void
-opengl_renderer::draw_marker (double x, double y, double z,
-			      const Matrix& lc, const Matrix& fc)
-{
-  ColumnVector tmp = xform.transform (x, y, z, false);
-  
-  glLoadIdentity ();
-  glTranslated (tmp(0), tmp(1), -tmp(2));
-
-  if (fc.numel () > 0)
-    {
-      glColor3dv (fc.data ());
-      set_polygon_offset (true, 1.0);
-      glCallList (filled_marker_id);
-      set_polygon_offset (false);
-    }
-
-  if (lc.numel () > 0)
-    {
-      glColor3dv (lc.data ());
-      glCallList (marker_id);
-    }
-}
-
-unsigned int
-opengl_renderer::make_marker_list (const std::string& marker, double size,
-				   bool filled) const
-{
-  unsigned int ID = glGenLists (1);
-  double sz = size * backend.get_screen_resolution () / 72.0;
-
-  // constants for the * marker
-  const double sqrt2d4 = 0.35355339059327;
-  double tt = sz*sqrt2d4;
-
-  glNewList (ID, GL_COMPILE);
-
-  switch (marker[0])
-    {
-    case '+':
-      glBegin (GL_LINES);
-      glVertex2f (-sz/2 ,0     );
-      glVertex2f (sz/2  ,0     );
-      glVertex2f (0     ,-sz/2 );
-      glVertex2f (0     ,sz/2  );
-      glEnd ();
-      break;
-    case 'x':
-      glBegin(GL_LINES);
-      glVertex2f (-sz/2 ,-sz/2);
-      glVertex2f (sz/2  ,sz/2 );
-      glVertex2f (-sz/2 ,sz/2 );
-      glVertex2f (sz/2 ,-sz/2 );
-      glEnd ();
-      break;
-    case '*':
-      glBegin (GL_LINES);
-      glVertex2f (-sz/2 ,0     );
-      glVertex2f (sz/2  ,0     );
-      glVertex2f (0     ,-sz/2 );
-      glVertex2f (0     ,sz/2  );
-      glVertex2f (-tt   ,-tt   );
-      glVertex2f (+tt   ,+tt   );
-      glVertex2f (-tt   ,+tt   );
-      glVertex2f (+tt   ,-tt   );
-      glEnd ();
-      break;
-    case '.':
-      glBegin (GL_POLYGON);
-      glVertex2f (-sz/10, -sz/10);
-      glVertex2f (-sz/10, sz/10 );
-      glVertex2f (sz/10 , sz/10 );
-      glVertex2f (sz/10 , -sz/10);
-      glEnd ();
-      break;
-    case 's':
-      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-      glVertex2d (-sz/2, -sz/2);
-      glVertex2d (-sz/2,  sz/2);
-      glVertex2d ( sz/2,  sz/2);
-      glVertex2d ( sz/2, -sz/2);
-      glEnd();
-      break;
-    case 'o':
-      {
-	double ang_step = M_PI / 5;
-
-	glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-	for (double ang = 0; ang < (2*M_PI); ang += ang_step)
-	  glVertex2d (sz*cos(ang)/2, sz*sin(ang)/2);
-	glEnd ();
-      }
-      break;
-    case 'd':
-      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-      glVertex2d (    0, -sz/2);
-      glVertex2d ( sz/2,     0);
-      glVertex2d (    0,  sz/2);
-      glVertex2d (-sz/2,     0);
-      glEnd();
-      break;
-    case '^':
-      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-      glVertex2f (0     ,  sz/2);
-      glVertex2f (sz/2  , -sz/2);
-      glVertex2f (-sz/2 , -sz/2);
-      glEnd ();
-      break;
-    case 'v':
-      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-      glVertex2f (0     ,-sz/2);
-      glVertex2f (-sz/2 ,sz/2 );
-      glVertex2f (sz/2  ,sz/2 );
-      glEnd ();
-      break;
-    case '>':
-      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-      glVertex2f (sz/2  ,0    );
-      glVertex2f (-sz/2 ,sz/2 );
-      glVertex2f (-sz/2 ,-sz/2);
-      glEnd ();
-      break;
-    case '<':
-      glBegin ((filled ? GL_POLYGON : GL_LINE_LOOP));
-      glVertex2f (-sz/2 ,0    );
-      glVertex2f (sz/2  ,-sz/2);
-      glVertex2f (sz/2  ,sz/2 );
-      glEnd ();
-    default:
-      warning ("opengl_renderer: unsupported marker `%s'",
-	       marker.c_str ());
-      break;
-    }
-
-  glEndList ();
-
-  return ID;
-}
--- a/src/graphics/opengl/gl-render.h	Thu Jun 05 20:42:14 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/*
-
-Copyright (C) 2008 Michael Goffioul
-
-This file is part of Octave.
-
-Octave is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 3 of the License, or (at your
-option) any later version.
-
-Octave is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with Octave; see the file COPYING.  If not, see
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#if !defined (gl_render_h)
-#define gl_render_h 1
-
-#include "config.h"
-
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-#include <GL/gl.h>
-#include <GL/glu.h>
-
-#include "graphics.h"
-
-class
-OCTGRAPHICS_API
-opengl_renderer
-{
-public:
-  opengl_renderer (void) { }
-
-  virtual ~opengl_renderer (void) { }
-
-  virtual void draw (const graphics_handle& h)
-    { draw (gh_manager::get_object (h)); }
-
-  virtual void draw (const graphics_object& go);
-
-  virtual void draw (const Matrix& hlist)
-    {
-      int len = hlist.length ();
-
-      for (int i = 0; i < len; i++)
-	{
-	  graphics_handle h = gh_manager::lookup (hlist(i));
-
-	  if (h.ok ())
-	    draw (h);
-	}
-    }
-
-  virtual void set_viewport (int w, int h);
-
-protected:
-  virtual void draw (const figure::properties& props);
-  virtual void draw (const axes::properties& props);
-  virtual void draw (const line::properties& props);
-  virtual void draw (const surface::properties& props);
-  virtual void draw (const patch::properties& props);
-  virtual void draw (const hggroup::properties& props);
-
-  virtual void set_color (const Matrix& c);
-  virtual void set_polygon_offset (bool on, double offset = 0.0);
-  virtual void set_linewidth (float w);
-  virtual void set_linestyle (const std::string& s, bool stipple = false);
-  virtual void set_clipbox (double x1, double x2, double y1, double y2,
-			    double z1, double z2);
-  virtual void set_clipping (bool on);
-
-  virtual void init_marker (const std::string& m, double size, float width);
-  virtual void end_marker (void);
-  virtual void draw_marker (double x, double y, double z,
-			    const Matrix& lc, const Matrix& fc);
-
-private:
-  opengl_renderer (const opengl_renderer&) { }
-
-  opengl_renderer& operator = (const opengl_renderer&)
-    { return *this; }
-
-  bool is_nan_or_inf (double x, double y, double z) const
-    {
-      return (xisnan (x) || xisnan (y) || xisnan (z)
-	      || xisinf (x) || xisinf (y) || xisinf (z));
-    }
-
-  octave_uint8 clip_code (double x, double y, double z) const
-    {
-      return ((x < xmin ? 1 : 0)
-	      | (x > xmax ? 1 : 0) << 1
-	      | (y < ymin ? 1 : 0) << 2
-	      | (y > ymax ? 1 : 0) << 3
-	      | (z < zmin ? 1 : 0) << 4
-	      | (z > zmax ? 1 : 0) << 5
-	      | (is_nan_or_inf (x, y, z) ? 0 : 1) << 6);
-    }
-
-  unsigned int make_marker_list (const std::string& m, double size,
-				 bool filled) const;
-
-private:
-  // the backend associated with the figure being rendered
-  graphics_backend backend;
-
-  // axes transformation data
-  graphics_xform xform;
-
-  // axis limits in model scaled coordinate
-  double xmin, xmax;
-  double ymin, ymax;
-  double zmin, zmax;
-
-  // Z projection limits in windows coordinate
-  double xZ1, xZ2;
-
-  // call lists identifiers for markers
-  unsigned int marker_id, filled_marker_id;
-
-  // camera information for primitive sorting
-  ColumnVector camera_pos, camera_dir;
-
-private:
-  class patch_tesselator;
-};
-
-#endif