1########################################################################
2##
3## Copyright (C) 2005-2021 The Octave Project Developers
4##
5## See the file COPYRIGHT.md in the top-level directory of this
6## distribution or <https://octave.org/copyright/>.
7##
8## This file is part of Octave.
9##
10## Octave is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Octave is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Octave; see the file COPYING.  If not, see
22## <https://www.gnu.org/licenses/>.
23##
24########################################################################
25
26## -*- texinfo -*-
27## @deftypefn  {} {} axes ()
28## @deftypefnx {} {} axes (@var{property}, @var{value}, @dots{})
29## @deftypefnx {} {} axes (@var{hpar}, @var{property}, @var{value}, @dots{})
30## @deftypefnx {} {} axes (@var{hax})
31## @deftypefnx {} {@var{h} =} axes (@dots{})
32## Create a Cartesian axes object and return a handle to it, or set the current
33## axes to @var{hax}.
34##
35## Called without any arguments, or with @var{property}/@var{value} pairs,
36## construct a new axes.  The optional argument @var{hpar} is a graphics handle
37## specifying the parent for the new axes and may be a figure, uipanel, or
38## uitab.
39##
40## Called with a single axes handle argument @var{hax}, the function makes
41## @var{hax} the current axes (as returned by @code{gca}).  It also makes
42## the figure which contains @var{hax} the current figure (as returned by
43## @code{gcf}).  Finally, it restacks the parent object's @code{children}
44## property so that the axes @var{hax} appears before all other axes handles
45## in the list.  This causes @var{hax} to be displayed on top of any other axes
46## objects (Z-order stacking).  In addition it restacks any legend or colorbar
47## objects associated with @var{hax} so that they are also visible.
48##
49## Programming Note: The full list of properties is documented at
50## @ref{Axes Properties}.
51## @seealso{gca, set, get}
52## @end deftypefn
53
54function h = axes (varargin)
55
56  htmp = hpar = [];
57  if (nargin > 0)
58    if (ishghandle (varargin{1}(1)))
59      htmp = varargin{1};
60      if (! isscalar (htmp))
61        error ("axes: H must be a scalar handle");
62      endif
63      typ = get (htmp, "type");
64      if (strcmp (typ, "axes") && nargin == 1)
65        cf = ancestor (htmp, "figure");
66        if (__is_handle_visible__ (htmp))
67          set (0, "currentfigure", cf);
68          set (cf, "currentaxes", htmp);
69        endif
70        restack_axes (htmp, get (htmp, "parent"));
71
72        if (nargout > 0)
73          h = htmp;
74        endif
75        return;
76
77      elseif (any (strcmp (typ, {"figure", "uipanel", "uitab"})))
78        hpar = htmp;
79        htmp = [];
80        varargin(1) = [];
81
82      else
83        error ("axes: H must be a handle to an axes or container");
84      endif
85    endif
86  endif
87
88  if (isempty (hpar))
89    ## Find a parent if not given as first argument.
90    idx = find (strcmpi (varargin(1:2:end), "parent"), 1, "first");
91    if (! isempty (idx) && numel (varargin) >= 2*idx)
92      hpar = varargin{2*idx};
93      varargin([2*idx-1, 2*idx]) = [];
94    else
95      hpar = gcf ();
96    endif
97  endif
98
99  ## If there is an annotation axes currently on top of the children stack,
100  ## then it must be placed back on top.
101  ## FIXME: It may be necessary to keep uiXXX objects above all axes objects
102  ##        including even the transparent scribe axes layer.
103  ch = allchild (hpar);
104  h_annotation = ch(strcmp (get (ch, "tag"), "scribeoverlay"));
105
106  ## Create an axes object.
107  htmp = __go_axes__ (hpar, varargin{:});
108  if (__is_handle_visible__ (htmp))
109    set (ancestor (hpar, "figure"), "currentaxes", htmp);
110  endif
111
112  ## Restack annotation object if necessary
113  if (! isempty (h_annotation))
114    ## FIXME: This will put annotation layer first, above even uicontrol
115    ## objects.  May need to keep annotation layer above all axes only.
116    shh = get (0, "ShowHiddenHandles");
117    unwind_protect
118      set (0, "ShowHiddenHandles", "on");
119      ch(ch == h_annotation) = htmp;
120      ch = [h_annotation; ch];
121      set (hpar, "children", ch);
122    unwind_protect_cleanup
123      set (0, "ShowHiddenHandles", shh);
124    end_unwind_protect
125  endif
126
127  if (nargout > 0)
128    h = htmp;
129  endif
130
131endfunction
132
133function restack_axes (h, hpar)
134
135  shh = get (0, "ShowHiddenHandles");
136  unwind_protect
137    set (0, "ShowHiddenHandles", "on");
138    ch = get (hpar, "children");
139    axidx = strcmp (get (ch, "type"), "axes");
140    ## Strip out any annotation axes layer, unless h itself is annotation axes.
141    if (! strcmp (get (h, "tag"), "scribeoverlay"))
142      axidx(axidx) = ! strcmp (get (ch(axidx), "tag"), "scribeoverlay");
143    endif
144    hax = ch(axidx);  # List of axes
145
146    ## Find and stack any legend belonging to this axes above this axes.
147    try
148      hleg = get (h, "__legend_handle__");
149    catch
150      hleg = false;
151    end_try_catch
152
153    ## Find and stack any colorbar belonging to this axes above this axes.
154    try
155      hcb = get (h, "__colorbar_handle__");
156    catch
157      hcb = false;
158    end_try_catch
159
160    ## Preserve order of colorbars and legends above this axes
161    if (hleg || hcb)
162      if (hleg && ! hcb)
163        h = [hleg; h];
164      elseif (hcb && ! hleg)
165        h = [hcb; h];
166      else
167        hcb_idx = find (hcb == hax);
168        hleg_idx = find (hleg == hax);
169        if (hleg_idx < hcb_idx)
170          h = [hleg; hcb; h];
171        else
172          h = [hcb; hleg; h];
173        endif
174      endif
175    endif
176
177    ## FIXME: ismember call is very slow (2/3rds of runtime for function)
178    ch(axidx) = [h; hax(! ismember (hax, h))];
179    set (hpar, "children", ch);
180
181  unwind_protect_cleanup
182    set (0, "ShowHiddenHandles", shh);
183  end_unwind_protect
184
185endfunction
186
187
188## FIXME: These demos actually just show how axes objects behave.
189##        They do not show how the axes() function itself works.
190%!demo
191%! clf;
192%! x = -10:10;
193%! plot (x,x, x,-x);
194%! set (gca, "yscale", "log");
195%! legend ({"x >= 1", "x <= 1"}, "location", "north");
196%! title ({"log axes discard negative data", "ylim = [1, 10]"});
197
198%!demo
199%! clf;
200%! x = -10:0.1:10;
201%! y = sin (x)./(1 + abs (x)) + 0.1*x - 0.4;
202%! plot (x, y);
203%! set (gca, "xaxislocation", "origin");
204%! set (gca, "yaxislocation", "origin");
205%! box off;
206%! title ({"no plot box", "xaxislocation = origin, yaxislocation = origin"});
207
208%!demo
209%! clf;
210%! x = -10:0.1:10;
211%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
212%! plot (x, y);
213%! set (gca, "xaxislocation", "origin");
214%! set (gca, "yaxislocation", "left");
215%! box off;
216%! title ({"no plot box", "xaxislocation = origin, yaxislocation = left"});
217
218%!demo
219%! clf;
220%! x = -10:0.1:10;
221%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
222%! plot (x, y);
223%! title ("no plot box");
224%! set (gca, "xaxislocation", "origin");
225%! set (gca, "yaxislocation", "right");
226%! box off;
227%! title ({"no plot box", "xaxislocation = origin, yaxislocation = right"});
228
229%!demo
230%! clf;
231%! x = -10:0.1:10;
232%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
233%! plot (x, y);
234%! set (gca, "xaxislocation", "bottom");
235%! set (gca, "yaxislocation", "origin");
236%! box off;
237%! title ({"no plot box", "xaxislocation = bottom, yaxislocation = origin"});
238
239%!demo
240%! clf;
241%! x = -10:0.1:10;
242%! y = sin (x)./(1+abs (x)) + 0.1*x - 0.4;
243%! plot (x, y);
244%! set (gca, "xaxislocation", "top");
245%! set (gca, "yaxislocation", "origin");
246%! box off;
247%! title ({"no plot box", "xaxislocation = top, yaxislocation = origin"});
248
249%!test
250%! hf = figure ("visible", "off");
251%! unwind_protect
252%!   hax1 = axes ("tag", "axes1");
253%!   plot (1:10, "b");
254%!   hleg1 = legend ("leg1", "location", "east");
255%!   hcb1 = colorbar ("location", "east");
256%!   hanno = annotation ("arrow");
257%!   hscribe = get (hanno, "parent");
258%!
259%!   hax2 = axes ("tag", "axes2");
260%!   plot (10:-1:1, "r");
261%!   hcb2 = colorbar ("location", "east");
262%!   hleg2 = legend ("hax2");
263%!
264%!   ## Verify base configuration
265%!   ch = allchild (hf);
266%!   hax = ch(isaxes (ch));
267%!   hax1pos = find (hax1 == hax);
268%!   hax2pos = find (hax2 == hax);
269%!   hleg1pos = find (hleg1 == hax);
270%!   hleg2pos = find (hleg2 == hax);
271%!   hcb1pos = find (hcb1 == hax);
272%!   hcb2pos = find (hcb2 == hax);
273%!   hscribepos = find (hscribe == hax);
274%!
275%!   assert (all (hscribepos < ...
276%!                [hax1pos, hax2pos, hleg1pos, hleg2pos, hcb1pos, hcb2pos]));
277%!   assert (hax2pos < hax1pos);
278%!   assert (hleg2pos < hcb2pos && hcb2pos < hax2pos);
279%!   assert (hcb1pos < hleg1pos && hleg1pos < hax1pos);
280%!
281%!   ## Restack axes1 on top
282%!   axes (hax1);
283%!   ch = allchild (hf);
284%!   hax = ch(isaxes (ch));
285%!   hax1pos = find (hax1 == hax);
286%!   hax2pos = find (hax2 == hax);
287%!   hleg1pos = find (hleg1 == hax);
288%!   hleg2pos = find (hleg2 == hax);
289%!   hcb1pos = find (hcb1 == hax);
290%!   hcb2pos = find (hcb2 == hax);
291%!   hscribepos = find (hscribe == hax);
292%!
293%!   assert (all (hscribepos < ...
294%!                [hax1pos, hax2pos, hleg1pos, hleg2pos, hcb1pos, hcb2pos]));
295%!   assert (hax1pos < hax2pos);
296%!   assert (hcb1pos < hleg1pos && hleg1pos < hax1pos);
297%!   assert (hleg2pos < hcb2pos && hcb2pos < hax2pos);
298%!
299%!   ## Restack axes2 on top
300%!   axes (hax2);
301%!   ch = allchild (hf);
302%!   hax = ch(isaxes (ch));
303%!   hax1pos = find (hax1 == hax);
304%!   hax2pos = find (hax2 == hax);
305%!   hleg1pos = find (hleg1 == hax);
306%!   hleg2pos = find (hleg2 == hax);
307%!   hcb1pos = find (hcb1 == hax);
308%!   hcb2pos = find (hcb2 == hax);
309%!   hscribepos = find (hscribe == hax);
310%!
311%!   assert (all (hscribepos < ...
312%!                [hax1pos, hax2pos, hleg1pos, hleg2pos, hcb1pos, hcb2pos]));
313%!   assert (hax2pos < hax1pos);
314%!   assert (hleg2pos < hcb2pos && hcb2pos < hax2pos);
315%!   assert (hcb1pos < hleg1pos && hleg1pos < hax1pos);
316%!
317%! unwind_protect_cleanup
318%!   close (hf);
319%! end_unwind_protect
320
321%!error <H must be a scalar handle> axes ([0, 0])
322%!error <H must be a handle to an axes or container> axes (0)
323