octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Matlab compatible legends for FLTK/Gnuplot backends?


From: David Bateman
Subject: Re: Matlab compatible legends for FLTK/Gnuplot backends?
Date: Mon, 06 Sep 2010 00:41:02 +0200
User-agent: Mozilla-Thunderbird 2.0.0.22 (X11/20090706)

David Bateman wrote:
As it seems to me that the blocking feature for 3.4.0 is the support of legends in FLTK, I've been looking at writing a matlab compatiable, or at least more compatible than currently, legend function. I however want this function to not be specific to a backend and I want it to work with both FLTK and the gnuplot backend and be implemented entirely as a frontend function. I'm however blocked in that goal when calculating the extents of the text strings.

How I'm trying to implement this function is to create an axis object, with the "tag" property set to "legend" and the userdata with a structure with a field pointing to the axis the legend is attached to. The position, limits, etc of the legend axis are initially arbitrary and into this axis I write all of the text strings. The object of doing this is to calculate the extent of each of the text strings so that I can then select an optimal positioning of the keys in the legend, and only after that resize the axis, position it and write the symbols. The get_extents method of the graphics.cc(text) class fills in the "extents" property of the text objects. However, it relies on having both FreeType and font_config installed. I'd rather not add new dependency for the gnuplot backend and in any case why should I assume that FreeType will give extents that are compatible with gnuplot. So from here I see I have one of four choices

1) Accept the FreeType and font_config dependencies, making these essential for any plotting with legends in Octave. However, this might not be ideal for the gnuplot backend,

2) Try and find another means of implementing the get_extents method of the the graphics.cc(text) class that will be used if FreeType isn't installed (or for backends that don't use FreeType), but this is likely to be approximative

3) Give up on trying to have a legend function that is implemented entirely in the frontend and continue to use gnuplot itself for the legend insertion for the gnuplot backend. I'd probably implement the missing horizontal legend orientation in gnuplot and make the gnuplot legend more compatible with matlab in this case.

4) Get someone on this list to tell me I'm a bloody idiot and there is another means of getting the text extents that will be correct (at least approximately) for all backends.

I'm kind of hoping for 4), but failing that help in choosing which of the other three options would be appreciated

D.



Well here is a first implementation that use method 1) above. I'm sure there are some things missing, for example the method of using the "annotation" field of the graphics objects to decide if they are included in the legend ass described in

http://www.mathworks.com/help/techdoc/creating_plots/braliom.html

and its seems the legend and the extra fields of the legend axis

'EdgeColor'
'Interpreter'
'Location'
'Orientation'
'String'
'TextColor'

as described in

https://savannah.gnu.org/bugs/?30461

The patch does however implement legends with different parents and horizontal legends. So it fixes the bugs
29348, 30461 and in passing partially fixes the bug  30519.

It seems to work pretty well for the FLTK backend, and with a few hacks, it works ok for me with the WXT terminal of gnuplot. It hasn't received a lot of testing with gnuplot however, and I'm sending it now as I'm not sure I'll have much time to work on this in the next couple of days.

So the questions I have are

a) If we keep the approach in this patch as described in method 1) above is the need for FreeType and font_config to be installed an acceptable limitation? These would become required rather than just optional

b) Is the approximate behavior of the text extents returned by FreeType for the gnuplot terminal good enough? It needs more testing.

c) Why do I need to set the plot axis "activepositionproperty" to "position" for the gnuplot backend to get the legend to display correctly? I see no reason for this need.

d) I worked internally in the legend function in "points" as by default the text extents were returned in points. Would it be better to work in pixels as that is what the figure position returns by default?

If the answer to a) and b) are "yes" I'll continue to work this up as a changeset, cleaning out the old dead legend code and finishing fixing bug 30519 (rather than just the minimum to get legends working). Otherwise I'll need to consider about how to implement method 3) above in a clean manner.

Regards
David

diff --git a/scripts/plot/__go_draw_axes__.m b/scripts/plot/__go_draw_axes__.m
--- a/scripts/plot/__go_draw_axes__.m
+++ b/scripts/plot/__go_draw_axes__.m
@@ -1353,7 +1353,7 @@
     else
       if (nd == 3)
         fputs (plot_stream, "set border 895;\n");
-      else
+      elseif (! isempty (axis_obj.ytick))
         if (strcmpi (axis_obj.yaxislocation, "right"))
           fprintf (plot_stream, "unset ytics; set y2tics %s nomirror\n",
                    axis_obj.tickdir);
diff --git a/scripts/plot/__plt_get_axis_arg__.m 
b/scripts/plot/__plt_get_axis_arg__.m
--- a/scripts/plot/__plt_get_axis_arg__.m
+++ b/scripts/plot/__plt_get_axis_arg__.m
@@ -40,7 +40,8 @@
       && varargin{1}(1) != 0 && ! isfigure (varargin{1}(1)))
     tmp = varargin{1};
     obj = get (tmp);
