changeset 19831:ab1185e08273

maint: Move annotation.m from plot/draw to plot/appearance directory. * scripts/plot/appearance/annotation.m: Moved from plot/draw. * scripts/plot/appearance/module.mk: Add annotation.m to build system. * scripts/plot/draw/module.mk: Remove annotation.m from build system.
author Rik <rik@octave.org>
date Fri, 20 Feb 2015 14:59:43 -0800
parents a0c9c85860c0
children 925fdd91abba
files scripts/plot/appearance/annotation.m scripts/plot/appearance/module.mk scripts/plot/draw/annotation.m scripts/plot/draw/module.mk
diffstat 4 files changed, 1242 insertions(+), 1242 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/appearance/annotation.m	Fri Feb 20 14:59:43 2015 -0800
@@ -0,0 +1,1241 @@
+## Copyright (C) 2015 Pantxo Diribarne
+## 
+##   This program 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.
+##   
+##   This program 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} {} annotation (@var{type})
+## @deftypefnx {Function File} {} annotation ("line", @var{x}, @var{y})
+## @deftypefnx {Function File} {} annotation ("arrow", @var{x}, @var{y})
+## @deftypefnx {Function File} {} annotation ("doublearrow", @var{x}, @var{y})
+## @deftypefnx {Function File} {} annotation ("textarrow", @var{x}, @var{y})
+## @deftypefnx {Function File} {} annotation ("textbox", @var{pos})
+## @deftypefnx {Function File} {} annotation ("rectangle", @var{pos})
+## @deftypefnx {Function File} {} annotation ("ellipse", @var{pos})
+## @deftypefnx {Function File} {} annotation (@var{hf}, @dots{})
+## @deftypefnx {Function File} {} annotation (@dots{}, @var{prop}, @var{val})
+## @deftypefnx {Function File} {@var{h} =} annotation (@dots{})
+## Draw annotations to emphasize parts of a figure.
+##
+## You may build a default annotation specifying only the type
+## @var{type} of the annotation.  Otherwise you can set the position of
+## the annotation using either @var{x} and @var{y} coordinates for
+## line-based annotations or a position vector @var{pos} for others.
+## In any case, coordinates are interpreted using the @qcode{"units"}
+## property of the annotation objects: the default is
+## @qcode{"normalized"}, which means the lower left hand corner of the
+## figure has coordinates @samp{[0 0]} and the upper right hand corner
+## @samp{[1 1]}. 
+##
+## The figure on which the annotations should be drawn may be 
+## specified by providing its graphics handle @var{hf} before any
+## other argument.  Otherwise annotations are drawn on the current
+## figure. 
+## 
+## Further arguments can be provided in the form of
+## @var{prop}/@var{val} pairs to customize the annotation appearance
+## and the units in which coordinates are interpreted.  The annotation
+## can also be customized afterward using its graphics handle 
+## @var{h} and @code{set} function.
+## 
+## All annotation objects share two properties:
+##
+## @itemize
+## @item
+##  @qcode{"units"}: the units in which coordinates are interpreted.
+##  Its value may be one of @qcode{"centimeters"} |
+##  @qcode{"characters"} | @qcode{"inches"} | @qcode{"@{normalized@}"}
+##  | @qcode{"pixels"} | @qcode{"points"}.
+##
+## @item
+##  @qcode{"position"}: a four elements vector [x0 y0 width height]
+##  specifying the coordinates (x0,y0) of the origin of the annotation
+##  object, its width and its height.  The width and height may be
+##  negative, depending on the orientation of the object.
+##
+## @end itemize
+##
+## Valid annotation types and their specific properties are described
+## below: 
+##
+## @table @asis
+## @item @qcode{"line"}
+##  Constructs a line.  @var{x} and @var{y} must be two
+##  elements vectors specifying the x and y coordinates of the two
+##  ends of the line.
+##
+##  The line can be customized using @qcode{"linewidth"},
+##  @qcode{"linestyle"} and @qcode{"color"} properties the same way
+##  as with @code{line} objects.
+##
+## @item  @qcode{"arrow"}
+##   Construct an arrow.  The second point in vectors @var{x} and
+##   @var{y} specifies the arrowhead coordinates.
+##
+##  Besides line properties, the arrowhead can be customized using
+##  @qcode{"headlength"}, @qcode{"headwidth"} and @qcode{"headstyle"}
+##  properties.  Supported values for @qcode{"headstyle"} property are:
+##  [@qcode{"diamond"} | @qcode{"ellipse"} | @qcode{"plain"} |
+##  @qcode{"rectangle"} | @qcode{"vback1"} | @qcode{"@{vback2@}"} | 
+##  @qcode{"vback3"}] 
+## 
+## @item  @qcode{"doublearrow"}
+##   Construct a double arrow.  Vectors @var{x} and @var{y} specify the
+##   arrowheads coordinates.
+##
+##  The line and the arrowhead can be customized as for arrow
+##  annotations but some property names are duplicated:
+##  @qcode{"head1length"}/@qcode{"head2length"},
+##  @qcode{"head1width"}/@qcode{"head2width"}, etc.  The index 1 marks
+##  the properties of the arrowhead at the first point in @var{x} and
+##  @var{y} coordinates. 
+##
+## @item  @qcode{"textarrow"}
+##  Construct an arrow with a text label at the opposite of the
+##  arrowhead.
+##
+##  The line and the arrowhead can be customized as for arrow
+##  annotations, and the text can be customized using the same
+##  properties as @code{text} graphics objects.  Note however
+##  that some text property names are prefixed with "text" to
+##  distinguish arrow and text properties:
+##  @qcode{"textbackgroundcolor"}, @qcode{"textcolor"},
+##  @qcode{"textedgecolor"}, @qcode{"textlinewidth"},
+##  @qcode{"textmargin"}, @qcode{"textrotation"}.
+##
+## @item  @qcode{"textbox"}
+##  Construct a box with a text inside.  @var{pos} specifies the
+##  @qcode{"position"} property of the annotation.
+##
+##  You may use @qcode{"backgroundcolor"}, @qcode{"edgecolor"},
+##  @qcode{"linestyle"} , @qcode{"linewidth"} properties to customize
+##  the box background color and edges appearance.  A limited set of
+##  @code{text} objects properties is also available: besides
+##  @qcode{"font@dots{}"} properties, you may also use
+##  @qcode{"horizontalalignment"} and @qcode{"verticalalignment"} to
+##  move the text inside the box.
+##
+##  Finally the  @qcode{"fitboxtotext"} property controls the actual
+##  extent of the box.  If @qcode{"on"} (the default) the
+##  box limits are fitted to the text extent.
+##
+## @item  @qcode{"rectangle"}
+##  Construct a rectangle.  @var{pos} specifies the
+##  @qcode{"position"} property of the annotation. 
+##
+##  You may use @qcode{"facecolor"}, @qcode{"color"},
+##  @qcode{"linestyle"} and @qcode{"linewidth"} properties to customize
+##  the rectangle background color and edges appearance.
+##
+## @item  @qcode{"ellipse"}
+##  Construct an ellipse.  @var{pos} specifies the
+##  @qcode{"position"} property of the annotation. 
+##
+##  See @qcode{"rectangle"} annotations for customization. 
+## @end table
+## 
+## @seealso{xlabel, title}
+## @end deftypefn
+
+function varargout = annotation (varargin)
+
+  objtype = "";
+  hf = [];
+  lims = [];
+  x = y = [];
+  opts = {};
+  
+  nargin = numel (varargin);
+  if (nargin == 0)
+    print_usage ();
+  endif
+  
+  
+  ## Parent figure
+  if (isfigure (varargin{1}))
+    hf = varargin{1};
+    varargin = varargin(2:end);
+    nargin --;
+  endif
+  
+  ## Annotation type
+  types = {"line", "arrow", "doublearrow", "textarrow", ...
+           "textbox", "ellipse", "rectangle"};
+  if (ischar (varargin{1}))
+    objtype = varargin{1};
+    varargin(1) = [];
+    nargin --;
+  else
+    print_usage ();
+  endif
+  
+  switch objtype
+    case types(1:4)
+         
+      if (nargin == 0)
+        lims  = [];
+      elseif (nargin >= 2)
+        x = varargin{1};
+        y = varargin{2};
+        varargin(1:2) = [];
+      
+        if (isnumeric (x) && isnumeric (y) && 
+            length (x) == 2 && length (y) == 2)
+          lims = [x(1) y(1) diff(x) diff(y)];
+        else 
+          error ("annotation: expect 2 elements vectors for X and Y");
+        endif
+      else
+        print_usage ();
+      endif
+    case types(5:end)
+      if (nargin == 0)
+        lims  = [];
+      else
+        lims = varargin{1};
+        varargin(1) = [];
+        
+        if (! isvector (lims) || length (lims) != 4)
+          error ("annotation: expect 4 elements vector for POS")
+        endif
+      endif
+    otherwise
+      error ("annotation: unknown annotation type %s", objtype)
+  endswitch
+
+  ## options
+  opts = varargin;
+  nopts = numel (opts);
+  if (! isempty (opts))
+    if (fix (nopts/2) != nopts/2 ||
+        !all (cellfun (@ischar, opts(1:2:end))))
+      warning ("annotation: couldn't parse PROP/VAL pairs, skipping");
+      opts = {};
+    endif
+  endif
+  
+  ## create annotation
+  showhidden = get (0, "showhiddenhandles");
+  set (0, "showhiddenhandles", "on");
+
+  unwind_protect
+    if (isempty (hf))
+      hf = gcf ();
+    endif
+    
+    ## Axes
+    hca = get (hf, "currentaxes");
+
+    hax = findall (hf, "-depth", 1, "tag", "scribeoverlay");
+    if (isempty (hax))
+      hax = buildoverlay (hf);
+    endif
+
+    ## Build annotation
+    htmp = buildannot (hax, objtype, lims);
+
+    ## Set user defined properties
+    if (!isempty (opts))
+      set (htmp, opts{:});
+    endif
+    
+  unwind_protect_cleanup
+    set (0, "showhiddenhandles", showhidden);
+    set (hf, "currentaxes", hca);
+  end_unwind_protect
+  
+  if (nargout != 0)
+    varargout{1} = htmp;
+  endif
+
+endfunction
+
+function hax = buildoverlay (hf)
+
+  hax = axes ("parent", hf, "position", [0 0 1 1], ...
+              "visible", "off","tag", "scribeoverlay", ...
+              "xlim", [0 1], "ylim", [0 1], ...
+              "handlevisibility", "off");
+  
+  ## hidden property to store figure size in absolute (points)
+  ## coordinates 
+  addproperty ("figsize_points", hax, "axesxmtick", []);
+  update_figsize_points (hf, {}, hax);
+
+  
+  listener = {@update_figsize_points, hax};
+  addlistener (hf, "position", listener);
+    
+  delfcn = @() dellistener (hf, "position", listener);
+  set (hax, "deletefcn", delfcn);
+  
+endfunction
+
+function update_figsize_points (hf, dummy, hax)
+
+  persistent recursive = false;
+  if (! recursive)
+    recursive = true;
+    units = get (hf, "units");
+    set (hf, "units", "points");
+    pos = get (hf, "position");
+    set (hf, "units", units);
+    
+    set (hax, "figsize_points", pos(3:4));
+    recursive = false;
+  endif
+  
+endfunction
+  
+function h = buildannot (hax, objtype, pos)
+
+  ## Base hggroup
+  h = hggroup ("parent", hax);
+  
+  ## Add common properties
+  if (strcmp (graphics_toolkit (), "gnuplot"))
+    ## FIXME: this is a workaround for bug #39394 (gnuplot toolkit)
+    defprops = {"position", "axesposition", [0.3 0.3 0.1 0.1], ...
+                "units", "textunits", "data"}; 
+  else
+    defprops = {"position", "axesposition", [0.3 0.3 0.1 0.1], ...
+                "units", "axesunits", "normalized"};
+  endif
+  addbaseprops (h, defprops);
+  setappdata (h, "__former_units__", "normalized");
+
+  ## Common updaters
+  listener = {@update_position, h, true};
+  
+  addlistener (hax, "figsize_points", listener);
+  
+  delfcn = @() dellistener (hax, "figsize_points", listener);
+  set (h, "deletefcn", delfcn);
+  
+  addlistener (h, "units", {@update_position, h});
+  
+  ## Now work with normalized coordinates
+  if (! isempty (pos))
+    set (h, "position", pos);
+  endif
+  pos = getnormpos (h);
+  
+  ## Build annotation object and its specific properties/updaters
+  switch objtype
+    case {"line", "arrow", "doublearrow", "textarrow"}
+      ## Add properties
+      proptable = lineprops ();
+      if (strcmp (objtype, "arrow"))
+        proptable = [proptable arrowprops()];
+      elseif (strcmp (objtype, "doublearrow"))
+        proptable = [proptable dblarrowprops()];
+      elseif (strcmp (objtype, "textarrow"))
+        proptable = [proptable arrowprops()];
+        proptable = [proptable textprops()];
+      endif
+
+      addbaseprops (h, proptable);
+      
+      ## create line
+      hli = line ([pos(1); (pos(1) + pos(3))],
+                  [pos(2); (pos(2) + pos(4))],
+                  "parent", h, "color", get (h, "color"),
+                  "linestyle", get (h, "linestyle"),
+                  "linewidth", get (h, "linewidth"));
+      
+      ## create patch(s) and text
+      if (strcmp (objtype, "arrow"))
+        [x, y] = arrowcoordinates (h);
+        hpa = patch (x, y, get (h, "color"), "parent", h,
+                    "edgecolor",  get (h, "color"));
+        update_arrow (h, {}, "position", hpa);
+      elseif (strcmp (objtype, "doublearrow"))
+        [x, y] = arrowcoordinates (h, 1);
+        hpa = patch (x, y, get (h, "color"), "parent", h,
+                    "edgecolor",  get (h, "color"));
+        
+        [x, y] = arrowcoordinates (h, 2);
+        hpa(2) = patch (x, y, get (h, "color"), "parent", h,
+                    "edgecolor",  get (h, "color"));
+        
+        update_arrow (h, {}, "position", hpa);
+      elseif (strcmp (objtype, "textarrow"))
+        [x, y] = arrowcoordinates (h);
+        hpa = patch (x, y, get (h, "color"), "parent", h,
+                    "edgecolor",  get (h, "color"));
+        update_arrow (h, {}, "position", hpa);
+        
+        hte = text (get (h, "position")(1), ...
+                   get (h, "position")(2), ...
+                   get (h, "string"), "parent", h, ...
+                   "color", get (h, "color"));
+        propnames = textprops ("names");
+        for ii = 1:numel (propnames)
+          update_text (h, {}, propnames{ii}, hte);
+        endfor
+        update_text (h, {}, "position", hte);
+      endif
+      
+      ## updaters
+      addlistener (h, "color", {@update_line, "color", hli});
+      addlistener (h, "linestyle", {@update_line, "linestyle", hli});
+      addlistener (h, "linewidth", {@update_line, "linewidth", hli});
+      addlistener (h, "x", {@update_line, "x", hli});
+      addlistener (h, "y", {@update_line, "y", hli});
+      addlistener (h, "position", {@update_line, "position", hli});
+
+      if (strcmp (objtype, "arrow"))
+        addlistener (h, "position", {@update_arrow, "position", hpa});
+        addlistener (h, "headwidth", {@update_arrow, "position", hpa});
+        addlistener (h, "headstyle", {@update_arrow, "position", hpa});
+        addlistener (h, "headlength", {@update_arrow, "position", hpa});
+        addlistener (h, "color", {@update_arrow, "color", hpa});
+      elseif (strcmp (objtype, "doublearrow"))
+        addlistener (h, "position", {@update_arrow, "position", hpa});
+        addlistener (h, "head1width", 
+                     {@update_arrow, "position", [hpa(1) 0]});
+        addlistener (h, "head2width",
+                     {@update_arrow, "position", [0 hpa(2)]});
+        addlistener (h, "head1style",
+                     {@update_arrow, "position", [hpa(1) 0]});
+        addlistener (h, "head2style",
+                     {@update_arrow, "position", [0 hpa(2)]});
+        addlistener (h, "head1length",
+                     {@update_arrow, "position", [hpa(1) 0]});
+        addlistener (h, "head2length",
+                     {@update_arrow, "position", [0 hpa(2)]});
+        addlistener (h, "color",
+                     {@update_arrow, "color", hpa});
+      elseif (strcmp (objtype, "textarrow"))
+        addlistener (h, "position", {@update_arrow, "position", hpa});
+        addlistener (h, "headwidth", {@update_arrow, "position", hpa});
+        addlistener (h, "headstyle", {@update_arrow, "position", hpa});
+        addlistener (h, "headlength", {@update_arrow, "position", hpa});
+        addlistener (h, "color", {@update_arrow, "color", hpa});
+        propnames = textprops ("names");
+        for ii = 1:numel (propnames)
+          addlistener (h, propnames{ii},
+                       {@update_text, propnames{ii}, hte});
+          if (any (strcmp (propnames{ii},
+                           {"fontangle", "fontname", ...
+                            "fontsize", "fontweight", ...
+                            "horizontalalignment", "string", ...
+                            "textmargin", "textrotation", ...
+                            "verticalalignment"})))
+            addlistener (h, propnames{ii}, ...
+                         {@update_text, "position", hte});
+          endif
+        endfor
+        addlistener (h, "position", {@update_text, "position", hte});
+        addlistener (h, "color", {@update_text, "color", hte});
+      endif
+      
+    case {"rectangle", "ellipse"}
+         
+      ## Add properties
+      addbaseprops (h, rectprops ());
+      
+      ## Create rectangle/ellipse
+      if (strcmp (objtype, "rectangle"))
+        [x, y] = pos2rect (pos);
+      else
+        [x, y] = pos2ell (pos);
+      endif
+          
+      hr =  patch (x, y, "parent", h);
+      
+      propnames = rectprops ("names");
+      for ii = 1:numel (propnames)
+        update_rect (h, {}, propnames{ii}, hr, objtype);
+      endfor
+
+      ## Updaters
+      addlistener (h, "position", {@update_rect, "position", hr, objtype});
+      for ii = 1:numel (propnames)
+        addlistener (h, propnames{ii},
+                     {@update_rect, propnames{ii}, hr, objtype});
+      endfor
+
+    case "textbox"
+         
+      ## Add properties
+      addbaseprops (h, textboxprops());
+
+      ## Create textbox
+      hpa = patch ("parent", h);
+      hte = text (pos(1), pos(2), get (h, "string"), "parent", h, ...
+                 "color", get (h, "color"));
+      update_textbox (h, {}, "position", [hte hpa]);
+      
+      propnames = textboxprops ("names");
+      for ii = 1:numel (propnames)
+        update_textbox (h, {}, propnames{ii}, [hte hpa]);
+      endfor
+
+      ## Updaters
+      addlistener (h, "position", {@update_textbox, "position", [hte hpa]});
+      for ii = 1:numel (propnames)
+        addlistener (h, propnames{ii},
+                     {@update_textbox, propnames{ii}, [hte hpa]});
+      endfor
+      addlistener (h, "horizontalalignment",
+                   {@update_textbox, "position", [hte hpa]});
+      addlistener (h, "verticalalignment",
+                   {@update_textbox, "position", [hte hpa]});
+      
+  endswitch
+  
+endfunction
+
+function props = lineprops (varargin)
+  props = {"color", "color", [0 0 0], ...
+           "linestyle",  "linelinestyle", "-", ...
+           "linewidth", "linelinewidth", 0.5, ...
+           "x", "linexdata", [0.3 0.4], ...
+           "y", "lineydata", [0.3 0.4]};
+  if (strcmp (varargin, "names"))
+    props = props(1:3:end);
+  endif
+endfunction
+
+function props = arrowprops (varargin)
+  props = {"headlength", "data", 10, ...
+           "headstyle",  "radio", "diamond|ellipse|none|plain|rectangle|vback1|{vback2}|vback3", ...
+           "headwidth", "data", 10};
+  if (strcmp (varargin, "names"))
+    props = props(1:3:end);
+  endif
+endfunction
+
+function props = dblarrowprops (varargin)
+  props = {"head1length", "data", 10, ...
+           "head1style",  "radio", "diamond|ellipse|none|plain|rectangle|vback1|{vback2}|vback3", ...
+           "head1width", "data", 10, ...
+           "head2length", "data", 10, ...
+           "head2style",  "radio", "diamond|ellipse|none|plain|rectangle|vback1|{vback2}|vback3", ...
+           "head2width", "data", 10};
+  if (strcmp (varargin, "names"))
+    props = props(1:3:end);
+  endif
+endfunction
+
+function props = textprops (varargin)
+  props = {"fontangle", "textfontangle", "normal", ...
+           "fontname",  "textfontname", "Helvetica", ...
+           "fontsize", "textfontsize", 10, ...
+           "fontunits", "textfontunits", "points", ...
+           "fontweight",  "textfontweight", "normal", ...
+           "horizontalalignment", "texthorizontalalignment", "left", ...
+           "interpreter", "textinterpreter", "tex", ...
+           "string", "textstring", "", ...
+           "textbackgroundcolor", "textbackgroundcolor", "none", ...
+           "textcolor", "textcolor", "k", ...
+           "textedgecolor", "textedgecolor", "none", ...
+           "textlinewidth", "textlinewidth",0.5, ...
+           "textmargin", "textmargin", 5, ...
+           "textrotation", "textrotation", 0, ...
+           "verticalalignment", "textverticalalignment", "middle"};
+  if (strcmp (varargin, "names"))
+    props = props(1:3:end);
+  endif
+endfunction
+
+function props = textboxprops (varargin)
+  props = {"backgroundcolor", "patchfacecolor", "none", ...
+           "color", "textcolor", [0 0 0], ...
+           "edgecolor", "patchedgecolor", [0 0 0], ...
+           "facealpha", "patchfacealpha", 1, ...
+           "fontangle", "textfontangle", "normal", ...
+           "fontname",  "textfontname", "Helvetica", ...
+           "fontsize", "textfontsize", 10, ...
+           "fontunits", "textfontunits", "points", ...
+           "fontweight",  "textfontweight", "normal", ...
+           "horizontalalignment", "texthorizontalalignment", "left", ...
+           "interpreter", "textinterpreter", "tex", ...
+           "linestyle",  "linelinestyle", "-", ...
+           "linewidth", "linelinewidth", 0.5, ...
+           "string", "textstring", "", ...
+           "fitboxtotext", "radio","{on}|off", ...
+           "margin", "data", 5, ...
+           "verticalalignment", "textverticalalignment",  "middle"};
+  if (strcmp (varargin, "names"))
+    props = props(1:3:end);
+  endif
+endfunction
+
+function props = rectprops (varargin)
+  props = {"edgecolor", "patchedgecolor", "k", ...
+           "facealpha", "patchfacealpha", 1, ...
+           "facecolor", "patchfacecolor", "none", ...
+           "linestyle", "patchlinestyle", "-", ...
+           "linewidth", "patchlinewidth", 0.5};
+  if (strcmp (varargin, "names"))
+    props = props(1:3:end);
+  endif
+endfunction
+
+function addbaseprops (h, proptable)
+  cellfun (@(pname, ptype, parg) addproperty (pname, h, ptype, parg),
+           proptable(1:3:end), proptable(2:3:end), proptable(3:3:end));
+endfunction
+
+function update_position (h1, dummy, h, force = false)
+  if (! force)
+    pos = convertposition (h, getappdata (h, "__former_units__"),
+                           get (h, "units"));
+    setappdata (h, "__former_units__", get (h, "units"));
+    set (h, "position", pos);
+  else
+    ## FIXME: Inefficient trick to force all objects to be redrawn
+    set (h, "position", [0 0 .5 .5],
+         "position", get (h, "position"));
+  endif      
+endfunction
+
+
+function update_line (h, dummy, prop, hli)
+  persistent recursive = false;
+  if (! recursive)
+    switch prop
+      case "color"
+        set (hli, "color", get (h, "color"));
+      case "linestyle"
+        set (hli, "linestyle", get (h, "linestyle"));
+      case "linewidth"
+        set (hli, "linewidth", get (h, "linewidth"));
+      case "x"
+        ## Update position
+        x = get (h, "x");
+        pos = get (h, "position");
+        pos(1) = x(1);
+        pos(3) = diff (x);
+        recursive = true;
+        set (h, "position", pos);
+        recursive = false;
+
+        ## Draw in normalized coordinates
+        pos = getnormpos (h);
+        x = [pos(1) (pos(1) + pos(3))];
+        set (hli, "xdata", x);
+        
+      case "y"
+       ## Update position
+        y = get (h, "y");
+        pos = get (h, "position");
+        pos(2) = y(1);
+        pos(4) = diff (y);
+        recursive = true;
+        set (h, "position", pos);
+        recursive = false;
+
+        ## Draw in normalized coordinates
+        pos = getnormpos (h);
+        y = [pos(2) (pos(2) + pos(4))];
+        set (hli, "ydata", y);
+      case "position"
+        ## Update x and y
+        pos = get (h, "position");
+        x = [pos(1) (pos(1) + pos(3))];
+        y = [pos(2) (pos(2) + pos(4))];
+        
+        recursive = true;
+        set (h, "x", x);
+        set (h, "y", y);
+        recursive = false;
+        
+        ## Draw in normalized coordinates
+        pos = getnormpos (h);
+        x = [pos(1) (pos(1) + pos(3))];
+        y = [pos(2) (pos(2) + pos(4))];
+        set (hli, "xdata", x);
+        set (hli, "ydata", y);
+        
+    endswitch
+  endif
+endfunction
+
+function [x, y] = arrowcoordinates (h, nar = [])
+  warning ("off", "Octave:broadcast", "local")
+  pos = getnormpos (h);
+  ppos = norm2pts (h, pos(3:4).');
+  ang = angle (complex (ppos(1), ppos(2)));
+
+  if (isempty (nar))
+    ln = get (h, "headlength");   # in points
+    wd = get (h, "headwidth");
+    headstyle = get (h, "headstyle");
+    pos = pos(1:2) .+ pos(3:4);
+  elseif (nar == 1)
+    ln = get (h, "head1length"); # in points
+    wd = get (h, "head1width");
+    headstyle = get (h, "head1style");
+    pos = pos(1:2);
+    ang += pi;
+  elseif (nar == 2)
+    ln = get (h, "head2length"); # in points
+    wd = get (h, "head2width");
+    headstyle = get (h, "head2style");
+    pos = pos(1:2) .+ pos(3:4);
+  else
+    error ("annotation: %d, no such arrow number")
+  endif
+
+  switch headstyle
+    case "diamond"
+      x = [0 -ln/2 -ln -ln/2 0];
+      y = [0 -wd/2 0 wd/2 0];
+    case "ellipse"
+      pts = linspace (0, 2*pi, 12);
+      x = ln/2 * (cos (pts) - 1);
+      y = wd/2 * sin (pts);
+    case "rectangle"
+      x = [0 0 -ln -ln 0];
+      y = [wd/2 -wd/2 -wd/2 wd/2 wd/2];
+    case "vback1"
+      x = [0 -ln -0.85*ln -ln 0];
+      y = [0 wd/2 0 -wd/2 0];
+    case "vback2"
+      x = [0 -ln -0.65*ln -ln 0];
+      y = [0 wd/2 0 -wd/2 0];
+    case "vback3"
+      x = [0 -ln -0.2*ln -ln 0];
+      y = [0 wd/2 0 -wd/2 0];
+    case "plain"
+      x = [0 -ln -ln -ln 0];
+      y = [0 wd/2 0 -wd/2 0];
+    case "none"
+      x = [0 0 0];
+      y = [0 0 0];
+    otherwise
+      error ("annotation: \"%s\" headstyle not implemented", headstyle)
+  endswitch
+
+  R = [cos(ang) -sin(ang);
+       sin(ang) cos(ang)];
+  XY = R * [x; y];
+  XY = pts2norm (h, XY);
+  XY = pos(1:2).' .+ XY;
+  
+  x = XY(1,:).';
+  y = XY(2,:).';
+endfunction
+
+function update_arrow (h, dummy, prop, hpa = [])
+  persistent recursive = false;
+  
+  nar = [];
+  for ii = 1:numel (hpa)
+    if (numel (hpa) == 2)
+      nar = ii;
+    endif
+    if (hpa(ii))
+      switch prop
+        case "position"
+          [x, y] = arrowcoordinates (h, nar);
+          set (hpa(ii), "xdata", x, "ydata", y);
+        case "color"
+          set (hpa(ii), "facecolor", get (h, "color"));
+          set (hpa(ii), "edgecolor", get (h, "color"));
+      endswitch
+    endif
+  endfor
+endfunction
+
+function update_text (h, dummy, prop, hte)
+  persistent recursive = false;
+
+  if (! recursive)
+    switch prop
+      case "position"
+        if (isempty (get (h, "string")))
+          return
+        endif
+        
+        pos = getnormpos (h);
+        
+        set (hte, "position", [textcoordinates(hte, pos) 0]);
+        
+      otherwise
+        if (strncmp (prop, "text", 4))
+          set (hte, prop(5:end), get (h, prop));
+        else
+          set (hte, prop, get (h, prop));
+        endif
+    endswitch
+  endif
+endfunction
+
+function update_textbox (h, dummy, prop, htb)
+  persistent recursive = false;
+
+  hte = htb(1);
+  hpa = htb(2);
+  
+  if (! recursive)
+    switch prop
+      case {"edgecolor", "facealpha",
+            "linestyle", "linewidth"}
+        set (hpa, prop, get (h, prop));
+      case {"backgroundcolor"}
+        set (hpa, "facecolor", get (h, prop));
+      otherwise
+        if (! any (strcmp (prop, {"fitboxtotext", "position"})))
+          set (hte, prop, get (h, prop));
+        endif
+           
+        pos = getnormpos (h);
+        
+        if (strcmp (get (h, "fitboxtotext"), "on"))
+          pos(3:4) = get (hte, "extent")(3:4);
+        endif
+        
+        [x, y] = pos2rect (pos);
+        set (hpa, "xdata", x', "ydata", y');
+          
+        switch (get (h, "horizontalalignment"))
+          case "left"
+            x = x(1);
+          case "center"
+            x = mean (x(1:2));
+          case "right"
+            x = x(2);
+        endswitch
+
+        switch (get (h, "verticalalignment"))
+          case {"top", "cap"}
+            y = y(3);
+          case "middle"
+            y = mean (y(2:3));
+          case {"bottom", "baseline"}
+            y = y(2);
+        endswitch
+        set (hte, "position", [x y 0]);
+    endswitch
+  endif
+  
+endfunction
+
+function XY = textcoordinates (hte, pos)
+  warning ("off", "Octave:broadcast", "local")
+
+  ## Get the "tight" extent of the text object in points units
+  textpos = get(hte, "position");
+  rot = get (hte, "rotation");
+  units = get (hte, "units");
+
+  set (hte, "rotation", 0, "units", "points", "position", [0 0 0]);
+  ext = get (hte, "extent");
+  set (hte, "rotation", rot, "units", units, "position", textpos);
+  
+  ## Find which one of the 8 following points we should align the
+  ## arrow with
+  ##  8-----7-----6
+  ##  1  text box 5
+  ##  2-----3-----4
+  
+  ## FIXME: Matlab's horizontal/verticalalignment properties are
+  ## interpreted differently: horizontalalignment is passed to the 
+  ## underlying text object whereas the verticalalignement controls
+  ## the vertical alignment of the arrow.
+  
+  ang = angle (complex (pos(3), pos(4)));
+  rot = rot / 180 * pi;
+
+  [~, pt] = min (abs ((-pi:pi/4:pi) - ang)); 
+  pt -= floor (rot / (pi/4));
+  if (pt <= 0)
+    pt = rem (pt, 8) + 8;
+  elseif (pt > 8)
+    pt = rem (pt, 8);
+  endif
+  
+  ## Compute the text actual "position" property
+  dx = ext(3)/2;
+  dy = ext(4)/2;
+  XY = [-dx -dx 0 dx dx dx 0 -dx;
+        0 -dy -dy -dy 0 dy dy dy];
+  
+  switch get (hte, "horizontalalignment")
+    case "left"
+      XY(1,:) += dx;
+    case "right"
+      XY(1,:) -= dx;
+  endswitch
+  
+  switch get (hte, "verticalalignment")
+    case {"baseline", "bottom"}
+      XY(2,:) += dy;
+    case {"cap", "top"}
+      XY(2,:) -= dy;
+  endswitch
+  
+  R = [cos(rot) -sin(rot);
+       sin(rot) cos(rot)];
+  XY = R * XY;
+  XY = pts2norm (get (hte, "parent"), XY);
+  XY = pos(1:2) .- XY(:,pt).';
+  
+endfunction
+
+function nXY = pts2norm (h, pXY)
+  sz = get (get (h,"parent"), "figsize_points");
+  
+  nXY(1,:) = pXY(1,:) ./ sz(1);
+  nXY(2,:) = pXY(2,:) ./ sz(2);
+endfunction
+
+function pXY = norm2pts (h, nXY)
+  sz = get (get (h,"parent"), "figsize_points");
+  
+  pXY(1,:) = nXY(1,:) .* sz(1);
+  pXY(2,:) = nXY(2,:) .* sz(2);
+endfunction
+
+function pos = convertposition (h, from, to)
+  ## FIXME: handle "characters" units  
+  pos = get (h, "position");
+  
+  ## First convert to normalized coordinates
+  sz = get (get (h,"parent"), "figsize_points");
+  switch from
+    case "centimeters"
+      pos /= 2.54;
+      pos *= 72;
+      pos(1:2:end) /= sz(1);
+      pos(2:2:end) /= sz(2);
+    case "inches"
+      pos *= 72;
+      pos(1:2:end) /= sz(1);
+      pos(2:2:end) /= sz(2);
+    case "pixels"
+      pos /= get (0, "screenpixelsperinch");
+      pos *= 72;
+      pos(1:2:end) /= sz(1);
+      pos(2:2:end) /= sz(2);
+  endswitch
+
+  ## Then convert to requested coordinates
+  switch to
+    case "centimeters"
+      sz /= 72;
+      sz *= 2.54;
+      pos(1:2:end) *= sz(1);
+      pos(2:2:end) *= sz(2);
+    case "inches"
+      sz /= 72;
+      pos(1:2:end) *= sz(1);
+      pos(2:2:end) *= sz(2);
+    case "pixels"
+      sz /= 72;
+      sz *= get (0, "screenpixelsperinch"); 
+      pos(1:2:end) *= sz(1);
+      pos(2:2:end) *= sz(2);
+  endswitch
+
+endfunction
+
+function pos = getnormpos (h)
+  units = get (h, "units");
+  pos = convertposition (h, units, "normalized");
+endfunction
+
+function [x, y] = pos2rect (pos)
+  x = [pos(1) pos(1)+pos(3) pos(1)+pos(3) pos(1)];
+  y = [pos(2) pos(2) pos(2)+pos(4) pos(2)+pos(4)];
+endfunction
+
+function [x, y] = pos2ell (pos)
+  a = pos(3)/2;
+  b = pos(4)/2;
+
+  ## Arbitrarily use 100 points 
+  ## when it is spread over
+  ang = linspace (0, 2*pi, 100);
+  
+  x = a * cos (ang);
+  y = b * sin (ang);
+
+  x += pos(1) + a;
+  y += pos(2) + b;
+endfunction
+
+function update_rect (h, dummy, prop, hre, typ)
+  persistent recursive = false;
+  
+  if (! recursive)
+    switch prop
+      case "position"
+        pos = getnormpos (h);
+        if (strcmp (typ, "rectangle"))
+          [x, y] = pos2rect (pos);
+        else
+          [x, y] = pos2ell (pos);
+        endif
+        
+        set (hre, "xdata", x, "ydata", y);
+      otherwise
+        set (hre, prop, get (h, prop));
+    endswitch
+  endif
+endfunction
+
+
+## FIXME: the additionnal regular axes is necessary for fltk to draw the
+##        annotation axes.
+%!demo
+%! clf; axes ('visible', 'off');
+%! annotation ('ellipse', [.2 .2 .6 .6], 'linewidth', 4)
+%! ang = pi/2:-pi/2:-pi;
+%! lab = {'N', 'W', 'S', 'E'};
+%! x0 = 0.5;
+%! y0 = 0.5;
+%! r = 0.3;
+%! for ii = 1:4
+%!   x = r * cos (ang(ii)) + x0;
+%!   y = r * sin (ang(ii)) + y0;
+%!   annotation ('textarrow', [x x0], [y y0], ...
+%!               'string', lab{ii},  'fontsize', 20);
+%! end
+%! 
+%! h = annotation ('doublearrow', [x0 x0], [y0-r y0+r], ...
+%!                 'head1style', 'diamond', 'head1length', 60, ...
+%!                 'head2style', 'diamond', 'head2length', 60);
+
+%!demo
+%! clf; axes ('visible', 'off');
+%! plot (1:10);
+%! xlabel ('X-LABEL')
+%! ylabel ('LARGE Y-LABEL', 'fontsize', 20)
+%! title ('FIGURE LAYOUT', 'fontsize', 24)
+%! 
+%! ti = get (gca, 'tightinset');
+%! pos = get (gca, 'position');
+%! pos(1:2) = pos(1:2) - ti(1:2);
+%! pos(3) = pos(3) + ti (1) + ti (3);
+%! pos(4) = pos(4) + ti (2) + ti (4);
+%! 
+%! ht = annotation ('textbox', pos, 'string', ' Position + tighinset', ...
+%!                  'fitboxtotext', 'off', 'linestyle', '--', ...
+%!                  'edgecolor', 'g', 'linewidth', 3, 'color', 'g', ...
+%!                  'verticalalignment', 'bottom', 'fontsize', 15);
+%! 
+%! ho = annotation ('textbox', get (gca, 'outerposition'), ...
+%!                  'string', ' Outerposition','fitboxtotext', 'off', ...
+%!                  'linestyle', '--', 'edgecolor', 'r', ...
+%!                  'linewidth', 3, 'color', 'r', ...
+%!                  'verticalalignment', 'bottom', 'fontsize', 15);
+%! 
+%! hi = annotation ('textbox', get (gca, 'position'), ...
+%!                  'string', ' Position','fitboxtotext', 'off', ...
+%!                  'linestyle', '--', 'edgecolor', 'b', ...
+%!                  'linewidth', 3, 'color', 'b', ...
+%!                  'verticalalignment', 'bottom', 'fontsize', 15);
+
+%!demo
+%! clf; axes ('visible', 'off');
+%! h = annotation ('arrow');
+%! 
+%! %% Get allowed headstyles
+%! styles = set (h, 'headstyle');
+%! delete (h)
+%! 
+%! %% Textbox for the title
+%! annotation ('textbox', [0.1 0 0.8 1], 'string', ...
+%!             '"headstyle" property:', ...
+%!             'backgroundcolor', [0.7 0.7 0.7], 'fontsize', 20, ...
+%!             'fitboxtotext', 'off', 'verticalalignment', 'top', ...
+%!             'horizontalalignment', 'center');
+%! 
+%! %% Textarrows 
+%! ns = numel (styles);
+%! nrows = ceil (ns/2);
+%! dy = 1/nrows;
+%! y = 1 - dy/2;
+%! 
+%! jj = 1;
+%! for ii = 1:nrows
+%!   annotation ('textarrow', [0.3 0.5], [y y], ...
+%!               'string', ['"' styles{jj} '"'], 'fontsize', 15, ...
+%!               'headstyle', styles{jj}, 'textcolor', 'b');
+%!   jj = jj + 1;
+%!   if (jj <= ns)
+%!     annotation ('textarrow', [0.7 0.5], [y y], ...
+%!                 'string', ['"' styles{jj} '"'], 'fontsize', 15, ...
+%!                 'headstyle', styles{jj}, 'textcolor', 'b');
+%!   jj = jj + 1;
+%!   end
+%!   y = y - dy;
+%! end
+%! annotation ('line', [0.5 0.5], [dy/2 1-dy/2], 'linestyle', '-.')
+
+%!demo
+%! clf; axes ('visible', 'off');
+%! 
+%! %% Textbox for the title
+%! annotation ('textbox', [0.1 0 0.8 1], 'string', ...
+%!             'Text arrows: text rotation', ...
+%!             'backgroundcolor', [0.7 0.7 0.7], 'fontsize', 20, ...
+%!             'fitboxtotext', 'off', 'verticalalignment', 'top', ...
+%!             'horizontalalignment', 'center');
+%! 
+%! %% Textarrows 
+%! for ii = 1:10
+%!   rot = floor (rand (1) * 360 / 90) * 90;
+%!   annotation ('textarrow', 0.5 + [(0.6 * (rand(1) - .5)) 0], ...
+%!               0.5 + [(0.6 * (rand(1) - .5)) 0], ...
+%!               'string', 'A text', ...
+%!               'headstyle', 'none', 'textrotation', rot);
+%! end
+
+%!demo
+%! clf; axes ('visible', 'off');
+%! 
+%! %% Textbox for the title
+%! annotation ('textbox', [0.1 0 0.8 1], 'string', ...
+%!             'Text arrows: text alignment', ...
+%!             'backgroundcolor', [0.7 0.7 0.7], 'fontsize', 20, ...
+%!             'fitboxtotext', 'off', 'verticalalignment', 'top', ...
+%!             'horizontalalignment', 'center');
+%! 
+%! %% Textarrows
+%! halig = {'right', 'center', 'left'};
+%! ii = 1;
+%! for x = .3:.2:.7
+%!   annotation ('textarrow', [x .5], [.5 .9], ...
+%!               'string', {'Multiple lines', 'text'}, ...
+%!               'headstyle', 'none', 'horizontalalignment', halig{ii});
+%!   ii = ii + 1;
+%! end
+
+%!demo
+%! clf; axes ('visible', 'off');
+%! 
+%! x = 0:0.01:2*pi;
+%! y = sin (x);
+%! plot (x, y)
+%! 
+%! %% Extrema
+%! x0 = [pi/2 3*pi/2];
+%! y0 = [1 -1];
+%! 
+%! %% Convert axes coordinates into normalized coordinates
+%! xl = xlim ();
+%! yl = [-1.2 1.5];
+%! ylim (yl);
+%! x0 = (x0 - xl(1)) / diff(xl);
+%! y0 = (y0 - yl(1)) / diff(yl);
+%! 
+%! pos = get (gca (), 'position');
+%! x0 = x0*pos(3) + pos(1);
+%! y0 = y0*pos(4) + pos(2);
+%! 
+%! 
+%! %% Textarrows
+%! for ii = 1:2
+%!   annotation ('doublearrow', [(x0(ii) - .05) (x0(ii) + .05)], ...
+%!               [y0(ii) y0(ii)], 'head1style', 'vback3', ...
+%!               'head2style', 'vback3', ...
+%!               'head1width', 4, 'head2width', 4)
+%!   h = annotation ('textarrow', [0.5 x0(ii)], [.85 y0(ii)], ...
+%!                   'linestyle', '--', 'headstyle', 'none');
+%! end
+%! set (h, 'string', 'Extrema', 'fontsize', 15) 
+
+## test line properties
+%!test
+%! hf = figure ("visible", "off");
+%! hax = axes ();
+%! unwind_protect
+%!   h = annotation ("line", [0.2 0.7], [0.2 0.7], "linewidth", 2,
+%!                   "linestyle", "--", "color", "r");
+%!   hli = get (h, "children");
+%!   assert (get (hli, "linewidth"), 2);
+%!   assert (get (hli, "linestyle"), "--");
+%!   assert (get (hli, "color"), [1 0 0]);
+%!   assert (gca (), hax);
+%! unwind_protect_cleanup
+%!   close (hf)
+%! end_unwind_protect
+
+## test textarrow properties
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   h = annotation ("textarrow", [0.2 0.7], [0.2 0.7],
+%!                   "string", "Hello!", "fontsize", 20, 
+%!                   "textrotation", 90, "textcolor", "r");
+%!   hte = findobj (h, "-depth", 1, "type", "text");
+%!   assert (get (hte, "string"), "Hello!");
+%!   assert (get (hte, "fontsize"), 20);
+%!   assert (get (hte, "rotation"), 90);
+%!   assert (get (hte, "color"), [1 0 0]);
+%! unwind_protect_cleanup
+%!   close (hf)
+%! end_unwind_protect
+
+## test textbox properties
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   h = annotation ("textbox", [0.2 0.2 0.7 0.3], "string", "Hello!",
+%!                   "horizontalalignment", "left", 
+%!                   "verticalalignment", "bottom",
+%!                   "backgroundcolor", "r");
+%!   hte = findobj (h, "-depth", 1, "type", "text");
+%!   hpa = findobj (h, "-depth", 1, "type", "patch");
+%!   assert (get (hte, "string"), "Hello!");
+%!   assert (get (hte, "verticalalignment"), "bottom");
+%!   assert (get (hte, "horizontalalignment"), "left");
+%!   assert (get (hpa, "facecolor"), [1 0 0]);
+%! unwind_protect_cleanup
+%!   close (hf)
+%! end_unwind_protect
+
+## test units conversion
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   h = annotation ("arrow", [0.1 0.2],  [0.5 0.2]);
+%!   set (h, "units", "inches");
+%!   pos = get (h, "position");
+%!   x = get (h, "x");
+%!   y = get (h, "y");
+%!   set (h, "units", "centimeters");
+%!   assert (get (h, "position"), pos * 2.54, 20*eps);
+%!   assert (get (h, "x"), x * 2.54, 20*eps);
+%!   assert (get (h, "y"), y * 2.54, 20*eps);
+%! unwind_protect_cleanup
+%!   close (hf)
+%! end_unwind_protect
+
+## test annotated figure
+%!test
+%! hf1 = figure ("visible", "off");
+%! hf2 = figure ("visible", "off");
+%! unwind_protect
+%!   h = annotation (hf1, "doublearrow");
+%!   assert (ancestor (h, "figure"), hf1);
+%!   assert (gcf (), hf2);
+%! unwind_protect_cleanup
+%!   close (hf1)
+%!   close (hf2)
+%! end_unwind_protect
+  
+## Test input validation
+%!error <unknown annotation type foo> annotation ("foo")
+%!error annotation ([], "foo")
+%!error annotation ({})
+%!error annotation ("line", [.5 .6])
+%!error <expect 2 elements vectors for X and Y> annotation ("line", 1:3, 1:3)
+%!error <expect 4 elements vector for POS> annotation ("textbox", 1:3)
--- a/scripts/plot/appearance/module.mk	Fri Feb 20 14:56:06 2015 -0800
+++ b/scripts/plot/appearance/module.mk	Fri Feb 20 14:59:43 2015 -0800
@@ -7,6 +7,7 @@
 plot_appearance_FCN_FILES = \
   plot/appearance/__clabel__.m \
   plot/appearance/__getlegenddata__.m \
+  plot/appearance/annotation.m \
   plot/appearance/axis.m \
   plot/appearance/box.m \
   plot/appearance/caxis.m \
--- a/scripts/plot/draw/annotation.m	Fri Feb 20 14:56:06 2015 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1241 +0,0 @@
-## Copyright (C) 2015 Pantxo Diribarne
-## 
-##   This program 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.
-##   
-##   This program 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} {} annotation (@var{type})
-## @deftypefnx {Function File} {} annotation ("line", @var{x}, @var{y})
-## @deftypefnx {Function File} {} annotation ("arrow", @var{x}, @var{y})
-## @deftypefnx {Function File} {} annotation ("doublearrow", @var{x}, @var{y})
-## @deftypefnx {Function File} {} annotation ("textarrow", @var{x}, @var{y})
-## @deftypefnx {Function File} {} annotation ("textbox", @var{pos})
-## @deftypefnx {Function File} {} annotation ("rectangle", @var{pos})
-## @deftypefnx {Function File} {} annotation ("ellipse", @var{pos})
-## @deftypefnx {Function File} {} annotation (@var{hf}, @dots{})
-## @deftypefnx {Function File} {} annotation (@dots{}, @var{prop}, @var{val})
-## @deftypefnx {Function File} {@var{h} =} annotation (@dots{})
-## Draw annotations to emphasize parts of a figure.
-##
-## You may build a default annotation specifying only the type
-## @var{type} of the annotation.  Otherwise you can set the position of
-## the annotation using either @var{x} and @var{y} coordinates for
-## line-based annotations or a position vector @var{pos} for others.
-## In any case, coordinates are interpreted using the @qcode{"units"}
-## property of the annotation objects: the default is
-## @qcode{"normalized"}, which means the lower left hand corner of the
-## figure has coordinates @samp{[0 0]} and the upper right hand corner
-## @samp{[1 1]}. 
-##
-## The figure on which the annotations should be drawn may be 
-## specified by providing its graphics handle @var{hf} before any
-## other argument.  Otherwise annotations are drawn on the current
-## figure. 
-## 
-## Further arguments can be provided in the form of
-## @var{prop}/@var{val} pairs to customize the annotation appearance
-## and the units in which coordinates are interpreted.  The annotation
-## can also be customized afterward using its graphics handle 
-## @var{h} and @code{set} function.
-## 
-## All annotation objects share two properties:
-##
-## @itemize
-## @item
-##  @qcode{"units"}: the units in which coordinates are interpreted.
-##  Its value may be one of @qcode{"centimeters"} |
-##  @qcode{"characters"} | @qcode{"inches"} | @qcode{"@{normalized@}"}
-##  | @qcode{"pixels"} | @qcode{"points"}.
-##
-## @item
-##  @qcode{"position"}: a four elements vector [x0 y0 width height]
-##  specifying the coordinates (x0,y0) of the origin of the annotation
-##  object, its width and its height.  The width and height may be
-##  negative, depending on the orientation of the object.
-##
-## @end itemize
-##
-## Valid annotation types and their specific properties are described
-## below: 
-##
-## @table @asis
-## @item @qcode{"line"}
-##  Constructs a line.  @var{x} and @var{y} must be two
-##  elements vectors specifying the x and y coordinates of the two
-##  ends of the line.
-##
-##  The line can be customized using @qcode{"linewidth"},
-##  @qcode{"linestyle"} and @qcode{"color"} properties the same way
-##  as with @code{line} objects.
-##
-## @item  @qcode{"arrow"}
-##   Construct an arrow.  The second point in vectors @var{x} and
-##   @var{y} specifies the arrowhead coordinates.
-##
-##  Besides line properties, the arrowhead can be customized using
-##  @qcode{"headlength"}, @qcode{"headwidth"} and @qcode{"headstyle"}
-##  properties.  Supported values for @qcode{"headstyle"} property are:
-##  [@qcode{"diamond"} | @qcode{"ellipse"} | @qcode{"plain"} |
-##  @qcode{"rectangle"} | @qcode{"vback1"} | @qcode{"@{vback2@}"} | 
-##  @qcode{"vback3"}] 
-## 
-## @item  @qcode{"doublearrow"}
-##   Construct a double arrow.  Vectors @var{x} and @var{y} specify the
-##   arrowheads coordinates.
-##
-##  The line and the arrowhead can be customized as for arrow
-##  annotations but some property names are duplicated:
-##  @qcode{"head1length"}/@qcode{"head2length"},
-##  @qcode{"head1width"}/@qcode{"head2width"}, etc.  The index 1 marks
-##  the properties of the arrowhead at the first point in @var{x} and
-##  @var{y} coordinates. 
-##
-## @item  @qcode{"textarrow"}
-##  Construct an arrow with a text label at the opposite of the
-##  arrowhead.
-##
-##  The line and the arrowhead can be customized as for arrow
-##  annotations, and the text can be customized using the same
-##  properties as @code{text} graphics objects.  Note however
-##  that some text property names are prefixed with "text" to
-##  distinguish arrow and text properties:
-##  @qcode{"textbackgroundcolor"}, @qcode{"textcolor"},
-##  @qcode{"textedgecolor"}, @qcode{"textlinewidth"},
-##  @qcode{"textmargin"}, @qcode{"textrotation"}.
-##
-## @item  @qcode{"textbox"}
-##  Construct a box with a text inside.  @var{pos} specifies the
-##  @qcode{"position"} property of the annotation.
-##
-##  You may use @qcode{"backgroundcolor"}, @qcode{"edgecolor"},
-##  @qcode{"linestyle"} , @qcode{"linewidth"} properties to customize
-##  the box background color and edges appearance.  A limited set of
-##  @code{text} objects properties is also available: besides
-##  @qcode{"font@dots{}"} properties, you may also use
-##  @qcode{"horizontalalignment"} and @qcode{"verticalalignment"} to
-##  move the text inside the box.
-##
-##  Finally the  @qcode{"fitboxtotext"} property controls the actual
-##  extent of the box.  If @qcode{"on"} (the default) the
-##  box limits are fitted to the text extent.
-##
-## @item  @qcode{"rectangle"}
-##  Construct a rectangle.  @var{pos} specifies the
-##  @qcode{"position"} property of the annotation. 
-##
-##  You may use @qcode{"facecolor"}, @qcode{"color"},
-##  @qcode{"linestyle"} and @qcode{"linewidth"} properties to customize
-##  the rectangle background color and edges appearance.
-##
-## @item  @qcode{"ellipse"}
-##  Construct an ellipse.  @var{pos} specifies the
-##  @qcode{"position"} property of the annotation. 
-##
-##  See @qcode{"rectangle"} annotations for customization. 
-## @end table
-## 
-## @seealso{xlabel, title}
-## @end deftypefn
-
-function varargout = annotation (varargin)
-
-  objtype = "";
-  hf = [];
-  lims = [];
-  x = y = [];
-  opts = {};
-  
-  nargin = numel (varargin);
-  if (nargin == 0)
-    print_usage ();
-  endif
-  
-  
-  ## Parent figure
-  if (isfigure (varargin{1}))
-    hf = varargin{1};
-    varargin = varargin(2:end);
-    nargin --;
-  endif
-  
-  ## Annotation type
-  types = {"line", "arrow", "doublearrow", "textarrow", ...
-           "textbox", "ellipse", "rectangle"};
-  if (ischar (varargin{1}))
-    objtype = varargin{1};
-    varargin(1) = [];
-    nargin --;
-  else
-    print_usage ();
-  endif
-  
-  switch objtype
-    case types(1:4)
-         
-      if (nargin == 0)
-        lims  = [];
-      elseif (nargin >= 2)
-        x = varargin{1};
-        y = varargin{2};
-        varargin(1:2) = [];
-      
-        if (isnumeric (x) && isnumeric (y) && 
-            length (x) == 2 && length (y) == 2)
-          lims = [x(1) y(1) diff(x) diff(y)];
-        else 
-          error ("annotation: expect 2 elements vectors for X and Y");
-        endif
-      else
-        print_usage ();
-      endif
-    case types(5:end)
-      if (nargin == 0)
-        lims  = [];
-      else
-        lims = varargin{1};
-        varargin(1) = [];
-        
-        if (! isvector (lims) || length (lims) != 4)
-          error ("annotation: expect 4 elements vector for POS")
-        endif
-      endif
-    otherwise
-      error ("annotation: unknown annotation type %s", objtype)
-  endswitch
-
-  ## options
-  opts = varargin;
-  nopts = numel (opts);
-  if (! isempty (opts))
-    if (fix (nopts/2) != nopts/2 ||
-        !all (cellfun (@ischar, opts(1:2:end))))
-      warning ("annotation: couldn't parse PROP/VAL pairs, skipping");
-      opts = {};
-    endif
-  endif
-  
-  ## create annotation
-  showhidden = get (0, "showhiddenhandles");
-  set (0, "showhiddenhandles", "on");
-
-  unwind_protect
-    if (isempty (hf))
-      hf = gcf ();
-    endif
-    
-    ## Axes
-    hca = get (hf, "currentaxes");
-
-    hax = findall (hf, "-depth", 1, "tag", "scribeoverlay");
-    if (isempty (hax))
-      hax = buildoverlay (hf);
-    endif
-
-    ## Build annotation
-    htmp = buildannot (hax, objtype, lims);
-
-    ## Set user defined properties
-    if (!isempty (opts))
-      set (htmp, opts{:});
-    endif
-    
-  unwind_protect_cleanup
-    set (0, "showhiddenhandles", showhidden);
-    set (hf, "currentaxes", hca);
-  end_unwind_protect
-  
-  if (nargout != 0)
-    varargout{1} = htmp;
-  endif
-
-endfunction
-
-function hax = buildoverlay (hf)
-
-  hax = axes ("parent", hf, "position", [0 0 1 1], ...
-              "visible", "off","tag", "scribeoverlay", ...
-              "xlim", [0 1], "ylim", [0 1], ...
-              "handlevisibility", "off");
-  
-  ## hidden property to store figure size in absolute (points)
-  ## coordinates 
-  addproperty ("figsize_points", hax, "axesxmtick", []);
-  update_figsize_points (hf, {}, hax);
-
-  
-  listener = {@update_figsize_points, hax};
-  addlistener (hf, "position", listener);
-    
-  delfcn = @() dellistener (hf, "position", listener);
-  set (hax, "deletefcn", delfcn);
-  
-endfunction
-
-function update_figsize_points (hf, dummy, hax)
-
-  persistent recursive = false;
-  if (! recursive)
-    recursive = true;
-    units = get (hf, "units");
-    set (hf, "units", "points");
-    pos = get (hf, "position");
-    set (hf, "units", units);
-    
-    set (hax, "figsize_points", pos(3:4));
-    recursive = false;
-  endif
-  
-endfunction
-  
-function h = buildannot (hax, objtype, pos)
-
-  ## Base hggroup
-  h = hggroup ("parent", hax);
-  
-  ## Add common properties
-  if (strcmp (graphics_toolkit (), "gnuplot"))
-    ## FIXME: this is a workaround for bug #39394 (gnuplot toolkit)
-    defprops = {"position", "axesposition", [0.3 0.3 0.1 0.1], ...
-                "units", "textunits", "data"}; 
-  else
-    defprops = {"position", "axesposition", [0.3 0.3 0.1 0.1], ...
-                "units", "axesunits", "normalized"};
-  endif
-  addbaseprops (h, defprops);
-  setappdata (h, "__former_units__", "normalized");
-
-  ## Common updaters
-  listener = {@update_position, h, true};
-  
-  addlistener (hax, "figsize_points", listener);
-  
-  delfcn = @() dellistener (hax, "figsize_points", listener);
-  set (h, "deletefcn", delfcn);
-  
-  addlistener (h, "units", {@update_position, h});
-  
-  ## Now work with normalized coordinates
-  if (! isempty (pos))
-    set (h, "position", pos);
-  endif
-  pos = getnormpos (h);
-  
-  ## Build annotation object and its specific properties/updaters
-  switch objtype
-    case {"line", "arrow", "doublearrow", "textarrow"}
-      ## Add properties
-      proptable = lineprops ();
-      if (strcmp (objtype, "arrow"))
-        proptable = [proptable arrowprops()];
-      elseif (strcmp (objtype, "doublearrow"))
-        proptable = [proptable dblarrowprops()];
-      elseif (strcmp (objtype, "textarrow"))
-        proptable = [proptable arrowprops()];
-        proptable = [proptable textprops()];
-      endif
-
-      addbaseprops (h, proptable);
-      
-      ## create line
-      hli = line ([pos(1); (pos(1) + pos(3))],
-                  [pos(2); (pos(2) + pos(4))],
-                  "parent", h, "color", get (h, "color"),
-                  "linestyle", get (h, "linestyle"),
-                  "linewidth", get (h, "linewidth"));
-      
-      ## create patch(s) and text
-      if (strcmp (objtype, "arrow"))
-        [x, y] = arrowcoordinates (h);
-        hpa = patch (x, y, get (h, "color"), "parent", h,
-                    "edgecolor",  get (h, "color"));
-        update_arrow (h, {}, "position", hpa);
-      elseif (strcmp (objtype, "doublearrow"))
-        [x, y] = arrowcoordinates (h, 1);
-        hpa = patch (x, y, get (h, "color"), "parent", h,
-                    "edgecolor",  get (h, "color"));
-        
-        [x, y] = arrowcoordinates (h, 2);
-        hpa(2) = patch (x, y, get (h, "color"), "parent", h,
-                    "edgecolor",  get (h, "color"));
-        
-        update_arrow (h, {}, "position", hpa);
-      elseif (strcmp (objtype, "textarrow"))
-        [x, y] = arrowcoordinates (h);
-        hpa = patch (x, y, get (h, "color"), "parent", h,
-                    "edgecolor",  get (h, "color"));
-        update_arrow (h, {}, "position", hpa);
-        
-        hte = text (get (h, "position")(1), ...
-                   get (h, "position")(2), ...
-                   get (h, "string"), "parent", h, ...
-                   "color", get (h, "color"));
-        propnames = textprops ("names");
-        for ii = 1:numel (propnames)
-          update_text (h, {}, propnames{ii}, hte);
-        endfor
-        update_text (h, {}, "position", hte);
-      endif
-      
-      ## updaters
-      addlistener (h, "color", {@update_line, "color", hli});
-      addlistener (h, "linestyle", {@update_line, "linestyle", hli});
-      addlistener (h, "linewidth", {@update_line, "linewidth", hli});
-      addlistener (h, "x", {@update_line, "x", hli});
-      addlistener (h, "y", {@update_line, "y", hli});
-      addlistener (h, "position", {@update_line, "position", hli});
-
-      if (strcmp (objtype, "arrow"))
-        addlistener (h, "position", {@update_arrow, "position", hpa});
-        addlistener (h, "headwidth", {@update_arrow, "position", hpa});
-        addlistener (h, "headstyle", {@update_arrow, "position", hpa});
-        addlistener (h, "headlength", {@update_arrow, "position", hpa});
-        addlistener (h, "color", {@update_arrow, "color", hpa});
-      elseif (strcmp (objtype, "doublearrow"))
-        addlistener (h, "position", {@update_arrow, "position", hpa});
-        addlistener (h, "head1width", 
-                     {@update_arrow, "position", [hpa(1) 0]});
-        addlistener (h, "head2width",
-                     {@update_arrow, "position", [0 hpa(2)]});
-        addlistener (h, "head1style",
-                     {@update_arrow, "position", [hpa(1) 0]});
-        addlistener (h, "head2style",
-                     {@update_arrow, "position", [0 hpa(2)]});
-        addlistener (h, "head1length",
-                     {@update_arrow, "position", [hpa(1) 0]});
-        addlistener (h, "head2length",
-                     {@update_arrow, "position", [0 hpa(2)]});
-        addlistener (h, "color",
-                     {@update_arrow, "color", hpa});
-      elseif (strcmp (objtype, "textarrow"))
-        addlistener (h, "position", {@update_arrow, "position", hpa});
-        addlistener (h, "headwidth", {@update_arrow, "position", hpa});
-        addlistener (h, "headstyle", {@update_arrow, "position", hpa});
-        addlistener (h, "headlength", {@update_arrow, "position", hpa});
-        addlistener (h, "color", {@update_arrow, "color", hpa});
-        propnames = textprops ("names");
-        for ii = 1:numel (propnames)
-          addlistener (h, propnames{ii},
-                       {@update_text, propnames{ii}, hte});
-          if (any (strcmp (propnames{ii},
-                           {"fontangle", "fontname", ...
-                            "fontsize", "fontweight", ...
-                            "horizontalalignment", "string", ...
-                            "textmargin", "textrotation", ...
-                            "verticalalignment"})))
-            addlistener (h, propnames{ii}, ...
-                         {@update_text, "position", hte});
-          endif
-        endfor
-        addlistener (h, "position", {@update_text, "position", hte});
-        addlistener (h, "color", {@update_text, "color", hte});
-      endif
-      
-    case {"rectangle", "ellipse"}
-         
-      ## Add properties
-      addbaseprops (h, rectprops ());
-      
-      ## Create rectangle/ellipse
-      if (strcmp (objtype, "rectangle"))
-        [x, y] = pos2rect (pos);
-      else
-        [x, y] = pos2ell (pos);
-      endif
-          
-      hr =  patch (x, y, "parent", h);
-      
-      propnames = rectprops ("names");
-      for ii = 1:numel (propnames)
-        update_rect (h, {}, propnames{ii}, hr, objtype);
-      endfor
-
-      ## Updaters
-      addlistener (h, "position", {@update_rect, "position", hr, objtype});
-      for ii = 1:numel (propnames)
-        addlistener (h, propnames{ii},
-                     {@update_rect, propnames{ii}, hr, objtype});
-      endfor
-
-    case "textbox"
-         
-      ## Add properties
-      addbaseprops (h, textboxprops());
-
-      ## Create textbox
-      hpa = patch ("parent", h);
-      hte = text (pos(1), pos(2), get (h, "string"), "parent", h, ...
-                 "color", get (h, "color"));
-      update_textbox (h, {}, "position", [hte hpa]);
-      
-      propnames = textboxprops ("names");
-      for ii = 1:numel (propnames)
-        update_textbox (h, {}, propnames{ii}, [hte hpa]);
-      endfor
-
-      ## Updaters
-      addlistener (h, "position", {@update_textbox, "position", [hte hpa]});
-      for ii = 1:numel (propnames)
-        addlistener (h, propnames{ii},
-                     {@update_textbox, propnames{ii}, [hte hpa]});
-      endfor
-      addlistener (h, "horizontalalignment",
-                   {@update_textbox, "position", [hte hpa]});
-      addlistener (h, "verticalalignment",
-                   {@update_textbox, "position", [hte hpa]});
-      
-  endswitch
-  
-endfunction
-
-function props = lineprops (varargin)
-  props = {"color", "color", [0 0 0], ...
-           "linestyle",  "linelinestyle", "-", ...
-           "linewidth", "linelinewidth", 0.5, ...
-           "x", "linexdata", [0.3 0.4], ...
-           "y", "lineydata", [0.3 0.4]};
-  if (strcmp (varargin, "names"))
-    props = props(1:3:end);
-  endif
-endfunction
-
-function props = arrowprops (varargin)
-  props = {"headlength", "data", 10, ...
-           "headstyle",  "radio", "diamond|ellipse|none|plain|rectangle|vback1|{vback2}|vback3", ...
-           "headwidth", "data", 10};
-  if (strcmp (varargin, "names"))
-    props = props(1:3:end);
-  endif
-endfunction
-
-function props = dblarrowprops (varargin)
-  props = {"head1length", "data", 10, ...
-           "head1style",  "radio", "diamond|ellipse|none|plain|rectangle|vback1|{vback2}|vback3", ...
-           "head1width", "data", 10, ...
-           "head2length", "data", 10, ...
-           "head2style",  "radio", "diamond|ellipse|none|plain|rectangle|vback1|{vback2}|vback3", ...
-           "head2width", "data", 10};
-  if (strcmp (varargin, "names"))
-    props = props(1:3:end);
-  endif
-endfunction
-
-function props = textprops (varargin)
-  props = {"fontangle", "textfontangle", "normal", ...
-           "fontname",  "textfontname", "Helvetica", ...
-           "fontsize", "textfontsize", 10, ...
-           "fontunits", "textfontunits", "points", ...
-           "fontweight",  "textfontweight", "normal", ...
-           "horizontalalignment", "texthorizontalalignment", "left", ...
-           "interpreter", "textinterpreter", "tex", ...
-           "string", "textstring", "", ...
-           "textbackgroundcolor", "textbackgroundcolor", "none", ...
-           "textcolor", "textcolor", "k", ...
-           "textedgecolor", "textedgecolor", "none", ...
-           "textlinewidth", "textlinewidth",0.5, ...
-           "textmargin", "textmargin", 5, ...
-           "textrotation", "textrotation", 0, ...
-           "verticalalignment", "textverticalalignment", "middle"};
-  if (strcmp (varargin, "names"))
-    props = props(1:3:end);
-  endif
-endfunction
-
-function props = textboxprops (varargin)
-  props = {"backgroundcolor", "patchfacecolor", "none", ...
-           "color", "textcolor", [0 0 0], ...
-           "edgecolor", "patchedgecolor", [0 0 0], ...
-           "facealpha", "patchfacealpha", 1, ...
-           "fontangle", "textfontangle", "normal", ...
-           "fontname",  "textfontname", "Helvetica", ...
-           "fontsize", "textfontsize", 10, ...
-           "fontunits", "textfontunits", "points", ...
-           "fontweight",  "textfontweight", "normal", ...
-           "horizontalalignment", "texthorizontalalignment", "left", ...
-           "interpreter", "textinterpreter", "tex", ...
-           "linestyle",  "linelinestyle", "-", ...
-           "linewidth", "linelinewidth", 0.5, ...
-           "string", "textstring", "", ...
-           "fitboxtotext", "radio","{on}|off", ...
-           "margin", "data", 5, ...
-           "verticalalignment", "textverticalalignment",  "middle"};
-  if (strcmp (varargin, "names"))
-    props = props(1:3:end);
-  endif
-endfunction
-
-function props = rectprops (varargin)
-  props = {"edgecolor", "patchedgecolor", "k", ...
-           "facealpha", "patchfacealpha", 1, ...
-           "facecolor", "patchfacecolor", "none", ...
-           "linestyle", "patchlinestyle", "-", ...
-           "linewidth", "patchlinewidth", 0.5};
-  if (strcmp (varargin, "names"))
-    props = props(1:3:end);
-  endif
-endfunction
-
-function addbaseprops (h, proptable)
-  cellfun (@(pname, ptype, parg) addproperty (pname, h, ptype, parg),
-           proptable(1:3:end), proptable(2:3:end), proptable(3:3:end));
-endfunction
-
-function update_position (h1, dummy, h, force = false)
-  if (! force)
-    pos = convertposition (h, getappdata (h, "__former_units__"),
-                           get (h, "units"));
-    setappdata (h, "__former_units__", get (h, "units"));
-    set (h, "position", pos);
-  else
-    ## FIXME: Inefficient trick to force all objects to be redrawn
-    set (h, "position", [0 0 .5 .5],
-         "position", get (h, "position"));
-  endif      
-endfunction
-
-
-function update_line (h, dummy, prop, hli)
-  persistent recursive = false;
-  if (! recursive)
-    switch prop
-      case "color"
-        set (hli, "color", get (h, "color"));
-      case "linestyle"
-        set (hli, "linestyle", get (h, "linestyle"));
-      case "linewidth"
-        set (hli, "linewidth", get (h, "linewidth"));
-      case "x"
-        ## Update position
-        x = get (h, "x");
-        pos = get (h, "position");
-        pos(1) = x(1);
-        pos(3) = diff (x);
-        recursive = true;
-        set (h, "position", pos);
-        recursive = false;
-
-        ## Draw in normalized coordinates
-        pos = getnormpos (h);
-        x = [pos(1) (pos(1) + pos(3))];
-        set (hli, "xdata", x);
-        
-      case "y"
-       ## Update position
-        y = get (h, "y");
-        pos = get (h, "position");
-        pos(2) = y(1);
-        pos(4) = diff (y);
-        recursive = true;
-        set (h, "position", pos);
-        recursive = false;
-
-        ## Draw in normalized coordinates
-        pos = getnormpos (h);
-        y = [pos(2) (pos(2) + pos(4))];
-        set (hli, "ydata", y);
-      case "position"
-        ## Update x and y
-        pos = get (h, "position");
-        x = [pos(1) (pos(1) + pos(3))];
-        y = [pos(2) (pos(2) + pos(4))];
-        
-        recursive = true;
-        set (h, "x", x);
-        set (h, "y", y);
-        recursive = false;
-        
-        ## Draw in normalized coordinates
-        pos = getnormpos (h);
-        x = [pos(1) (pos(1) + pos(3))];
-        y = [pos(2) (pos(2) + pos(4))];
-        set (hli, "xdata", x);
-        set (hli, "ydata", y);
-        
-    endswitch
-  endif
-endfunction
-
-function [x, y] = arrowcoordinates (h, nar = [])
-  warning ("off", "Octave:broadcast", "local")
-  pos = getnormpos (h);
-  ppos = norm2pts (h, pos(3:4).');
-  ang = angle (complex (ppos(1), ppos(2)));
-
-  if (isempty (nar))
-    ln = get (h, "headlength");   # in points
-    wd = get (h, "headwidth");
-    headstyle = get (h, "headstyle");
-    pos = pos(1:2) .+ pos(3:4);
-  elseif (nar == 1)
-    ln = get (h, "head1length"); # in points
-    wd = get (h, "head1width");
-    headstyle = get (h, "head1style");
-    pos = pos(1:2);
-    ang += pi;
-  elseif (nar == 2)
-    ln = get (h, "head2length"); # in points
-    wd = get (h, "head2width");
-    headstyle = get (h, "head2style");
-    pos = pos(1:2) .+ pos(3:4);
-  else
-    error ("annotation: %d, no such arrow number")
-  endif
-
-  switch headstyle
-    case "diamond"
-      x = [0 -ln/2 -ln -ln/2 0];
-      y = [0 -wd/2 0 wd/2 0];
-    case "ellipse"
-      pts = linspace (0, 2*pi, 12);
-      x = ln/2 * (cos (pts) - 1);
-      y = wd/2 * sin (pts);
-    case "rectangle"
-      x = [0 0 -ln -ln 0];
-      y = [wd/2 -wd/2 -wd/2 wd/2 wd/2];
-    case "vback1"
-      x = [0 -ln -0.85*ln -ln 0];
-      y = [0 wd/2 0 -wd/2 0];
-    case "vback2"
-      x = [0 -ln -0.65*ln -ln 0];
-      y = [0 wd/2 0 -wd/2 0];
-    case "vback3"
-      x = [0 -ln -0.2*ln -ln 0];
-      y = [0 wd/2 0 -wd/2 0];
-    case "plain"
-      x = [0 -ln -ln -ln 0];
-      y = [0 wd/2 0 -wd/2 0];
-    case "none"
-      x = [0 0 0];
-      y = [0 0 0];
-    otherwise
-      error ("annotation: \"%s\" headstyle not implemented", headstyle)
-  endswitch
-
-  R = [cos(ang) -sin(ang);
-       sin(ang) cos(ang)];
-  XY = R * [x; y];
-  XY = pts2norm (h, XY);
-  XY = pos(1:2).' .+ XY;
-  
-  x = XY(1,:).';
-  y = XY(2,:).';
-endfunction
-
-function update_arrow (h, dummy, prop, hpa = [])
-  persistent recursive = false;
-  
-  nar = [];
-  for ii = 1:numel (hpa)
-    if (numel (hpa) == 2)
-      nar = ii;
-    endif
-    if (hpa(ii))
-      switch prop
-        case "position"
-          [x, y] = arrowcoordinates (h, nar);
-          set (hpa(ii), "xdata", x, "ydata", y);
-        case "color"
-          set (hpa(ii), "facecolor", get (h, "color"));
-          set (hpa(ii), "edgecolor", get (h, "color"));
-      endswitch
-    endif
-  endfor
-endfunction
-
-function update_text (h, dummy, prop, hte)
-  persistent recursive = false;
-
-  if (! recursive)
-    switch prop
-      case "position"
-        if (isempty (get (h, "string")))
-          return
-        endif
-        
-        pos = getnormpos (h);
-        
-        set (hte, "position", [textcoordinates(hte, pos) 0]);
-        
-      otherwise
-        if (strncmp (prop, "text", 4))
-          set (hte, prop(5:end), get (h, prop));
-        else
-          set (hte, prop, get (h, prop));
-        endif
-    endswitch
-  endif
-endfunction
-
-function update_textbox (h, dummy, prop, htb)
-  persistent recursive = false;
-
-  hte = htb(1);
-  hpa = htb(2);
-  
-  if (! recursive)
-    switch prop
-      case {"edgecolor", "facealpha",
-            "linestyle", "linewidth"}
-        set (hpa, prop, get (h, prop));
-      case {"backgroundcolor"}
-        set (hpa, "facecolor", get (h, prop));
-      otherwise
-        if (! any (strcmp (prop, {"fitboxtotext", "position"})))
-          set (hte, prop, get (h, prop));
-        endif
-           
-        pos = getnormpos (h);
-        
-        if (strcmp (get (h, "fitboxtotext"), "on"))
-          pos(3:4) = get (hte, "extent")(3:4);
-        endif
-        
-        [x, y] = pos2rect (pos);
-        set (hpa, "xdata", x', "ydata", y');
-          
-        switch (get (h, "horizontalalignment"))
-          case "left"
-            x = x(1);
-          case "center"
-            x = mean (x(1:2));
-          case "right"
-            x = x(2);
-        endswitch
-
-        switch (get (h, "verticalalignment"))
-          case {"top", "cap"}
-            y = y(3);
-          case "middle"
-            y = mean (y(2:3));
-          case {"bottom", "baseline"}
-            y = y(2);
-        endswitch
-        set (hte, "position", [x y 0]);
-    endswitch
-  endif
-  
-endfunction
-
-function XY = textcoordinates (hte, pos)
-  warning ("off", "Octave:broadcast", "local")
-
-  ## Get the "tight" extent of the text object in points units
-  textpos = get(hte, "position");
-  rot = get (hte, "rotation");
-  units = get (hte, "units");
-
-  set (hte, "rotation", 0, "units", "points", "position", [0 0 0]);
-  ext = get (hte, "extent");
-  set (hte, "rotation", rot, "units", units, "position", textpos);
-  
-  ## Find which one of the 8 following points we should align the
-  ## arrow with
-  ##  8-----7-----6
-  ##  1  text box 5
-  ##  2-----3-----4
-  
-  ## FIXME: Matlab's horizontal/verticalalignment properties are
-  ## interpreted differently: horizontalalignment is passed to the 
-  ## underlying text object whereas the verticalalignement controls
-  ## the vertical alignment of the arrow.
-  
-  ang = angle (complex (pos(3), pos(4)));
-  rot = rot / 180 * pi;
-
-  [~, pt] = min (abs ((-pi:pi/4:pi) - ang)); 
-  pt -= floor (rot / (pi/4));
-  if (pt <= 0)
-    pt = rem (pt, 8) + 8;
-  elseif (pt > 8)
-    pt = rem (pt, 8);
-  endif
-  
-  ## Compute the text actual "position" property
-  dx = ext(3)/2;
-  dy = ext(4)/2;
-  XY = [-dx -dx 0 dx dx dx 0 -dx;
-        0 -dy -dy -dy 0 dy dy dy];
-  
-  switch get (hte, "horizontalalignment")
-    case "left"
-      XY(1,:) += dx;
-    case "right"
-      XY(1,:) -= dx;
-  endswitch
-  
-  switch get (hte, "verticalalignment")
-    case {"baseline", "bottom"}
-      XY(2,:) += dy;
-    case {"cap", "top"}
-      XY(2,:) -= dy;
-  endswitch
-  
-  R = [cos(rot) -sin(rot);
-       sin(rot) cos(rot)];
-  XY = R * XY;
-  XY = pts2norm (get (hte, "parent"), XY);
-  XY = pos(1:2) .- XY(:,pt).';
-  
-endfunction
-
-function nXY = pts2norm (h, pXY)
-  sz = get (get (h,"parent"), "figsize_points");
-  
-  nXY(1,:) = pXY(1,:) ./ sz(1);
-  nXY(2,:) = pXY(2,:) ./ sz(2);
-endfunction
-
-function pXY = norm2pts (h, nXY)
-  sz = get (get (h,"parent"), "figsize_points");
-  
-  pXY(1,:) = nXY(1,:) .* sz(1);
-  pXY(2,:) = nXY(2,:) .* sz(2);
-endfunction
-
-function pos = convertposition (h, from, to)
-  ## FIXME: handle "characters" units  
-  pos = get (h, "position");
-  
-  ## First convert to normalized coordinates
-  sz = get (get (h,"parent"), "figsize_points");
-  switch from
-    case "centimeters"
-      pos /= 2.54;
-      pos *= 72;
-      pos(1:2:end) /= sz(1);
-      pos(2:2:end) /= sz(2);
-    case "inches"
-      pos *= 72;
-      pos(1:2:end) /= sz(1);
-      pos(2:2:end) /= sz(2);
-    case "pixels"
-      pos /= get (0, "screenpixelsperinch");
-      pos *= 72;
-      pos(1:2:end) /= sz(1);
-      pos(2:2:end) /= sz(2);
-  endswitch
-
-  ## Then convert to requested coordinates
-  switch to
-    case "centimeters"
-      sz /= 72;
-      sz *= 2.54;
-      pos(1:2:end) *= sz(1);
-      pos(2:2:end) *= sz(2);
-    case "inches"
-      sz /= 72;
-      pos(1:2:end) *= sz(1);
-      pos(2:2:end) *= sz(2);
-    case "pixels"
-      sz /= 72;
-      sz *= get (0, "screenpixelsperinch"); 
-      pos(1:2:end) *= sz(1);
-      pos(2:2:end) *= sz(2);
-  endswitch
-
-endfunction
-
-function pos = getnormpos (h)
-  units = get (h, "units");
-  pos = convertposition (h, units, "normalized");
-endfunction
-
-function [x, y] = pos2rect (pos)
-  x = [pos(1) pos(1)+pos(3) pos(1)+pos(3) pos(1)];
-  y = [pos(2) pos(2) pos(2)+pos(4) pos(2)+pos(4)];
-endfunction
-
-function [x, y] = pos2ell (pos)
-  a = pos(3)/2;
-  b = pos(4)/2;
-
-  ## Arbitrarily use 100 points 
-  ## when it is spread over
-  ang = linspace (0, 2*pi, 100);
-  
-  x = a * cos (ang);
-  y = b * sin (ang);
-
-  x += pos(1) + a;
-  y += pos(2) + b;
-endfunction
-
-function update_rect (h, dummy, prop, hre, typ)
-  persistent recursive = false;
-  
-  if (! recursive)
-    switch prop
-      case "position"
-        pos = getnormpos (h);
-        if (strcmp (typ, "rectangle"))
-          [x, y] = pos2rect (pos);
-        else
-          [x, y] = pos2ell (pos);
-        endif
-        
-        set (hre, "xdata", x, "ydata", y);
-      otherwise
-        set (hre, prop, get (h, prop));
-    endswitch
-  endif
-endfunction
-
-
-## FIXME: the additionnal regular axes is necessary for fltk to draw the
-##        annotation axes.
-%!demo
-%! clf; axes ('visible', 'off');
-%! annotation ('ellipse', [.2 .2 .6 .6], 'linewidth', 4)
-%! ang = pi/2:-pi/2:-pi;
-%! lab = {'N', 'W', 'S', 'E'};
-%! x0 = 0.5;
-%! y0 = 0.5;
-%! r = 0.3;
-%! for ii = 1:4
-%!   x = r * cos (ang(ii)) + x0;
-%!   y = r * sin (ang(ii)) + y0;
-%!   annotation ('textarrow', [x x0], [y y0], ...
-%!               'string', lab{ii},  'fontsize', 20);
-%! end
-%! 
-%! h = annotation ('doublearrow', [x0 x0], [y0-r y0+r], ...
-%!                 'head1style', 'diamond', 'head1length', 60, ...
-%!                 'head2style', 'diamond', 'head2length', 60);
-
-%!demo
-%! clf; axes ('visible', 'off');
-%! plot (1:10);
-%! xlabel ('X-LABEL')
-%! ylabel ('LARGE Y-LABEL', 'fontsize', 20)
-%! title ('FIGURE LAYOUT', 'fontsize', 24)
-%! 
-%! ti = get (gca, 'tightinset');
-%! pos = get (gca, 'position');
-%! pos(1:2) = pos(1:2) - ti(1:2);
-%! pos(3) = pos(3) + ti (1) + ti (3);
-%! pos(4) = pos(4) + ti (2) + ti (4);
-%! 
-%! ht = annotation ('textbox', pos, 'string', ' Position + tighinset', ...
-%!                  'fitboxtotext', 'off', 'linestyle', '--', ...
-%!                  'edgecolor', 'g', 'linewidth', 3, 'color', 'g', ...
-%!                  'verticalalignment', 'bottom', 'fontsize', 15);
-%! 
-%! ho = annotation ('textbox', get (gca, 'outerposition'), ...
-%!                  'string', ' Outerposition','fitboxtotext', 'off', ...
-%!                  'linestyle', '--', 'edgecolor', 'r', ...
-%!                  'linewidth', 3, 'color', 'r', ...
-%!                  'verticalalignment', 'bottom', 'fontsize', 15);
-%! 
-%! hi = annotation ('textbox', get (gca, 'position'), ...
-%!                  'string', ' Position','fitboxtotext', 'off', ...
-%!                  'linestyle', '--', 'edgecolor', 'b', ...
-%!                  'linewidth', 3, 'color', 'b', ...
-%!                  'verticalalignment', 'bottom', 'fontsize', 15);
-
-%!demo
-%! clf; axes ('visible', 'off');
-%! h = annotation ('arrow');
-%! 
-%! %% Get allowed headstyles
-%! styles = set (h, 'headstyle');
-%! delete (h)
-%! 
-%! %% Textbox for the title
-%! annotation ('textbox', [0.1 0 0.8 1], 'string', ...
-%!             '"headstyle" property:', ...
-%!             'backgroundcolor', [0.7 0.7 0.7], 'fontsize', 20, ...
-%!             'fitboxtotext', 'off', 'verticalalignment', 'top', ...
-%!             'horizontalalignment', 'center');
-%! 
-%! %% Textarrows 
-%! ns = numel (styles);
-%! nrows = ceil (ns/2);
-%! dy = 1/nrows;
-%! y = 1 - dy/2;
-%! 
-%! jj = 1;
-%! for ii = 1:nrows
-%!   annotation ('textarrow', [0.3 0.5], [y y], ...
-%!               'string', ['"' styles{jj} '"'], 'fontsize', 15, ...
-%!               'headstyle', styles{jj}, 'textcolor', 'b');
-%!   jj = jj + 1;
-%!   if (jj <= ns)
-%!     annotation ('textarrow', [0.7 0.5], [y y], ...
-%!                 'string', ['"' styles{jj} '"'], 'fontsize', 15, ...
-%!                 'headstyle', styles{jj}, 'textcolor', 'b');
-%!   jj = jj + 1;
-%!   end
-%!   y = y - dy;
-%! end
-%! annotation ('line', [0.5 0.5], [dy/2 1-dy/2], 'linestyle', '-.')
-
-%!demo
-%! clf; axes ('visible', 'off');
-%! 
-%! %% Textbox for the title
-%! annotation ('textbox', [0.1 0 0.8 1], 'string', ...
-%!             'Text arrows: text rotation', ...
-%!             'backgroundcolor', [0.7 0.7 0.7], 'fontsize', 20, ...
-%!             'fitboxtotext', 'off', 'verticalalignment', 'top', ...
-%!             'horizontalalignment', 'center');
-%! 
-%! %% Textarrows 
-%! for ii = 1:10
-%!   rot = floor (rand (1) * 360 / 90) * 90;
-%!   annotation ('textarrow', 0.5 + [(0.6 * (rand(1) - .5)) 0], ...
-%!               0.5 + [(0.6 * (rand(1) - .5)) 0], ...
-%!               'string', 'A text', ...
-%!               'headstyle', 'none', 'textrotation', rot);
-%! end
-
-%!demo
-%! clf; axes ('visible', 'off');
-%! 
-%! %% Textbox for the title
-%! annotation ('textbox', [0.1 0 0.8 1], 'string', ...
-%!             'Text arrows: text alignment', ...
-%!             'backgroundcolor', [0.7 0.7 0.7], 'fontsize', 20, ...
-%!             'fitboxtotext', 'off', 'verticalalignment', 'top', ...
-%!             'horizontalalignment', 'center');
-%! 
-%! %% Textarrows
-%! halig = {'right', 'center', 'left'};
-%! ii = 1;
-%! for x = .3:.2:.7
-%!   annotation ('textarrow', [x .5], [.5 .9], ...
-%!               'string', {'Multiple lines', 'text'}, ...
-%!               'headstyle', 'none', 'horizontalalignment', halig{ii});
-%!   ii = ii + 1;
-%! end
-
-%!demo
-%! clf; axes ('visible', 'off');
-%! 
-%! x = 0:0.01:2*pi;
-%! y = sin (x);
-%! plot (x, y)
-%! 
-%! %% Extrema
-%! x0 = [pi/2 3*pi/2];
-%! y0 = [1 -1];
-%! 
-%! %% Convert axes coordinates into normalized coordinates
-%! xl = xlim ();
-%! yl = [-1.2 1.5];
-%! ylim (yl);
-%! x0 = (x0 - xl(1)) / diff(xl);
-%! y0 = (y0 - yl(1)) / diff(yl);
-%! 
-%! pos = get (gca (), 'position');
-%! x0 = x0*pos(3) + pos(1);
-%! y0 = y0*pos(4) + pos(2);
-%! 
-%! 
-%! %% Textarrows
-%! for ii = 1:2
-%!   annotation ('doublearrow', [(x0(ii) - .05) (x0(ii) + .05)], ...
-%!               [y0(ii) y0(ii)], 'head1style', 'vback3', ...
-%!               'head2style', 'vback3', ...
-%!               'head1width', 4, 'head2width', 4)
-%!   h = annotation ('textarrow', [0.5 x0(ii)], [.85 y0(ii)], ...
-%!                   'linestyle', '--', 'headstyle', 'none');
-%! end
-%! set (h, 'string', 'Extrema', 'fontsize', 15) 
-
-## test line properties
-%!test
-%! hf = figure ("visible", "off");
-%! hax = axes ();
-%! unwind_protect
-%!   h = annotation ("line", [0.2 0.7], [0.2 0.7], "linewidth", 2,
-%!                   "linestyle", "--", "color", "r");
-%!   hli = get (h, "children");
-%!   assert (get (hli, "linewidth"), 2);
-%!   assert (get (hli, "linestyle"), "--");
-%!   assert (get (hli, "color"), [1 0 0]);
-%!   assert (gca (), hax);
-%! unwind_protect_cleanup
-%!   close (hf)
-%! end_unwind_protect
-
-## test textarrow properties
-%!test
-%! hf = figure ("visible", "off");
-%! unwind_protect
-%!   h = annotation ("textarrow", [0.2 0.7], [0.2 0.7],
-%!                   "string", "Hello!", "fontsize", 20, 
-%!                   "textrotation", 90, "textcolor", "r");
-%!   hte = findobj (h, "-depth", 1, "type", "text");
-%!   assert (get (hte, "string"), "Hello!");
-%!   assert (get (hte, "fontsize"), 20);
-%!   assert (get (hte, "rotation"), 90);
-%!   assert (get (hte, "color"), [1 0 0]);
-%! unwind_protect_cleanup
-%!   close (hf)
-%! end_unwind_protect
-
-## test textbox properties
-%!test
-%! hf = figure ("visible", "off");
-%! unwind_protect
-%!   h = annotation ("textbox", [0.2 0.2 0.7 0.3], "string", "Hello!",
-%!                   "horizontalalignment", "left", 
-%!                   "verticalalignment", "bottom",
-%!                   "backgroundcolor", "r");
-%!   hte = findobj (h, "-depth", 1, "type", "text");
-%!   hpa = findobj (h, "-depth", 1, "type", "patch");
-%!   assert (get (hte, "string"), "Hello!");
-%!   assert (get (hte, "verticalalignment"), "bottom");
-%!   assert (get (hte, "horizontalalignment"), "left");
-%!   assert (get (hpa, "facecolor"), [1 0 0]);
-%! unwind_protect_cleanup
-%!   close (hf)
-%! end_unwind_protect
-
-## test units conversion
-%!test
-%! hf = figure ("visible", "off");
-%! unwind_protect
-%!   h = annotation ("arrow", [0.1 0.2],  [0.5 0.2]);
-%!   set (h, "units", "inches");
-%!   pos = get (h, "position");
-%!   x = get (h, "x");
-%!   y = get (h, "y");
-%!   set (h, "units", "centimeters");
-%!   assert (get (h, "position"), pos * 2.54, 20*eps);
-%!   assert (get (h, "x"), x * 2.54, 20*eps);
-%!   assert (get (h, "y"), y * 2.54, 20*eps);
-%! unwind_protect_cleanup
-%!   close (hf)
-%! end_unwind_protect
-
-## test annotated figure
-%!test
-%! hf1 = figure ("visible", "off");
-%! hf2 = figure ("visible", "off");
-%! unwind_protect
-%!   h = annotation (hf1, "doublearrow");
-%!   assert (ancestor (h, "figure"), hf1);
-%!   assert (gcf (), hf2);
-%! unwind_protect_cleanup
-%!   close (hf1)
-%!   close (hf2)
-%! end_unwind_protect
-  
-## Test input validation
-%!error <unknown annotation type foo> annotation ("foo")
-%!error annotation ([], "foo")
-%!error annotation ({})
-%!error annotation ("line", [.5 .6])
-%!error <expect 2 elements vectors for X and Y> annotation ("line", 1:3, 1:3)
-%!error <expect 4 elements vector for POS> annotation ("textbox", 1:3)
--- a/scripts/plot/draw/module.mk	Fri Feb 20 14:56:06 2015 -0800
+++ b/scripts/plot/draw/module.mk	Fri Feb 20 14:59:43 2015 -0800
@@ -18,7 +18,6 @@
   plot/draw/private/__stem__.m
 
 plot_draw_FCN_FILES = \
-  plot/draw/annotation.m \
   plot/draw/area.m \
   plot/draw/barh.m \
   plot/draw/bar.m \