# HG changeset patch
# User mmuetzel <markus.muetzel@gmx.de>
# Date 1458312673 -3600
#      Fri Mar 18 15:51:13 2016 +0100
# Node ID 68d064da798d8c6e2706c7349fb07c7c89d20501
# Parent  57dea7b5ca469a3f5a7bbeea010b913e0fd154a2
implement object class "light"

* graphics.in.h: Define new class light.
* graphics.cc: Add function for default_light_position. Add light to
list of possible objects in make_graphics_object_from_type. Add default
properties for light to map in root_figure::init_factory_properties.
Export function __go_light__ for creation of light objects.
* gl-render.h: Declare opengl_renderer::draw_light. Add private property
view_vector for lighting mode "reverselit".
* gl-render.cc: Add draw_light to opengl_renderer::draw. Set view_vector
before drawing anything. Set OpenGL ambient light color before drawing
axes children. Set normals of OpenGL faces in patch objects if possible.
Face normals in patch objects towards viewer for lighting mode
"reverselit". Only enable OpenGL lighting if normals are defined for
patch. Add OpenGL light source at given position with given color (for
diffuse and specular reflex).
* light.m: New function to add light object to axes.
* __unimplemented__.m: Remove "light" from list.
* __gnuplot_draw_axes__.m: Do not throw an error if light object is
present.

diff -r 57dea7b5ca46 -r 68d064da798d libinterp/corefcn/gl-render.cc
--- a/libinterp/corefcn/gl-render.cc	Fri Mar 18 09:33:43 2016 +0100
+++ b/libinterp/corefcn/gl-render.cc	Fri Mar 18 15:51:13 2016 +0100
@@ -624,6 +624,8 @@
     draw_surface (dynamic_cast<const surface::properties&> (props));
   else if (go.isa ("patch"))
     draw_patch (dynamic_cast<const patch::properties&> (props));
+  else if (go.isa ("light"))
+    draw_light (dynamic_cast<const light::properties&> (props));
   else if (go.isa ("hggroup"))
     draw_hggroup (dynamic_cast<const hggroup::properties&> (props));
   else if (go.isa ("text"))
@@ -1509,6 +1511,9 @@
 
   // 1st pass: draw light objects
 
+  // save camera position for lighting before painting anything
+  view_vector = props.get_cameraposition ().matrix_value ();
+
   // Start with the last element of the array of child objects to
   // display them in the order they were added to the array.
 
@@ -1625,6 +1630,13 @@
   if (antialias == GL_TRUE)
     glEnable (GL_LINE_SMOOTH);
 
+  // set ambient light color before drawing axes children
+  float cb[4] = { 1.0, 1.0, 1.0, 1.0 };
+  Matrix ambient_lcolor = props.get_ambientlightcolor_rgb ();
+  for (int i = 0; i < 3; i++)
+    cb[i] = ambient_lcolor(i);
+  glLightfv (GL_LIGHT0, GL_AMBIENT, cb);
+
   draw_axes_children (props);
 
 #else
@@ -1804,6 +1816,9 @@
                  (props.edgelighting_is ("flat") ? 1 : 2));
   int ea_mode = (props.edgealpha_is_double () ? 0 :
                  (props.edgealpha_is ("flat") ? 1 : 2));
+  int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
+                  (props.backfacelighting_is ("reverselit") ? 1 : 2));
+
 
   Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0)
                                       : props.get_facecolor_rgb ());
@@ -1953,9 +1968,15 @@
                       d = sqrt (n(j-1,i-1,0) * n(j-1,i-1,0)
                                 + n(j-1,i-1,1) * n(j-1,i-1,1)
                                 + n(j-1,i-1,2) * n(j-1,i-1,2));
-                      glNormal3d (n(j-1,i-1,0)/d,
-                                  n(j-1,i-1,1)/d,
-                                  n(j-1,i-1,2)/d);
+                      double dir = 1.0;
+                      if (bfl_mode > 0)
+                        dir = (n(j-1,i-1,0) * view_vector(0) + 
+                               n(j-1,i-1,1) * view_vector(1) + 
+                               n(j-1,i-1,2) * view_vector(2) < 0) ? 
+                                ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                      glNormal3d (dir * n(j-1,i-1,0)/d,
+                                  dir * n(j-1,i-1,1)/d,
+                                  dir * n(j-1,i-1,2)/d);
                     }
                   glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
 