-    if (strcmp (obj.type, "axes") || strcmp (obj.type, "hggroup"))
+    if ((strcmp (obj.type, "axes") && ! strcmp (obj.tag, "legend")) 
+        || strcmp (obj.type, "hggroup"))
       h = ancestor (tmp, "axes");
       varargin(1) = [];
       if (isempty (varargin))
diff --git a/scripts/plot/legend.m b/scripts/plot/legend.m
--- a/scripts/plot/legend.m
+++ b/scripts/plot/legend.m
@@ -1,5 +1,4 @@
-## Copyright (C) 2001, 2006, 2007, 2008, 2009 Laurent Mazet
-## Copyright (C) 2006 John W. Eaton
+## Copyright (C) 2010 David Bateman
 ##
 ## This file is part of Octave.
 ##
@@ -22,6 +21,7 @@
 ## @deftypefnx {Function File} {} legend (@var{matstr})
 ## @deftypefnx {Function File} {} legend (@var{cell})
 ## @deftypefnx {Function File} {} legend (@dots{}, "location", @var{pos})
+## @deftypefnx {Function File} {} legend (@dots{}, "orientation", @var{orient})
 ## @deftypefnx {Function File} {} legend (@var{hax}, @dots{})
 ## @deftypefnx {Function File} {} legend (@var{hobjs}, @dots{})
 ## @deftypefnx {Function File} {} legend (@var{hax}, @var{hobjs}, @dots{})
@@ -70,6 +70,10 @@
 ##   can be appended to any location string
 ## @end multitable
 ##
+## The optional parameter @var{orient} determines if the key elements
+## are placed vertically or horizontally. The allowed values are "vertical"
+## or "horizontal" with the default being "vertical".
+##
 ## The following customizations are available using @var{option}:
 ##
 ## @table @asis
@@ -94,25 +98,53 @@
 ## @end table
 ## @end deftypefn
 
-function legend (varargin)
+function [hlegend2, hobjects2, hplot2, text_strings2] = legend (varargin)
 
-  [ca, varargin, nargin] = __plt_get_axis_arg__ ("legend", varargin{:});
-  nargs = nargin;
+  [ca, varargin, nargs] = __plt_get_axis_arg__ (true, "legend", varargin{:});
+  if (isnan (ca))
+    fig = get (0, "currentfigure");
+    if (isempty (fig))
+      ca = gca ();
+      fig = get (ca, "parent");
+    else
+      ca = get (fig, "children");
+      ca ( ! strcmp (get (get (fig, "children"), "type"), "axes")) = [];
+      ca_pos = get (get (fig, "currentaxes"), "position");
+      ca_outpos = get (get (fig, "currentaxes"), "outerposition");
+      for i = numel (ca) : -1 : 1
+        if (! all (ca_pos, get (ca(i), "position") )
+            || ! all (ca_outpos, get (ca(i), "outerposition")))
+          ca(i) = [];
+        endif
+      endfor
+    endif
+  else
+    fig = get (ca, "parent");
+  endif
 
   if (all (ishandle (varargin{1})))
     kids = flipud (varargin{1}(:));
     varargin(1) = [];
     nargs = numel (varargin);
   else
-    kids = get (ca, "children");
+    kids = get (fig, "children");
+    kids (strcmp (get (kids, "tag"), "legend")) = [];
+    if (isscalar (kids))
+      kids = get(kids, "children")(:);
+    else
+      kids = [get(kids, "children"){:}](:);
+    endif
   endif
   nkids = numel (kids);
 
+  position = "northeast";
+  orientation = "vertical";
   if (nargs > 0)
     pos = varargin{nargs};
     if (isnumeric (pos) && isscalar (pos) && round (pos) == pos)
       if (pos >= -1 && pos <= 4)
-        set (ca, "keypos", pos);
+        position = {"northeastoutside", "best", "northeast",
+                    "northwest", "southwest", "southeast"} (pos + 2);
         nargs--;
       else
         error ("legend: invalid position specified");
@@ -120,17 +152,64 @@
     endif
   endif
   
-  if (nargs > 1)
+  while (nargs > 1)
     pos = varargin{nargs-1};
     str = varargin{nargs};
     if (strcmpi (pos, "location")  && ischar (str))
-      set (ca, "keypos", str);
+      position = lower (str);
       nargs -= 2;
+    elseif (strcmpi (pos, "orientation")  && ischar (str))
+      orientation = lower (str);
+      nargs -= 2;
+    else
+      break;
     endif
+  endwhile
+
+  ## Validate the orientation 
+  switch (orientation)
+    case {"vertical", "horizontal"}
+    otherwise
+      error ("legend: unrecognized legend orientation");
+  endswitch
+
+  ## Validate the position type is valid
+  outside = false;
+  inout = findstr (position, "outside");
+  if (! isempty (inout))
+    outside = true;
+    position = position(1:inout-1);
+  else
+    outside = false;
   endif
 
