diff src/graphics.cc @ 11175:c0a95a5c6d25

Address the speed of plotting large hggroup groups and in particular contours (bug #31305). Changes to address this include - Use __go_patch__ in __contour__ rather than patch so that the cost of setting up the callback functions is avoided. The contourgroup callback handles the updating of properties. - Add children_property class to store children in a list so that adding and deleting children is a low cost operation. - Create a new version of update_axis_limits code that doesn't force the recalculation of all of the objects children. Patch also allows unclosed patch contours with the FLTK backend.
author David Bateman <dbateman@free.fr>
date Tue, 02 Nov 2010 00:47:31 +0100
parents 36442102c340
children 5fa7667f90e5
line wrap: on
line diff
--- a/src/graphics.cc	Mon Nov 01 06:54:43 2010 -0400
+++ b/src/graphics.cc	Tue Nov 02 00:47:31 2010 +0100
@@ -688,7 +688,8 @@
     {
       double e = double (data[i]);
 
-      if (! (xisinf (e) || xisnan (e)))
+      // Don't need to test for NaN here as NaN>x and NaN<x is always false 
+      if (! xisinf (e))
         {
           if (e < emin)
             emin = e;
@@ -1206,6 +1207,62 @@
   return false;
 }
 
+Matrix 
+children_property::do_get_children (bool return_hidden) const
+{
+  Matrix retval (children_list.size (), 1);
+  octave_idx_type k = 0;
+
+  graphics_object go = gh_manager::get_object (0);
+          
+  root_figure::properties& props =
+    dynamic_cast<root_figure::properties&> (go.get_properties ());
+
+  if (! props.is_showhiddenhandles ())
+    {
+      for (const_children_list_iterator p = children_list.begin ();
+           p != children_list.end (); p++)
+        {
+          graphics_handle kid = *p;
+
+          if (gh_manager::is_handle_visible (kid))
+            {
+              if (! return_hidden)
+                retval(k++) = *p;
+            }
+          else if (return_hidden)
+            retval(k++) = *p;
+        }
+
+      retval.resize (k, 1);
+    }
+  else
+    {
+      for (const_children_list_iterator p = children_list.begin ();
+           p != children_list.end (); p++)
+        retval(k++) = *p;
+    }
+      
+  return retval;
+}
+
+void 
+children_property::do_delete_children (bool clear)
+{
+  for (children_list_iterator p = children_list.begin ();
+       p != children_list.end (); p++)
+    {
+      graphics_object go = gh_manager::get_object (*p);
+
+      if (go.valid_object ())
+        gh_manager::free (*p);
+
+    }
+
+  if (clear)
+    children_list.clear ();
+}
+
 bool
 callback_property::validate (const octave_value& v) const
 {
@@ -1883,7 +1940,6 @@
     }
 }
 
-
 static octave_value
 xget (const graphics_handle& h, const caseless_str& name)
 {
@@ -1952,7 +2008,6 @@
 adopt (const graphics_handle& p, const graphics_handle& h)
 {
   graphics_object parent_obj = gh_manager::get_object (p);
-
   parent_obj.adopt (h);
 }
 
@@ -2147,34 +2202,6 @@
 }
 
 void
-base_properties::remove_child (const graphics_handle& h)
-{
-  octave_idx_type k = -1;
-  octave_idx_type n = children.numel ();
-  for (octave_idx_type i = 0; i < n; i++)
-    {
-      if (h.value () == children(i))
-        {
-          k = i;
-          break;
-        }
-    }
-
-  if (k >= 0)
-    {
-      Matrix new_kids (n-1, 1);
-      octave_idx_type j = 0;
-      for (octave_idx_type i = 0; i < n; i++)
-        {
-          if (i != k)
-            new_kids(j++) = children(i);
-        }
-      children = new_kids;
-      mark_modified ();
-    }
-}
-
-void
 base_properties::set_parent (const octave_value& val)
 {
   double tmp = val.double_value ();
@@ -2203,45 +2230,6 @@
 }
 
 void
-base_properties::set_children (const octave_value& val)
-{
-  const Matrix new_kids = val.matrix_value ();
-
-  octave_idx_type nel = new_kids.numel ();
-
-  const Matrix new_kids_column = new_kids.reshape (dim_vector (nel, 1));
-
-  bool ok = true;
-
-  if (! error_state)
-    {
-      const Matrix visible_kids = get_children ();
-
-      if (visible_kids.numel () == new_kids.numel ())
-        {
-          Matrix t1 = visible_kids.sort ();
-          Matrix t2 = new_kids_column.sort ();
-
-          if (t1 != t2)
-            ok = false;
-        }
-      else
-        ok = false;
-
-      if (! ok)
-        error ("set: new children must be a permutation of existing children");
-    }
-  else
-    {
-      ok = false;
-      error ("set: expecting children to be array of graphics handles");
-    }
-
-  if (ok)
-    children = new_kids_column.stack (get_hidden_children ());
-}
-
-void
 base_properties::mark_modified (void)
 {
   __modified__ = "on";
@@ -2269,19 +2257,13 @@
 }
 
 void
-base_properties::delete_children (void)
-{
-  octave_idx_type n = children.numel ();
-
-  // A callback function might have already deleted the child,
-  // so check before deleting
-  for (octave_idx_type i = 0; i < n; i++)
-    {
-      graphics_object go = gh_manager::get_object (children(i));
-
-      if (go.valid_object ())
-        gh_manager::free (children(i));
-    }
+base_properties::update_axis_limits (const std::string& axis_type,
+                                     const graphics_handle& h) const
+{
+  graphics_object obj = gh_manager::get_object (__myhandle__);
+
+  if (obj)
+    obj.update_axis_limits (axis_type, h);
 }
 
 graphics_backend
@@ -2470,6 +2452,21 @@
 }
 
 void
+base_graphics_object::update_axis_limits (const std::string& axis_type,
+                                          const graphics_handle& h)
+{
+  if (valid_object ())
+    {
+      graphics_object parent_obj = gh_manager::get_object (get_parent ());
+
+      if (parent_obj)
+        parent_obj.update_axis_limits (axis_type, h);
+    }
+  else
+    error ("base_graphics_object::update_axis_limits: invalid graphics object");
+}
+
+void
 base_graphics_object::remove_all_listeners (void)
 {
   octave_map m = get (true).map_value ();
@@ -2728,9 +2725,11 @@
     {
       graphics_handle new_currentaxes;
 
-      for (octave_idx_type i = 0; i < children.numel (); i++)
-        {
-          graphics_handle kid = children(i);
+      Matrix kids = get_children ();
+
+      for (octave_idx_type i = 0; i < kids.numel (); i++)
+        {
+          graphics_handle kid = kids(i);
 
           graphics_object go = gh_manager::get_object (kid);
 
@@ -3418,9 +3417,7 @@
       activepositionproperty = "outerposition";
     }
 
-  delete_children ();
-
-  children = Matrix ();
+  delete_children (true);
 
   xlabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
   ylabel = gh_manager::make_graphics_handle ("text", __myhandle__, false);
@@ -3504,54 +3501,6 @@
     base_properties::remove_child (h);
 }
 
-Matrix
-base_properties::get_children_internal (bool return_hidden) const
-{
-  Matrix retval = children;
-  
-  graphics_object go = gh_manager::get_object (0);
-
-  root_figure::properties& props =
-      dynamic_cast<root_figure::properties&> (go.get_properties ());
-
-  if (! props.is_showhiddenhandles ())
-    {
-      octave_idx_type k = 0;
-
-      for (octave_idx_type i = 0; i < children.numel (); i++)
-        {
-          graphics_handle kid = children (i);
-
-          if (gh_manager::is_handle_visible (kid))
-            {
-              if (! return_hidden)
-                retval(k++) = children(i);
-            }
-          else
-            {
-              if (return_hidden)
-                retval(k++) = children(i);
-            }
-        }
-
-      retval.resize (k, 1);
-    }
-
-  return retval;
-}
-
-Matrix
-base_properties::get_children (void) const
-{
-  return get_children_internal (false);
-}
-
-Matrix
-base_properties::get_hidden_children (void) const
-{
-  return get_children_internal (true);
-}
-
 inline Matrix
 xform_matrix (void)
 {
@@ -4555,6 +4504,198 @@
 static bool updating_axis_limits = false;
 
 void
+axes::update_axis_limits (const std::string& axis_type,
+                          const graphics_handle& h)
+{
+  if (updating_axis_limits)
+    return;
+
+  Matrix kids = Matrix (1, 1, h.value ());
+ 
+  double min_val = octave_Inf;
+  double max_val = -octave_Inf;
+  double min_pos = octave_Inf;
+
+  char update_type = 0;
+
+  Matrix limits;
+  double val;
+
+#define FIX_LIMITS \
+  if (limits.numel() == 3) \
+    { \
+      val = limits(0); \
+      if (! (xisinf (val) || xisnan (val))) \
+        min_val = val; \
+      val = limits(1); \
+      if (! (xisinf (val) || xisnan (val))) \
+        max_val = val; \
+      val = limits(2); \
+      if (! (xisinf (val) || xisnan (val))) \
+        min_pos = val; \
+    } \
+  else \
+    { \
+      limits.resize(3, 1); \
+      limits(0) = min_val; \
+      limits(1) = max_val; \
+      limits(2) = min_pos; \
+    }
+
+  if (axis_type == "xdata" || axis_type == "xscale"
+      || axis_type == "xlimmode" || axis_type == "xliminclude"
+      || axis_type == "xlim")
+    {
+      if (xproperties.xlimmode_is ("auto"))
+        {
+          limits = xproperties.get_xlim ().matrix_value ();
+          FIX_LIMITS ;
+
+          get_children_limits (min_val, max_val, min_pos, kids, 'x');
+          
+          limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
+                                                xproperties.xscale_is ("log"));
+
+          update_type = 'x';
+        }
+    }
+  else if (axis_type == "ydata" || axis_type == "yscale"
+           || axis_type == "ylimmode" || axis_type == "yliminclude"
+           || axis_type == "ylim")
+    {
+      if (xproperties.ylimmode_is ("auto"))
+        {
+          limits = xproperties.get_ylim ().matrix_value ();
+          FIX_LIMITS ;
+
+          get_children_limits (min_val, max_val, min_pos, kids, 'y');
+
+          limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
+                                                xproperties.yscale_is ("log"));
+
+          update_type = 'y';
+        }
+    }
+  else if (axis_type == "zdata" || axis_type == "zscale"
+           || axis_type == "zlimmode" || axis_type == "zliminclude"
+           || axis_type == "zlim")
+    {
+      if (xproperties.zlimmode_is ("auto"))
+        {
+          limits = xproperties.get_zlim ().matrix_value ();
+          FIX_LIMITS ;
+
+          get_children_limits (min_val, max_val, min_pos, kids, 'z');
+
+          limits = xproperties.get_axis_limits (min_val, max_val, min_pos,
+                                                xproperties.zscale_is ("log"));
+
+          update_type = 'z';
+        }
+    }
+  else if (axis_type == "cdata" || axis_type == "climmode"
+           || axis_type == "cdatamapping" || axis_type == "climinclude"
+           || axis_type == "clim")
+    {
+      if (xproperties.climmode_is ("auto"))
+        {
+          limits = xproperties.get_clim ().matrix_value ();
+          FIX_LIMITS ;
+
+          get_children_limits (min_val, max_val, min_pos, kids, 'c');
+
+          if (min_val > max_val)
+            {
+              min_val = min_pos = 0;
+              max_val = 1;
+            }
+          else if (min_val == max_val)
+            max_val = min_val + 1;
+
+          limits.resize (1, 2);
+
+          limits(0) = min_val;
+          limits(1) = max_val;
+
+          update_type = 'c';
+        }
+
+    }
+  else if (axis_type == "alphadata" || axis_type == "alimmode"
+           || axis_type == "alphadatamapping" || axis_type == "aliminclude"
+           || axis_type == "alim")
+    {
+      if (xproperties.alimmode_is ("auto"))
+        {
+          limits = xproperties.get_alim ().matrix_value ();
+          FIX_LIMITS ;
+
+          get_children_limits (min_val, max_val, min_pos, kids, 'a');
+
+          if (min_val > max_val)
+            {
+              min_val = min_pos = 0;
+              max_val = 1;
+            }
+          else if (min_val == max_val)
+            max_val = min_val + 1;
+
+          limits.resize (1, 2);
+
+          limits(0) = min_val;
+          limits(1) = max_val;
+
+          update_type = 'a';
+        }
+
+    }
+
+#undef FIX_LIMITS
+
+  unwind_protect frame;
+  frame.protect_var (updating_axis_limits);
+
+  updating_axis_limits = true;
+
+  switch (update_type)
+    {
+    case 'x':
+      xproperties.set_xlim (limits);
+      xproperties.set_xlimmode ("auto");
+      xproperties.update_xlim ();
+      break;
+
+    case 'y':
+      xproperties.set_ylim (limits);
+      xproperties.set_ylimmode ("auto");
+      xproperties.update_ylim ();
+      break;
+
+    case 'z':
+      xproperties.set_zlim (limits);
+      xproperties.set_zlimmode ("auto");
+      xproperties.update_zlim ();
+      break;
+
+    case 'c':
+      xproperties.set_clim (limits);
+      xproperties.set_climmode ("auto");
+      break;
+
+    case 'a':
+      xproperties.set_alim (limits);
+      xproperties.set_alimmode ("auto");
+      break;
+
+    default:
+      break;
+    }
+
+  xproperties.update_transform ();
+
+}
+
+void
 axes::update_axis_limits (const std::string& axis_type)
 {
   if (updating_axis_limits || updating_aspectratios)
@@ -5069,9 +5210,151 @@
 
 // ---------------------------------------------------------------------
 
+void 
+hggroup::properties::update_limits (void) const
+{
+  graphics_object obj = gh_manager::get_object (__myhandle__);
+
+  if (obj)
+    {
+      obj.update_axis_limits ("xlim");
+      obj.update_axis_limits ("ylim");
+      obj.update_axis_limits ("zlim");
+      obj.update_axis_limits ("clim");
+      obj.update_axis_limits ("alim");
+    }
+}
+
+void 
+hggroup::properties::update_limits (const graphics_handle& h) const
+{
+  graphics_object obj = gh_manager::get_object (__myhandle__);
+
+  if (obj)
+    {
+      obj.update_axis_limits ("xlim", h);
+      obj.update_axis_limits ("ylim", h);
+      obj.update_axis_limits ("zlim", h);
+      obj.update_axis_limits ("clim", h);
+      obj.update_axis_limits ("alim", h);
+    }
+}
+
+static bool updating_hggroup_limits = false;
+
+void
+hggroup::update_axis_limits (const std::string& axis_type,
+                             const graphics_handle& h)
+{
+  if (updating_hggroup_limits)
+    return;
+
+  Matrix kids = Matrix (1, 1, h.value ());
+ 
+  double min_val = octave_Inf;
+  double max_val = -octave_Inf;
+  double min_pos = octave_Inf;
+
+  Matrix limits;
+  double val;
+
+  char update_type = 0;
+
+  if (axis_type == "xlim" || axis_type == "xliminclude")
+    {
+      limits = xproperties.get_xlim ().matrix_value ();
+      update_type = 'x';
+    }
+  else if (axis_type == "ylim" || axis_type == "yliminclude")
+    {
+      limits = xproperties.get_ylim ().matrix_value ();
+      update_type = 'y';
+    }
+  else if (axis_type == "zlim" || axis_type == "zliminclude")
+    {
+      limits = xproperties.get_zlim ().matrix_value ();
+      update_type = 'z';
+    }
+  else if (axis_type == "clim" || axis_type == "climinclude")
+    {
+      limits = xproperties.get_clim ().matrix_value ();
+      update_type = 'c';
+    }
+  else if (axis_type == "alim" || axis_type == "aliminclude")
+    {
+      limits = xproperties.get_alim ().matrix_value ();
+      update_type = 'a';
+    }
+
+  if (limits.numel() == 3)
+    {
+      val = limits(0);
+      if (! (xisinf (val) || xisnan (val)))
+        min_val = val;
+      val = limits(1);
+      if (! (xisinf (val) || xisnan (val)))
+        max_val = val;
+      val = limits(2);
+      if (! (xisinf (val) || xisnan (val)))
+        min_pos = val;
+    }
+  else
+    {
+      limits.resize(3,1);
+      limits(0) = min_val;
+      limits(1) = max_val;
+      limits(2) = min_pos;
+    }
+
+  get_children_limits (min_val, max_val, min_pos, kids, update_type);
+
+  unwind_protect frame;
+  frame.protect_var (updating_hggroup_limits);
+
+  updating_hggroup_limits = true;
+
+  if (limits(0) != min_val || limits(1) != max_val || limits(2) != min_pos)
+    {
+      limits(0) = min_val;
+      limits(1) = max_val;
+      limits(2) = min_pos;
+
+      switch (update_type)
+        {
+        case 'x':
+          xproperties.set_xlim (limits);
+          break;
+
+        case 'y':
+          xproperties.set_ylim (limits);
+          break;
+
+        case 'z':
+          xproperties.set_zlim (limits);
+          break;
+
+        case 'c':
+          xproperties.set_clim (limits);
+          break;
+
+        case 'a':
+          xproperties.set_alim (limits);
+          break;
+
+        default:
+          break;
+        }
+
+      base_graphics_object::update_axis_limits (axis_type, h);
+    }
+}
+
 void
 hggroup::update_axis_limits (const std::string& axis_type)
 {
+  if (updating_hggroup_limits)
+    return;
+
   Matrix kids = xproperties.get_children ();
 
   double min_val = octave_Inf;
@@ -5083,7 +5366,7 @@
   if (axis_type == "xlim" || axis_type == "xliminclude")
     {
       get_children_limits (min_val, max_val, min_pos, kids, 'x');
-      
+          
       update_type = 'x';
     }
   else if (axis_type == "ylim" || axis_type == "yliminclude")
@@ -5103,7 +5386,6 @@
       get_children_limits (min_val, max_val, min_pos, kids, 'c');
 
       update_type = 'c';
-
     }
   else if (axis_type == "alim" || axis_type == "aliminclude")
     {
@@ -5112,6 +5394,11 @@
       update_type = 'a';
     }
 
+  unwind_protect frame;
+  frame.protect_var (updating_hggroup_limits);
+
+  updating_hggroup_limits = true;
+
   Matrix limits (1, 3, 0.0);
 
   limits(0) = min_val;