@@ -1985,7 +2006,15 @@
                       d = sqrt (n(j-1,i,0) * n(j-1,i,0)
                                 + n(j-1,i,1) * n(j-1,i,1)
                                 + n(j-1,i,2) * n(j-1,i,2));
-                      glNormal3d (n(j-1,i,0)/d, n(j-1,i,1)/d, n(j-1,i,2)/d);
+                      double dir = 1.0;
+                      if (bfl_mode > 0)
+                        dir = (n(j-1,i,0) * view_vector(0) + 
+                               n(j-1,i,1) * view_vector(1) + 
+                               n(j-1,i,2) * view_vector(2) < 0) ? 
+                                ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                      glNormal3d (dir * n(j-1,i,0)/d,
+                                  dir * n(j-1,i,1)/d,
+                                  dir * n(j-1,i,2)/d);
                     }
 
                   glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
@@ -2015,7 +2044,15 @@
                       d = sqrt (n(j,i,0) * n(j,i,0)
                                 + n(j,i,1) * n(j,i,1)
                                 + n(j,i,2) * n(j,i,2));
-                      glNormal3d (n(j,i,0)/d, n(j,i,1)/d, n(j,i,2)/d);
+                      double dir = 1.0;
+                      if (bfl_mode > 0)
+                        dir = (n(j,i,0) * view_vector(0) + 
+                               n(j,i,1) * view_vector(1) + 
+                               n(j,i,2) * view_vector(2) < 0) ? 
+                                ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                      glNormal3d (dir * n(j,i,0)/d,
+                                  dir * n(j,i,1)/d,
+                                  dir * n(j,i,2)/d);
                     }
                   glVertex3d (x(j2,i), y(j,i2), z(j,i));
 
@@ -2044,7 +2081,15 @@
                       d = sqrt (n(j,i-1,0) * n(j,i-1,0)
                                 + n(j,i-1,1) * n(j,i-1,1)
                                 + n(j,i-1,2) * n(j,i-1,2));
-                      glNormal3d (n(j,i-1,0)/d, n(j,i-1,1)/d, n(j,i-1,2)/d);
+                      double dir = 1.0;
+                      if (bfl_mode > 0)
+                        dir = (n(j,i-1,0) * view_vector(0) + 
+                               n(j,i-1,1) * view_vector(1) + 
+                               n(j,i-1,2) * view_vector(2) < 0) ? 
+                                ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                      glNormal3d (dir * n(j,i-1,0)/d,
+                                  dir * n(j,i-1,1)/d,
+                                  dir * n(j,i-1,2)/d);
                     }
                   glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
 
@@ -2153,7 +2198,15 @@
                           d = sqrt (n(j-1,i,0) * n(j-1,i,0)
                                     + n(j-1,i,1) * n(j-1,i,1)
                                     + n(j-1,i,2) * n(j-1,i,2));
-                          glNormal3d (n(j-1,i,0)/d, n(j-1,i,1)/d, n(j-1,i,2)/d);
+                          double dir = 1.0;
+                          if (bfl_mode > 0)
+                            dir = (n(j-1,i,0) * view_vector(0) + 
+                                   n(j-1,i,1) * view_vector(1) + 
+                                   n(j-1,i,2) * view_vector(2) < 0) ? 
+                                    ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                          glNormal3d (dir * n(j-1,i,0)/d,
+                                      dir * n(j-1,i,1)/d,
+                                      dir * n(j-1,i,2)/d);
                         }
                       glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
 
@@ -2180,7 +2233,15 @@
                           d = sqrt (n(j,i,0) * n(j,i,0)
                                     + n(j,i,1) * n(j,i,1)
                                     + n(j,i,2) * n(j,i,2));
-                          glNormal3d (n(j,i,0)/d, n(j,i,1)/d, n(j,i,2)/d);
+                          double dir = 1.0;
+                          if (bfl_mode > 0)
+                            dir = (n(j,i,0) * view_vector(0) + 
+                                   n(j,i,1) * view_vector(1) + 
+                                   n(j,i,2) * view_vector(2) < 0) ? 
+                                    ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                          glNormal3d (dir * n(j,i,0)/d,
+                                      dir * n(j,i,1)/d,
+                                      dir * n(j,i,2)/d);
                         }
                       glVertex3d (x(j2,i), y(j,i2), z(j,i));
 