-  k = 1;
-  turn_on_legend = false;
+  switch (position)
+    case {"north", "south", "east", "west", "northeast", "northwest", ...
+          "southeast", "southwest"}
+    case "best"
+      warning ("legend: 'Best' not yet implemented for location specifier\n");
+      position = "northeast";
+    otherwise
+      error ("legend: unrecognized legend position");
+  endswitch
+
+  show = "create";
+  textpos = "default";
+  reverse = false;
+  box = "default";
+
+  hlegend = [];
+  fkids = get (fig, "children");
+  for i = 1 : numel(fkids)
+    if (ishandle (fkids (i)) && strcmp (get (fkids (i), "type"), "axes") 
+        && (strcmp (get (fkids (i), "tag"), "legend")))
+      udata = get (fkids (i), "userdata");
+      if (! isempty (intersect (udata.handle, ca)))
+        hlegend = fkids (i);
+        break;
+      endif
+    endif
+  endfor
 
   if (nargs == 1)
     arg = varargin{1};
@@ -139,30 +218,31 @@
         str = tolower (deblank (arg));
         switch (str)
           case {"off", "hide"}
-            set (ca, "key", "off");
+            show = "off";
             nargs--;
           case "show"
-            set (ca, "key", "on");
+            show = "on";
             nargs--;
           case "toggle"
-            val = get (ca, "key");
-            if (strcmpi (val, "on"))
-              set (ca, "key", "off");
+            if (isempty (hlegend) || strcmp (get (hlegend, "visible"), "off"))
+              show = "on";
             else
-              set (ca, "key", "on");
+              show = "off";
             endif
             nargs--;
           case "boxon"
-            set (ca, "key", "on", "keybox", "on");
+            box = "on";
             nargs--;
           case "boxoff"
-            set (ca, "keybox", "off");
+            box = "off";
             nargs--;
           case "left"
-            set (ca, "keyreverse", "off")
+            textpos = "left";
+            reverse = false;
             nargs--;
           case "right"
-            set (ca, "keyreverse", "on")
+            textpos = "right";
+            reverse = true;
             nargs--;
           otherwise
         endswitch
@@ -178,62 +258,514 @@
     endif
   endif
 
