changeset 18780:56bff71de2ca

polar.m: Link polar grid with axes properties (bug #39495). * polar.m: Document 'rtick', 'ttick' properties for polar plots. Add listeners for axes properties: color, fontangle, fontname, fontunits, fontweight, interpreter, layer, gridlinestyle. Do input validation on rtick, ttick. Change handlevisibility of hggroup to 'off'.
author Rik <rik@octave.org>
date Sat, 10 May 2014 17:36:31 -0700
parents b5b73959907f
children 9eb72fa5f8b5
files scripts/plot/draw/polar.m
diffstat 1 files changed, 128 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/draw/polar.m	Mon Apr 28 19:58:36 2014 +0200
+++ b/scripts/plot/draw/polar.m	Sat May 10 17:36:31 2014 -0700
@@ -36,6 +36,14 @@
 ##
 ## The optional return value @var{h} is a graphics handle to the created plot.
 ##
+## Implementation Note: The polar axis is drawn using line and text objects
+## encapsulated in an hggroup.  The hggroup properties are linked to the
+## original axes object such that altering an appearance property, for example
+## @code{fontname}, will update the polar axis.  Two new properties are
+## added to the original axes--@code{rtick}, @code{ttick}--which replace
+## @code{xtick}, @code{ytick}.  The first is a list of tick locations in the
+## radial (rho) direction; The second is a list of tick locations in the
+## angular (theta) direction specified in degrees, i.e., in the range 0--359.
 ## @seealso{rose, compass, plot}
 ## @end deftypefn
 
@@ -87,35 +95,46 @@
       print_usage ();
     endif
 
-    set (hax, "xaxislocation", "zero", "yaxislocation", "zero",
-              "plotboxaspectratio", [1, 1, 1]);
+    ## FIXME: Should more gracefully handle "hold on" and not override props.
+    set (hax, "visible", "off", "plotboxaspectratio", [1, 1, 1],
+              "zlim", [-1 1]);
 
-    ## Hide standard axes
-    set(hax, "visible", "off");
-
-    if (!isprop (hax, "rtick"))
+    if (! isprop (hax, "rtick"))
       addproperty ("rtick", hax, "data");
     endif
 
     ## calculate r(ho)tick from xtick
     xtick = get (hax, "xtick");
     rtick = xtick(find (xtick > 0, 1):find (xtick >= maxr, 1));
+    if (isempty (rtick))
+      rtick = [0.5 1];
+    endif
     set (hax, "rtick", rtick);
 
-    addlistener (hax, "rtick", @__update_polar_grid__);
-    addlistener (hax, "fontsize", @__update_text__);
-    addlistener (hax, "linewidth", @__update_lines__);
-
     ## add t(heta)tick
-    if (!isprop (hax, "ttick"))
+    if (! isprop (hax, "ttick"))
       addproperty ("ttick", hax, "data");
     endif
 
-    ## theta(angular) ticks in deg
+    ## theta(angular) ticks in degrees
     set (hax, "ttick", 0:30:330);
-    __update_polar_grid__(hax, []);
+
+    ## Create hggroup to hold text/line objects and attach listeners
+    hg = hggroup (hax, "tag", "polar_grid", "handlevisibility", "off");
+    __update_polar_grid__(hax, [], hg);
 
-    addlistener (hax, "ttick", @__update_polar_grid__);
+    addlistener (hax, "rtick", {@__update_polar_grid__, hg});
+    addlistener (hax, "ttick", {@__update_polar_grid__, hg});
+    addlistener (hax, "color", {@__update_patch__, hg});
+    addlistener (hax, "fontangle", {@__update_text__, hg, "fontangle"});
+    addlistener (hax, "fontname", {@__update_text__, hg, "fontname"});
+    addlistener (hax, "fontsize", {@__update_text__, hg, "fontsize"});
+    addlistener (hax, "fontunits", {@__update_text__, hg, "fontunits"});
+    addlistener (hax, "fontweight", {@__update_text__, hg, "fontweight"});
+    addlistener (hax, "interpreter", {@__update_text__, hg, "interpreter"});
+    addlistener (hax, "layer", {@__update_layer__, hg});
+    addlistener (hax, "gridlinestyle", {@__update_lines__, hg,"gridlinestyle"});
+    addlistener (hax, "linewidth", {@__update_lines__, hg, "linewidth"});
 
   unwind_protect_cleanup
     if (! isempty (oldfig))
@@ -219,68 +238,117 @@
 
 endfunction
 
-function __update_text__ (hax, dummy)
+## Callback functions for listeners
+
+function __update_text__ (hax, ~, hg, prop)
+
+  kids = get (hg, "children");
+  idx = strcmp (get (kids, "type"), "text");
+  set (kids(idx).', prop, get (hax, prop));
+
+endfunction
+
+function __update_lines__ (hax,  ~, hg, prop)
 
-  text_handles = findobj(findobj (hax, "tag", "polar_grid"), "type", "text");
-  set (text_handles, "fontsize", get (hax, "fontsize"));
+  kids = get (hg, "children");
+  idx = strcmp (get (kids, "type"), "line");
+  lprop = prop;
+  if (strcmp (prop, "gridlinestyle"))
+    lprop = "linestyle";
+  endif
+  set (kids(idx).', lprop, get (hax, prop));
+
+endfunction
+
+function __update_patch__ (hax, ~, hg)
+
+  kids = get (hg, "children");
+  idx = strcmp (get (kids, "type"), "patch");
+  set (kids(idx).', "facecolor", get (hax, "color"));
 
 endfunction
 
-function __update_lines__ (hax, dummy)
+function __update_layer__ (hax,  ~, hg)
 
-  line_handles = findobj(findobj (hax, "tag", "polar_grid"), "type", "line");
-  set (line_handles, "linewidth", get (hax, "linewidth"));
+  set (hg, "handlevisibility", "on");
+  kids = get (hax, "children");
+  if (strcmp (get (hax, "layer"), "bottom"))
+    set (hax, "children", [kids(kids != hg); hg]); 
+  else
+    set (hax, "children", [hg; kids(kids != hg)]); 
+  endif
+  set (hg, "handlevisibility", "off");
 
 endfunction
 
-function __update_polar_grid__ (hax, dummy)
+function __update_polar_grid__ (hax, ~, hg)
+
+  ## Delete existing polar grid
+  delete (get (hg, "children"));
+
+  rtick = unique (get (hax, "rtick")(:)');
+  rtick = rtick(rtick > 0);
+  if (isempty (rtick))
+    rtick = [0.5 1];
+  endif
 
-  ## Delete polar_grid hggroup if already present
-  delete (findobj (hax, "tag", "polar_grid"));
-  h = hggroup (hax, "tag", "polar_grid");
+  ttick = unique (get (hax, "ttick")(:)');
+  ttick = ttick(ttick >= 0);
+  if (isempty (ttick))
+    ttick = 0:30:330;
+  endif
 
-  rtick = get (hax, "rtick");
-  ttick = get (hax, "ttick");
-  lw = get (hax, "linewidth");
-  fs = get (hax, "fontsize");
+  lprops = {"linestyle", get(hax, "gridlinestyle"), ...
+            "linewidth", get(hax, "linewidth")};
+  ## "fontunits" should be first because it affects "fontsize" property.
+  tprops(1:2:12) = {"fontunits", "fontangle", "fontname", "fontsize", ...
+                    "fontweight", "interpreter"};
+  tprops(2:2:12) = get (hax, tprops(1:2:12));
 
   ## The number of points used for a circle
   circle_points = 50;
-  t = linspace(0, 2*pi, circle_points);
-  s = sin(t);
-  c = cos(t);
+  t = linspace (0, 2*pi, circle_points)';
+  x = kron (cos (t), rtick);
+  y = kron (sin (t), rtick);
+
+  ## Draw colored disk under axes at Z-depth = -1
+  patch (x(:,end), y(:,end), -ones (circle_points, 1),
+         get (hax, "color"), "parent", hg);
 
-  ## plot dotted circles
-  y = kron(s', rtick);
-  x = kron(c', rtick);
-  line (x(:,1:end-1), y(:,1:end-1), "linestyle", ":", "parent", h, "linewidth", lw);
+  ## Plot dotted circles
+  line (x(:,1:end-1), y(:,1:end-1), lprops{:}, "parent", hg);
 
-  ## the outer circle is drawn solid
-  line (x(:,end), y(:,end), "linestyle", "-", "parent", h, "linewidth", lw);
+  ## Outer circle is drawn solid
+  line (x(:,end), y(:,end), lprops{:}, "linestyle", "-", "parent", hg);
 
-  ## add radial labels
+  ## Add radial labels
   [x, y] = pol2cart (0.42 * pi, rtick);
-  text (x, y, num2cell(rtick), "verticalalignment", "bottom", "parent", h, "fontsize", fs);
+  text (x, y, num2cell (rtick), "verticalalignment", "bottom", tprops{:},
+        "parent", hg);
 
   ## add radial lines
-  rtick = get (hax, "rtick");
-
   s = rtick(end) * sin (ttick * pi / 180);
   c = rtick(end) * cos (ttick * pi / 180);
-  x = vertcat(zeros(1,numel(ttick)), c);
-  y = vertcat(zeros(1,numel(ttick)), s);
-  line (x, y, "linestyle", ":", "parent", h, "linewidth", lw)
+  x = [zeros(1, numel (ttick)); c];
+  y = [zeros(1, numel (ttick)); s];
+  line (x, y, "linestyle", ":", lprops{:}, "parent", hg);
 
   ## add angular labels
   tticklabel = num2cell (ttick);
+  ## FIXME: This tm factor does not work as fontsize increases
   tm = 1.08;
-  text (tm * c, tm * s, tticklabel, "horizontalalignment", "center", "parent", h, "fontsize", fs)
+  text (tm * c, tm * s, tticklabel, "horizontalalignment", "center",
+        tprops{:}, "parent", hg);
 
   lim = 1.1 * rtick(end);
   set (hax, "xlim", [-lim, lim], "ylim", [-lim, lim]);
 
+  ## Put polar grid behind or ahead of plot
+  __update_layer__ (hax, [], hg);
+
 endfunction
 
+
 %!demo
 %! clf;
 %! theta = linspace (0,2*pi,1000);
@@ -297,24 +365,25 @@
 
 %!demo
 %! clf;
-%! theta = linspace (0,8*pi,1000);
-%! rho = sin (5/4*theta);
-%! polar (theta, rho);
-%! set (gca, "rtick", 0.2:0.2:1)
-%! title ('polar() plot');
+%! theta = linspace (0,2*pi,1000);
+%! rho = sin (2*theta).*cos (2*theta);
+%! polar (theta, rho, '--r');
+%! set (gca, "rtick", 0.1:0.1:0.6, "ttick", 0:20:340);
+%! title ('polar() plot with finer grid');
 
 %!demo
 %! clf;
 %! theta = linspace (0,2*pi,1000);
-%! rho = sin (2*theta).*cos(2*theta);
-%! polar (theta,rho,'--b')
-%! set (gca, "fontsize", 12, "linewidth", 2);
-%! title ('polar() plot with bigger font and thicker line');
+%! rho = sin (2*theta).*cos (2*theta);
+%! polar (theta, rho, '--b');
+%! set (gca, "fontsize", 12, "linewidth", 2, "color", [0.8 0.8 0.8]);
+%! title ('polar() plot with modified axis appearance');
 
 %!demo
 %! clf;
-%! theta = linspace (0,2*pi,1000);
-%! rho = sin (2*theta).*cos(2*theta);
-%! polar (theta,rho,'--r')
-%! set (gca, "rtick", 0.1:0.1:0.6, "ttick", 0:20:340)
-%! title ('polar() plot with finer grid');
+%! theta = linspace (0,8*pi,1000);
+%! rho = sin (5/4*theta);
+%! polar (theta, rho);
+%! set (gca, "rtick", 0.2:0.2:1);
+%! title ('polar() plot');
+