@@ -2250,7 +2311,15 @@
                           d = sqrt (n(j,i-1,0) * n(j,i-1,0)
                                     + n(j,i-1,1) * n(j,i-1,1)
                                     + n(j,i-1,2) * n(j,i-1,2));
-                          glNormal3d (n(j,i-1,0)/d, n(j,i-1,1)/d, n(j,i-1,2)/d);
+                          double dir = 1.0;
+                          if (bfl_mode > 0)
+                            dir = (n(j,i-1,0) * view_vector(0) + 
+                                   n(j,i-1,1) * view_vector(1) + 
+                                   n(j,i-1,2) * view_vector(2) < 0) ?
+                                    ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                          glNormal3d (dir * n(j,i-1,0)/d,
+                                      dir * n(j,i-1,1)/d,
+                                      dir * n(j,i-1,2)/d);
                         }
                       glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
 
@@ -2277,7 +2346,15 @@
                           d = sqrt (n(j,i,0) * n(j,i,0)
                                     + n(j,i,1) * n(j,i,1)
                                     + n(j,i,2) * n(j,i,2));
-                          glNormal3d (n(j,i,0)/d, n(j,i,1)/d, n(j,i,2)/d);
+                          double dir = 1.0;
+                          if (bfl_mode > 0)
+                            dir = (n(j,i,0) * view_vector(0) + 
+                                   n(j,i,1) * view_vector(1) + 
+                                   n(j,i,2) * view_vector(2) < 0) ? 
+                                    ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+                          glNormal3d (dir * n(j,i,0)/d,
+                                      dir * n(j,i,1)/d,
+                                      dir * n(j,i,2)/d);
                         }
                       glVertex3d (x(j2,i), y(j,i2), z(j,i));
 
@@ -2402,6 +2479,8 @@
   bool has_z = (v.columns () > 2);
   bool has_facecolor = false;
   bool has_facealpha = false;
+  // FIXME: remove when patch object has normal computation (patch #8951)
+  bool has_normals = (n.rows () == nv);
 
   int fc_mode = ((props.facecolor_is ("none")
                   || props.facecolor_is_rgb ()) ? 0 :
@@ -2417,6 +2496,8 @@
                  (props.edgelighting_is ("flat") ? 1 : 2));
   int ea_mode = (props.edgealpha_is_double () ? 0 :
                  (props.edgealpha_is ("flat") ? 1 : 2));
+  int bfl_mode = (props.backfacelighting_is ("lit") ? 0 :
+                  (props.backfacelighting_is ("reverselit") ? 1 : 2));
 
   Matrix fcolor = props.get_facecolor_rgb ();
   Matrix ecolor = props.get_edgecolor_rgb ();
@@ -2499,8 +2580,18 @@
         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 (has_normals)
+          {
+            double dir = 1.0;
+            if (bfl_mode > 0)
+              dir = (n(idx,0) * view_vector(0) + 
+                     n(idx,1) * view_vector(1) + 
+                     n(idx,2) * view_vector(2) < 0) ?
+                      ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0;
+            nn(0) = dir * n(idx,0);
+            nn(1) = dir * n(idx,1);
+            nn(2) = dir * n(idx,2);
+          }
         if (c.numel () > 0)
           {
             cc.resize (1, 3);
@@ -2550,7 +2641,7 @@
                 }
             }
 
-          if ((fl_mode > 0) && has_light)
+          if ((fl_mode > 0) && has_light && has_normals)
             glEnable (GL_LIGHTING);
 
           // NOTE: Push filled part of patch backwards to avoid Z-fighting with
@@ -2609,7 +2700,7 @@
               tess.end_polygon ();
             }
 
-          if ((fl_mode > 0) && has_light)
+          if ((fl_mode > 0) && has_light && has_normals)
             glDisable (GL_LIGHTING);
         }
       else
@@ -2640,7 +2731,7 @@
                 }
             }
 
-          if ((el_mode > 0) && has_light)
+          if ((el_mode > 0) && has_light && has_normals)
             glEnable (GL_LIGHTING);
 
           set_linestyle (props.get_linestyle (), false);
@@ -2732,7 +2823,7 @@
           set_linestyle ("-");
           set_linewidth (0.5);
 