-  if (nargs > 0)
-    have_data = false;
-    for k = 1:nkids
-      typ = get (kids(k), "type");
-      if (strcmp (typ, "line") || strcmp (typ, "surface")
-          || strcmp (typ, "patch") || strcmp (typ, "hggroup"))
-        have_data = true;
+  if (strcmp (show, "off"))
+    if (! isempty (hlegend))
+      set (hlegend, "visible", "off");
+      set (get (hlegend, "children"), "visible", "off");
+      hlegend = [];
+    endif
+    hobjects = [];
+    hplots  = [];
+    text_strings = {};
+  elseif (strcmp (show, "on"))
+    if (! isempty (hlegend))
+      set (hlegend, "visible", "on");
+      set (get (hlegend, "children"), "visible", "on");
+    else
+      hobjects = [];
+      hplots  = [];
+      text_strings = {};
+    endif
+  elseif (strcmp (box, "on"))
+    if (! isempty (hlegend))
+      set (hlegend, "visible", "on", "box", "on");
+    endif
+  elseif (strcmp (box, "off"))
+    if (! isempty (hlegend))
+      set (hlegend, "box", "off", "visible", "off");
+    endif
+  else
+    hobjects = [];
+    hplots  = [];
+    text_strings = {};
+
+    if (nargs > 0)
+      have_data = false;
+      for k = 1:nkids
+        typ = get (kids(k), "type");
+        if (strcmp (typ, "line") || strcmp (typ, "surface")
+            || strcmp (typ, "patch") || strcmp (typ, "hggroup"))
+          have_data = true;
+          break;
+        endif
+      endfor
+
+      if (! have_data)
+        warning ("legend: plot data is empty; setting key labels has no 
effect");
+      endif
+    endif
+
+    if (strcmp (textpos, "default"))
+      warned = false;
+      k = nkids;
+      for i = 1 : nargs
+        arg = varargin{i};
+        if (ischar (arg))
+          typ = get (kids(k), "type");
+          while (k > 0
+                 && ! (strcmp (typ, "line") || strcmp (typ, "surface")
+                       || strcmp (typ, "patch") || strcmp (typ, "hggroup")))
+            typ = get (kids(--k), "type");
+          endwhile
+          if (k > 0)
+            if (strcmp (get (kids(k), "type"), "hggroup"))
+              hgkids = get (kids(k), "children");
+              for j = 1 : length (hgkids)
+                hgobj = get (hgkids (j));
+                if (isfield (hgobj, "displayname"))
+                  set (hgkids(j), "displayname", arg);
+                  hplots = [hplots, hgkids(j)];
+                  text_strings = {text_strings{:}, arg};
+                  break;
+                endif
+              endfor
+            else
+              set (kids(k), "displayname", arg);
+              hplots = [hplots, kids(k)];
+              text_strings = {text_strings{:}, arg};
+            endif
+
+            if (--k == 0)
+              break;
+            endif
+          elseif (! warned)
+            warned = true;
+            warning ("legend: ignoring extra labels");
+          endif
+        else
+          error ("legend: expecting argument to be a character string");
+        endif
+      endfor
+    else
+      k = nkids;
+      while (k > 0)
+        typ = get (kids(k), "type");
+        while (k > 0
+               && ! (strcmp (typ, "line") || strcmp (typ, "surface")
+                     || strcmp (typ, "patch") || strcmp (typ, "hggroup")))
+          typ = get (kids(--k), "type");
+        endwhile
+        if (k > 0)
+          if (strcmp (get (kids(k), "type"), "hggroup"))
+            hgkids = get (kids(k), "children");
+            for j = 1 : length (hgkids)
+              hgobj = get (hgkids (j));
+              if (isfield (hgobj, "displayname") 
+                  && ! isempty (hgobj.displayname))
+                hplots = [hplots, hgkids(j)];
+                text_strings = {text_strings{:}, hbobj.displayname};
+                break;
+              endif
+            endfor
+          else
+            if (! isempty (get (kids (k), "displayname")))
+              hplots = [hplots, kids(k)];
+              text_strings = {text_strings{:}, get(kids (k), "displayname")};
+            endif
+          endif
+          if (--k == 0)
+            break;
+          endif
+        endif
+      endwhile
+    endif
+
+    if (isempty (hplots))
+      if (! isempty (hlegend))
+        fkids = get (fig, "children");
+        delete (fkids (fkids == hlegend));
+        hlegend = [];
+        hobjects = [];
+        hplots  = [];
+        text_strings = {};
+      endif
+    else
+      ## Delete the old legend if it exists
+      if (! isempty (hlegend))
+        fkids = get (fig, "children");
+        delete (fkids (fkids == hlegend));
+      endif
+      
+      ## Force the figure to be drawn here, so that the figure position
+      ## is updated correctly before reading it
+      drawnow ();
+
+      ## Get axis size and fontsize in points.  
+      ## Rely on listener to handle coversion.
+      units = get (ca(1), "units");
+      fontunits = get (ca(1), "fontunits");
+      unwind_protect
+        set (ca(1), "units", "points");
+        set (ca(1), "fontunits", "points");
+        ca_pos = get (ca(1), "position");
+        ca_outpos = get (ca(1), "outerposition");
+        ca_fontsize = get (ca(1), "fontsize");
+      unwind_protect_cleanup
+        set (ca(1), "units", units);
+        set (ca(1), "fontunits", fontunits);
+      end_unwind_protect
+      
+      ## Padding between legend entries horizontally and vertically
+      xpadding = 1.2;
+      ypadding = 1.2;
+
+      ## Length of line segments in the legend in points
+      linelength = 15;
+
+      ## Create the axis first
+      ## FIXME hlegend should inherit properties from "ca"
+      curaxes = get (fig, "currentaxes");
+      unwind_protect
+        hlegend = axes ("tag", "legend", "userdata", struct ("handle", ca),
+                        "box", "off", "outerposition", [0, 0, 0, 0],
+                        "xtick", [], "ytick", [], "xticklabel", "",
+                        "yticklabel", "", "zticklabel", "", 
+                        "xlim", [0, 1], "ylim", [0, 1], "visible", "off");
+
+        ## Add text label to the axis first, checking their extents
+        nentries = numel (hplots);
+        texthandle = [];
+        maxwidth = 0;
+        maxheight = 0;
+        for k = 1 : nentries
+          if (reverse)
+            texthandle = [texthandle, text(0, 0, text_strings {k}, 
+                                           "horizontalalignment", "left")];
+          else
+            texthandle = [texthandle, text(0, 0, text_strings {k},
+                                           "horizontalalignment", "right")];
+          endif
+          units = get (texthandle (end), "units");
+          unwind_protect
+            set (texthandle (end), "units", "points");
+            extents = get (texthandle (end), "extent");
+            ## FIXME fudge for gnuplot as the text extents are calculated from
+            ## the FreeType text render rather than from gnuplot itself. Your
+            ## luck will vary depending on the terminals that are used.
+            if (strcmp (get (fig, "__backend__"), "gnuplot"))
+              extents = [1,1,1.1,1] .* extents;
+              linelength = 20;
+            endif
+            maxwidth = max (maxwidth, extents (3));
+            maxheight = max (maxheight, extents (4));
+          unwind_protect_cleanup
+            set (texthandle (end), "units", units);
+          end_unwind_protect
+        endfor
+
+        num1 = nentries;
+        if (strcmp (orientation, "vertical"))
+          height = nentries * ypadding * maxheight;
+          if (outside)
+            if (height > ca_pos (4))
+              ## Avoid shrinking the height of the axis to zero if outside
+              num1 = ca_pos(4) / maxheight / ypadding / 2;
+            endif
+          else
+            if (height > 0.9 * ca_pos (4))
+              num1 = 0.9 * ca_pos(4) / maxheight / ypadding;
+            endif
+          endif
+        else
+          width = nentries * ypadding * maxwidth;
+          if (outside)
+            if (width > ca_pos (3))
+              ## Avoid shrinking the width of the axis to zero if outside
+              num1 = ca_pos(3) / maxwidth / ypadding / 2;
+            endif
+          else
+            if (width > 0.9 * ca_pos (3))
+              num1 = 0.9 * ca_pos(3) / maxwidth / ypadding;
+            endif
+          endif
+        endif
+        num2 = ceil (nentries / num1);
+
+        xstep = xpadding * (maxwidth + linelength);
+        xpad = (xpadding - 1) * (maxwidth + linelength);
+        if (reverse)
+          xoffset = xpad / 3;
+          txoffset = 2 * xpad / 3 + linelength;
+        else
+          xoffset = 2 * xpad / 3 + maxwidth;
+          txoffset = xpad / 3 + maxwidth;
+        endif
+        ystep = ypadding * maxheight;
+        ypad = (ypadding - 1) * maxheight;
+        yoffset = ystep / 2;
+
+        ## Place the legend in the desired position
+        if (strcmp (orientation, "vertical"))
+          lpos = [0, 0, num2 * xstep, num1 * ystep];
+        else
+          lpos = [0, 0, num1 * xstep, num2 * ystep];
+        endif
+        switch(position)
+          case "north"
+            if (outside)
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_outpos(2) + ca_outpos(4) - lpos(4), lpos(3), lpos(4)];
+
+              new_pos = [ca_pos(1), ca_pos(2), ca_pos(3), ca_pos(4) - lpos(4)];
+              new_outpos = [ca_outpos(1), ca_outpos(2), ca_outpos(3), ...
+                            ca_outpos(4) - lpos(4)];
+            else
+              ca_pos
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), 
lpos(4)];
+            endif
+          case "south"
+            if (outside)
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ca_outpos(2), ...
+                      lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2) + lpos(4), ca_pos(3), ...
+                         ca_pos(4) - lpos(4)];
+              new_outpos = [ca_outpos(1), ca_outpos(2) + lpos(4), ...
+                            ca_outpos(3), ca_outpos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + (ca_pos(3) - lpos(3)) / 2, ...
+                      ca_pos(2) + ypad, lpos(3), lpos(4)];
+            endif
+          case "east"
+            if (outside)
+              lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3), ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2), ca_pos(3) - lpos(3), ca_pos(4)];
+              new_outpos = [ca_outpos(1), ca_outpos(2), ...
+                            ca_outpos(3) - lpos(3), ca_outpos(4)];
+            else
+              lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
+            endif
+          case "west"
+            if (outside)
+              lpos = [ca_outpos(1), ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, ...
+                      lpos(3), lpos(4)];
+              new_pos = [ca_pos(1) + lpos(3), ca_pos(2), ...
+                         ca_pos(3) - lpos(3), ca_pos(4)];
+              new_outpos = [ca_outpos(1) + lpos(3), ca_outpos(2), ...
+                            ca_outpos(3) - lpos(3), ca_outpos(4)];
+            else
+              lpos = [ca_pos(1) +  ypad, ...
+                      ca_pos(2) + (ca_pos(4) - lpos(4)) / 2, lpos(3), lpos(4)];
+            endif
+          case "northeast"
+            if (outside)
+              lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3), ...
+                      ca_outpos(2) + ca_outpos(4) - lpos(4), lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2), ca_pos(3) - lpos(3), ...
+                         ca_pos(4) - lpos(4)];
+              new_outpos = [ca_outpos(1), ca_outpos(2), ...
+                            ca_outpos(3) - lpos(3), ca_outpos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), 
lpos(4)];
+            endif
+          case "northwest"
+            if (outside)
+              lpos = [ca_outpos(1), ca_outpos(2) + ca_outpos(4) - lpos(4), ...
+                      lpos(3), lpos(4)];
+              new_pos = [ca_pos(1) + lpos(3), ca_pos(2), ...
+                         ca_pos(3) - lpos(3), ca_pos(4) - lpos(4)];
+              new_outpos = [ca_outpos(1) + lpos(3), ca_outpos(2), ...
+                            ca_outpos(3) - lpos(3), ca_outpos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + ypad, ...
+                      ca_pos(2) + ca_pos(4) - lpos(4) - ypad, lpos(3), 
lpos(4)];
+            endif
+          case "southeast"
+            if (outside)
+              lpos = [ca_outpos(1) + ca_outpos(3) - lpos(3), ca_outpos(2), 
+                      lpos(3), lpos(4)];
+              new_pos = [ca_pos(1), ca_pos(2) + lpos(4), ...
+                         ca_pos(3) - lpos(3), ca_pos(4) - lpos(4)];
+              new_outpos = [ca_outpos(1), ca_outpos(2) + lpos(4), ...
+                            ca_outpos(3) - lpos(3), ca_outpos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + ca_pos(3) - lpos(3) - ypad, ...
+                      ca_pos(2) + ypad, lpos(3), lpos(4)];
+            endif
+          case "southwest"
+            if (outside)
+              lpos = [ca_outpos(1), ca_outpos(2), 0, lpos(3), lpos(4)];
+              new_pos = [ca_pos(1) +lpos(3), ca_pos(2) + lpos(4), ...
+                         ca_pos(3) - lpos(3), ca_pos(4) - lpos(4)];
+              new_outpos = [ca_outpos(1) + lpos(3), ca_outpos(2) + lpos(4), ...
+                            ca_outpos(3) - lpos(3), ca_outpos(4) - lpos(4)];
+            else
+              lpos = [ca_pos(1) + ypad, ca_pos(2) + ypad, lpos(3), lpos(4)];
+            endif
+        endswitch
+
+        units = get (hlegend, "units");
+        unwind_protect
+          set (hlegend, "units", "points");
+          set (hlegend, "position", lpos, "outerposition", lpos);
+        unwind_protect_cleanup
+          set (hlegend, "units", units);
+        end_unwind_protect
+
+        ## Now write the line segments and place the text objects correctly
+        xk = 0;
+        yk = 0;
+        for k = 1 : numel (hplots)
+          hobjects = [hobjects, texthandle (k)];
+          color = get (hplots (k), "color");
+          style = get (hplots (k), "linestyle");
+          if (! strcmp (style, "none"))
+            l1 = line ("xdata", ([xoffset, xoffset + linelength] + xk * xstep) 
/ lpos(3),
+                       "ydata", [1, 1] .* (lpos(4) - yoffset - yk * ystep) / 
lpos(4), 
+                       "color", color, "linestyle", style);
+            hobjects = [hobjects, l1];
+          endif
+          marker = get (hplots (k), "marker");
+          if (! strcmp (marker, "none"))
+            l1 = line ("xdata", (xoffset + 0.5 * linelength  + xk * xstep) / 
lpos(3),
+                       "ydata", (lpos(4) - yoffset - yk * ystep) / lpos(4), 
+                       "color", color, "marker", marker,
+                      "markeredgecolor", get (hplots (k), "markeredgecolor"),
+                      "markerfacecolor", get (hplots (k), "markerfacecolor"),
+                      "markersize", get (hplots (k), "markersize"));
+            hobjects = [hobjects, l1];
+          endif
+          set (texthandle (k), "position", [(txoffset + xk * xstep) / lpos(3), 
...
+                                            (lpos(4) - yoffset - yk * ystep) / 
lpos(4)]);
+
+          if (strcmp (orientation, "vertical"))
+            yk++;
+            if (yk > num1)
+              yk = 0;
+              xk++;
+            endif
+          else
+            xk++;
+            if (xk > num1)
+              xk = 0;
+              yk++;
+            endif
+          endif
+        endfor
+
+        ## Add an invisible text object to original axis
+        ## that when it is destroyed will remove the legend
+        t1 = text (0, 0, "", "parent", ca(1), "tag", "legend", 
+                   "handlevisibility", "off", "visible", "off",
+                   "xliminclude", "off", "yliminclude", "off");
+        set (t1, "deletefcn", address@hidden, hlegend});
+
+        ## Resize the axis the legend is attached to if the
+        ## legend is "outside" the plot and create listener to 
+        ## resize axis to original size if the legend is deleted, 
+        ## hidden or shown
+        if (outside)
+          for i = 1 : numel (ca)
+            units = get (ca(i), "units");
+            unwind_protect
+              set (ca(i), "units", "points");
+              set (ca (i), "position", new_pos, "outerposition", new_outpos);
+            unwind_protect_cleanup
+              set (ca(i), "units", units);
+            end_unwind_protect
+          endfor
+
+          set (hlegend, "deletefcn", address@hidden, ca, ...
+                                      ca_pos, ca_outpos, t1});
+          addlistener (hlegend, "visible", address@hidden, ca, ...
+                                            ca_pos, new_pos, ...
+                                            ca_outpos, new_outpos});
+        else
+          set (hlegend, "deletefcn", address@hidden, ca, [], [], t1});
+        endif
+
+
+        ## FIXME A hack for gnuplot. God knows why this is needed
+        if (strcmp (get (fig, "__backend__"), "gnuplot"))
+          set (ca(1), "activepositionproperty", "position");
+        endif
+      unwind_protect_cleanup
+        set (fig, "currentaxes", curaxes);
+      end_unwind_protect
+    endif
+  endif
+
+  if (nargout > 0)
+    hlegend2 = hlegend2;
+    hobjects2 = hobjects;
+    hplot2 = hplots;
+    text_strings2 = text_strings;
+  endif
+
+endfunction
+
+function hideshowlegend (h, d, ca, pos1, pos2, outpos1, outpos2)
+  isvisible = strcmp (get (h, "visible"), "off");
+  if (! isvisible)
+    kids = get (h, "children");
+    for i = 1 : numel (kids)
+      if (! strcmp (get (kids(i), "visible"), "off"))
+        isvisible = true;
         break;
       endif
     endfor
-    if (! have_data)
-      warning ("legend: plot data is empty; setting key labels has no effect");
-    endif
   endif
 
-  warned = false;
-  k = nkids;
-  for i = 1:nargs
-    arg = varargin{i};
-    if (ischar (arg))
-      typ = get (kids(k), "type");
-      while (k > 1
-             && ! (strcmp (typ, "line") || strcmp (typ, "surface")
-                   || strcmp (typ, "patch") || strcmp (typ, "hggroup")))
-        typ = get (kids(--k), "type");
-      endwhile
-      if (k > 0)
-        if (strcmp (get (kids(k), "type"), "hggroup"))
-          hgkids = get (kids(k), "children");
-          for j = 1 : length (hgkids)
-            hgobj = get (hgkids (j));
-            if (isfield (hgobj, "keylabel"))
-              set (hgkids(j), "keylabel", arg);
-              break;
-            endif
-          endfor
+  for i = 1 : numel (ca)
+    if (ishandle (ca(i)) && strcmp (get (ca(i), "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")) &&
+        strcmp (get (ca(i), "beingdeleted"), "off"))
+      units = get (ca(i), "units");
+      unwind_protect
+        set (ca(i), "units", "points");
+        if (isvisible)
+          set (ca(i), "position", pos2, "outerposition", outpos2);
         else