-          if ((el_mode > 0) && has_light)
+          if ((el_mode > 0) && has_light && has_normals)
             glDisable (GL_LIGHTING);
         }
       else
@@ -2818,6 +2909,37 @@
 }
 
 void
+opengl_renderer::draw_light (const light::properties &props)
+{
+#if defined (HAVE_OPENGL)
+
+  // enable light source
+  glEnable (GL_LIGHT0); // Ambient lighting also is in GL_LIGHT0
+  
+  // light position
+  float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,attenuation
+  Matrix lpos = props.get_position ().matrix_value ();
+  for (int i = 0; i < 3; i++)
+    pos[i] = lpos(i);
+  glLightfv (GL_LIGHT0, GL_POSITION, pos);
+
+  // light color
+  float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning)
+  Matrix lcolor = props.get_color ().matrix_value ();
+  for (int i = 0; i < 3; i++)
+    col[i] = lcolor(i);
+  glLightfv (GL_LIGHT0, GL_DIFFUSE,  col);
+  glLightfv (GL_LIGHT0, GL_SPECULAR, col);
+
+#else
+  // This shouldn't happen because construction of opengl_renderer
+  // objects is supposed to be impossible if OpenGL is not available.
+
+  panic_impossible ();
+#endif
+}
+
+void
 opengl_renderer::draw_hggroup (const hggroup::properties &props)
 {
   draw (props.get_children ());
diff -r 57dea7b5ca46 -r 68d064da798d libinterp/corefcn/gl-render.h
--- a/libinterp/corefcn/gl-render.h	Fri Mar 18 09:33:43 2016 +0100
+++ b/libinterp/corefcn/gl-render.h	Fri Mar 18 15:51:13 2016 +0100
@@ -64,6 +64,7 @@
   virtual void draw_line (const line::properties& props);
   virtual void draw_surface (const surface::properties& props);
   virtual void draw_patch (const patch::properties& props);
+  virtual void draw_light (const light::properties& props);
   virtual void draw_hggroup (const hggroup::properties& props);
   virtual void draw_text (const text::properties& props);
   virtual void draw_image (const image::properties& props);
@@ -183,8 +184,8 @@
   // call lists identifiers for markers
   unsigned int marker_id, filled_marker_id;
 
-  // camera information for primitive sorting
-  ColumnVector camera_pos, camera_dir;
+  // camera information for primitive sorting and lighting
+  ColumnVector camera_pos, camera_dir, view_vector;
 
   // interpreter to be used by text_to_pixels
   caseless_str interpreter;
diff -r 57dea7b5ca46 -r 68d064da798d libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Fri Mar 18 09:33:43 2016 +0100
+++ b/libinterp/corefcn/graphics.cc	Fri Mar 18 15:51:13 2016 +0100
@@ -535,6 +535,18 @@
   return retval;
 }
 
+static Matrix
+default_light_position (void)
+{
+  Matrix m (1, 3);
+
+  m(0) = 1.0;
+  m(1) = 0.0;
+  m(2) = 1.0;
+
+  return m;
+}
+
 static double
 convert_font_size (double font_size, const caseless_str& from_units,
                    const caseless_str& to_units, double parent_height = 0)
@@ -1097,6 +1109,8 @@
     go = new text (h, p);
   else if (type.compare ("image"))
     go = new image (h, p);
+  else if (type.compare ("light"))
+    go = new light (h, p);
   else if (type.compare ("patch"))
     go = new patch (h, p);
   else if (type.compare ("surface"))
@@ -8075,7 +8089,7 @@
   if (fvc.is_undefined () || fvc.is_empty ())
     return Matrix ();
   else
-    return convert_cdata (*this, fvc,cdatamapping_is ("scaled"), 2);
+    return convert_cdata (*this, fvc, cdatamapping_is ("scaled"), 2);
 }
 
 static bool updating_patch_data = false;
@@ -9500,6 +9514,7 @@
   plist_map["image"] = image::properties::factory_defaults ();
   plist_map["patch"] = patch::properties::factory_defaults ();
   plist_map["surface"] = surface::properties::factory_defaults ();
+  plist_map["light"] = light::properties::factory_defaults ();
   plist_map["hggroup"] = hggroup::properties::factory_defaults ();
   plist_map["uimenu"] = uimenu::properties::factory_defaults ();
   plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