-          set (kids(k), "keylabel", arg);
+          set (ca(i), "position", pos1, "outerposition", outpos1);
         endif
-        turn_on_legend = true;
-        if (--k == 0)
-          break;
-        endif
-      elseif (! warned)
-        warned = true;
-        warning ("legend: ignoring extra labels");
-      endif
-    else
-      error ("legend: expecting argument to be a character string");
+      unwind_protect_cleanup
+        set (ca(i), "units", units);
+      end_unwind_protect
     endif
   endfor
+endfunction
 
-  if (turn_on_legend)
-    set (ca, "key", "on");
+function deletelegend1 (h, d, ca)
+  if (ishandle (ca) && strcmp (get (ca, "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")) &&
+      strcmp (get (ca, "beingdeleted"), "off"))
+    delete (ca);
   endif
+endfunction
 
+function deletelegend2 (h, d, ca, pos, outpos, t1)
+  for i = 1 : numel (ca)
+    if (ishandle (ca(i)) && strcmp (get (ca(i), "type"), "axes") && 
+      (isempty (gcbf()) || strcmp (get (gcbf(), "beingdeleted"),"off")) &&
+        strcmp (get (ca(i), "beingdeleted"), "off"))
+      if (!isempty (pos) && !isempty(outpos))
+        units = get (ca(i), "units");
+        unwind_protect
+          set (ca(i), "units", "points");
+          set (ca(i), "position", pos, "outerposition", outpos, "deletefcn", 
"");
+        unwind_protect_cleanup
+          set (ca(i), "units", units);
+        end_unwind_protect
+      endif
+      if (i == 1)
+        set (t1, "deletefcn", "");
+        delete (t1);
+      endif
+    endif
+  endfor
 endfunction
 
 %!demo
diff --git a/src/graphics.cc b/src/graphics.cc
--- a/src/graphics.cc
+++ b/src/graphics.cc
@@ -2750,7 +2750,7 @@
   Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 
1, 2);
   Matrix pos;
 
-  pos = convert_position (get_position ().matrix_value (), get_units (),
+  pos = convert_position (position.get ().matrix_value (), get_units (),
                           "pixels", screen_size);
 
   pos(0)--;
@@ -2774,6 +2774,14 @@
   set_position (pos);
 }
 
+octave_value
+figure::properties::get_position (void) const
+{
+  Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 
1, 2);
+  return convert_position (position.get ().matrix_value (), 
+                           "pixels", get_units (), screen_size);
+}
+
 void
 figure::properties::set_position (const octave_value& v)
 {
@@ -2782,7 +2790,9 @@
       Matrix old_bb, new_bb;
 
       old_bb = get_boundingbox ();
-      position = v;
+      Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n 
(0, 2, 1, 2);
+      position = convert_position (v.matrix_value (), get_units (), 
+                                   "pixels", screen_size);
       new_bb = get_boundingbox ();
 
       if (old_bb != new_bb)
@@ -2798,6 +2808,55 @@
     }
 }
 
+octave_value
+figure::properties::get_paperposition (void) const
+{
+  Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 
1, 2);
+  return convert_position (paperposition.get ().matrix_value (), 
+                           "inches", get_paperunits (), screen_size);
+}
+
+void 
+figure::properties::set_paperposition (const octave_value& val)
+{
+  if (! error_state)
+    {
+      Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n 
(0, 2, 1, 2);
+      Matrix pos =  convert_position (val.matrix_value (), get_paperunits (), 
+                                      "inches", screen_size);
+      if (paperposition.set (octave_value (pos), true))
+        {
+          mark_modified ();
+        }
+    }
+}
+
+octave_value
+figure::properties::get_papersize (void) const
+{
+  Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n (0, 2, 
1, 2);
+  Matrix sz = papersize.get ().matrix_value ();
+  sz. resize (1, 4);
+  return convert_position (sz, "inches", get_paperunits (), screen_size). 
extract_n (0, 0, 1, 2);
+}
+
+void 
+figure::properties::set_papersize (const octave_value& val)
+{
+  if (! error_state)
+    {
+      Matrix screen_size = xget (0, "screensize").matrix_value ().extract_n 
(0, 2, 1, 2);
+      Matrix sz = val.matrix_value ();
+      sz. resize (1, 4);
+
+      Matrix pos =  convert_position (sz, get_paperunits (), "inches", 
screen_size). extract_n (0, 0, 1, 2);
+      if (papersize.set (octave_value (pos), true))
+        {
+          mark_modified ();
+        }
+    }
+}
+
 std::string
 figure::properties::get_title (void) const
 {
@@ -3673,6 +3732,61 @@
   return pos;
 }
 