@@ -10325,6 +10340,15 @@
   GO_BODY (patch);
 }
 
+DEFUN (__go_light__, args, ,
+       "-*- texinfo -*-\n\
+@deftypefn {} {} __go_light__ (@var{parent})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  GO_BODY (light);
+}
+
 DEFUN (__go_hggroup__, args, ,
        "-*- texinfo -*-\n\
 @deftypefn {} {} __go_hggroup__ (@var{parent})\n\
diff -r 57dea7b5ca46 -r 68d064da798d libinterp/corefcn/graphics.in.h
--- a/libinterp/corefcn/graphics.in.h	Fri Mar 18 09:33:43 2016 +0100
+++ b/libinterp/corefcn/graphics.in.h	Fri Mar 18 15:51:13 2016 +0100
@@ -4811,6 +4811,55 @@
 
 // ---------------------------------------------------------------------
 
+class OCTINTERP_API light : public base_graphics_object
+{
+public:
+  class OCTINTERP_API properties : public base_properties
+  {
+    // See the genprops.awk script for an explanation of the
+    // properties declarations.
+    // Programming note: Keep property list sorted if new ones are added.
+
+    BEGIN_PROPERTIES (light)
+      color_property color , color_values (1, 1, 1)
+      array_property position , default_light_position ()
+      radio_property style , "{infinite}|local"
+    END_PROPERTIES
+
+  protected:
+    void init (void)
+    {
+      position.add_constraint (dim_vector (1, 3));
+    }
+  };
+
+private:
+  properties xproperties;
+
+public:
+  light (const graphics_handle& mh, const graphics_handle& p)
+    : base_graphics_object (), xproperties (mh, p)
+  { }
+
+  ~light (void) { }
+
+  base_properties& get_properties (void) { return xproperties; }
+
+  const base_properties& get_properties (void) const { return xproperties; }
+
+  bool valid_object (void) const { return true; }
+
+  bool has_readonly_property (const caseless_str& pname) const
+  {
+    bool retval = xproperties.has_readonly_property (pname);
+    if (! retval)
+      retval = base_properties::has_readonly_property (pname);
+    return retval;
+  }
+};
+
+// ---------------------------------------------------------------------
+
 class OCTINTERP_API patch : public base_graphics_object
 {
 public:
diff -r 57dea7b5ca46 -r 68d064da798d scripts/help/__unimplemented__.m
--- a/scripts/help/__unimplemented__.m	Fri Mar 18 09:33:43 2016 +0100
+++ b/scripts/help/__unimplemented__.m	Fri Mar 18 15:51:13 2016 +0100
@@ -712,7 +712,6 @@
   "libisloaded",
   "libpointer",
   "libstruct",
-  "light",
   "lightangle",
   "lighting",
   "linkdata",
diff -r 57dea7b5ca46 -r 68d064da798d scripts/plot/draw/light.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/draw/light.m	Fri Mar 18 15:51:13 2016 +0100
@@ -0,0 +1,145 @@
+## Copyright (C) 2016 Markus Muetzel
+##
+## 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/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {} light ()
+## @deftypefnx {Function File} {} light (@dots{}, @var{prop}, @var{val}, @dots{})
+## @deftypefnx {Function File} {} light (@var{hax}, @dots{})
+## @deftypefnx {Function File} {@var{h} =} light (@dots{})
+## Create light object in the current axes or for axes @var{hax}.
+##
+## When a light object is present in an axes object and the properties
+## @qcode{"EdgeLighting"} or @qcode{"FaceLighting"} of a @command{patch} or
+## @command{surface} object are set to a value other than @qcode{"none"}, these
+## objects are drawn with light and shadow effects. Supported values for these
+## properties are "none" (no lighting effects), "flat" (facetted look of the
+## objects), "gouraud" (linear interpolation of the lighting effects between the
+## vertices) and "phong" (not implemented, "gouraud" lighting is used instead).
+## For @command{patch} objects, the normals must be set manually (property
+## @qcode{"VertexNormals"}).
+##
+## Only one light object per axes is supported.
+##
+## Lighting is only supported for graphics toolkits supporting OpenGL (i.e.
+## "fltk" and "qt").
+##
+## The following properties specific to the light object can be passed with
+## their respective values:
+##
+## @table @asis
+## @item @qcode{"Color":} The color of the light object can be passed as an 
+## RGB-vector (e.g. @qcode{[1 0 0]} for red) or as a string (e.g. 'r' for red).
+## The default color is white (@qcode{[1 1 1]}).
+##
+## @item @qcode{"Position":} The direction from which the light emanates as an
+## 1x3-vector. The default direction is @qcode{[1 0 1]}.
+##
+## @item @qcode{"Style":} This string defines whether the light emanates from a
+## light source at infinite distance ("infinite") or from a local point source
+## ("local"). Only the default value "infinite" is supported.
+## @end table
+##
+## If @command{light} is called with an axes handle @var{hax}, it must be passed
+## as the first argument.
+##
+## Optionally, the handle to the light object is returned in @var{h}.
+##
+## @seealso{get, set, patch, surface}
+## @end deftypefn
+
+## Author: mmuetzel
+
+function h = light (varargin)
+
+  [hax, varargin] = __plt_get_axis_arg__ ("light", varargin{:});
+
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
+  endif
+
+  htmp = __go_light__ (hax, varargin{:});
+
+  if (nargout > 0)
+    h = htmp;
+  endif
+
+endfunction
+
+
+%!demo
+%! %% Demonstrate effects of lighting
+%! clf;
+%! %% patches
+%! h_axes1 = subplot (2, 2, 1);
+%! [x,y,z] = meshgrid (-2:0.2:2, -2:0.2:2, -2:0.2:2);
+%! val = x.^2 + y.^2 + z.^2;
+%! fv1 = isosurface (x, y, z, val, 1);
+%! h_patch1 = patch (fv1, "FaceColor", "c", "EdgeColor", "none", "FaceLighting", "Gouraud");
+%! isonormals (x, y, z, val, h_patch1)
+%! fv2 = isosurface (x, y+3, z, val, 1);
+%! h_patch2 = patch (fv2, "FaceColor", "r", "EdgeColor", "none", "FaceLighting", "Gouraud");
+%! isonormals (x, y+3, z, val, h_patch2)
+%! axis equal; axis tight
+%! title ("Patch with lighting");
+%! view (3)
+%! h_light1 = light ();
+%!
+%! h_axes2 = subplot(2, 2, 2);
+%! patch (fv1, "FaceColor", "c", "EdgeColor", "none");
+%! patch (fv2, "FaceColor", "r", "EdgeColor", "none");
+%! axis equal; axis tight
+%! title ("Patch without lighting");
+%! view (3)
+%!
+%! %% surfaces
+%! h_axes3 = subplot(2, 2, 3);
+%! h_surf1 = surf (h_axes3, peaks, "LineStyle", "none", "FaceLighting", "Gouraud");
+%! title ("Surface with lighting");
+%! view (3)
+%! h_light2 = light ();
+%!
+%! h_axes3 = subplot(2, 2, 4);
+%! h_surf2 = surf (h_axes3, peaks, "LineStyle", "none");
+%! title ("Surface without lighting");
+%! view (3)
+
+%!test
+%! hf = figure ("Visible", "off");
+%! unwind_protect
+%!   h = light ();
+%!   assert (findobj (hf, "Type", "light"), h);
+%!   assert (get (h, "Position"), [1, 0, 1]);
+%!   assert (get (h, "Color"), [1, 1, 1]);
+%!   assert (get (h, "Style"), "infinite");
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
+%!test
+%! hf = figure ("Visible", "off");
+%! ha = gca;
+%! unwind_protect
+%!   h = light (ha, "Position", [1 2 3], "Color", "r");
+%!   assert (get (h, "Position"), [1 2 3]);
+%!   assert (get (h, "Color"), [1 0 0]);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
diff -r 57dea7b5ca46 -r 68d064da798d scripts/plot/util/private/__gnuplot_draw_axes__.m
--- a/scripts/plot/util/private/__gnuplot_draw_axes__.m	Fri Mar 18 09:33:43 2016 +0100
+++ b/scripts/plot/util/private/__gnuplot_draw_axes__.m	Fri Mar 18 15:51:13 2016 +0100
@@ -1359,6 +1359,9 @@
           kids = [kids; obj.children];
         endif
 
+      case "light"
+        ## ignore it
+
       otherwise
         error ("__gnuplot_draw_axes__: unknown object class, %s", obj.type);
     endswitch