+octave_value 
+axes::properties::get_outerposition (void) const 
+{ 
+  graphics_object obj = gh_manager::get_object (get_parent ());
+  Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
+  return convert_position (outerposition.get ().matrix_value (),
+                           "normalized", get_units (), 
+                           parent_bb.extract_n (0, 2, 1, 2));
+}
+    
+void 
+axes::properties::set_outerposition (const octave_value& val)
+{
+  if (! error_state)
+    {
+      graphics_object obj = gh_manager::get_object (get_parent ());
+      Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
+      Matrix pos = convert_position (val.matrix_value(), get_units (), 
+                                     "normalized",
+                                     parent_bb.extract_n (0, 2, 1, 2));
+      if (outerposition.set (octave_value (pos), true))
+        {
+          update_outerposition ();
+          mark_modified ();
+        }
+    }
+}
+
+octave_value 
+axes::properties::get_position (void) const
+{ 
+  graphics_object obj = gh_manager::get_object (get_parent ());
+  Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
+  return convert_position (position.get ().matrix_value (), 
+                           "normalized", get_units (), 
+                           parent_bb.extract_n (0, 2, 1, 2));
+}
+
+void 
+axes::properties::set_position (const octave_value& val)
+{
+  if (! error_state)
+    {
+      graphics_object obj = gh_manager::get_object (get_parent ());
+      Matrix parent_bb = obj.get_properties ().get_boundingbox (true);
+      Matrix pos = convert_position (val.matrix_value (), get_units (), 
"normalized",
+                                     parent_bb.extract_n (0, 2, 1, 2));
+      if (position.set (octave_value (pos), true))
+        {
+          update_position ();
+          mark_modified ();
+        }
+    }
+}
+
 ColumnVector
 graphics_xform::xform_vector (double x, double y, double z)
 {
diff --git a/src/graphics.h.in b/src/graphics.h.in
--- a/src/graphics.h.in
+++ b/src/graphics.h.in
@@ -2663,14 +2663,14 @@
       string_property name , ""
       bool_property numbertitle , "on"
       radio_property paperunits , "{inches}|centimeters|normalized|points"
-      array_property paperposition , default_figure_paperposition ()
+      array_property paperposition SG , default_figure_paperposition ()
       radio_property paperpositionmode , "auto|{manual}"
-      array_property papersize , default_figure_papersize ()
+      array_property papersize SG , default_figure_papersize ()
       radio_property papertype , 
"{usletter}|uslegal|a0|a1|a2|a3|a4|a5|b0|b1|b2|b3|b4|b5|arch-a|arch-b|arch-c|arch-d|arch-e|a|b|c|d|e|tabloid|<custom>"
       radio_property pointer , 
"crosshair|fullcrosshair|{arrow}|ibeam|watch|topl|topr|botl|botr|left|top|right|bottom|circle|cross|fleur|custom|hand"
       array_property pointershapecdata , Matrix (16, 16, 0)
       array_property pointershapehotspot , Matrix (1, 2, 0)
-      array_property position S , default_figure_position ()
+      array_property position SG , default_figure_position ()
       radio_property renderer , "{painters}|zbuffer|opengl|none"
       radio_property renderermode , "{auto}|manual"
       bool_property resize , "on"
@@ -2934,7 +2934,7 @@
     // properties which are not in matlab: interpreter
 
     BEGIN_PROPERTIES (axes)
-      array_property position u , default_axes_position ()
+      array_property position uSG , default_axes_position ()
       bool_property box , "on"
       bool_property key , "off"
       bool_property keybox , "off"
@@ -2996,7 +2996,7 @@
       array_property view u , Matrix ()
       bool_property __hold_all__ h , "off"
       radio_property nextplot , "new|add|replacechildren|{replace}"
-      array_property outerposition u , default_axes_outerposition ()
+      array_property outerposition uSG , default_axes_outerposition ()
       radio_property activepositionproperty , "{outerposition}|position"
       color_property ambientlightcolor , color_values (1, 1, 1)
       array_property cameraposition m , Matrix (1, 3, 0.0)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]