1########################################################################
2##
3## Copyright (C) 1996-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  {} {} plot3 (@var{x}, @var{y}, @var{z})
28## @deftypefnx {} {} plot3 (@var{x}, @var{y}, @var{z}, @var{prop}, @var{value}, @dots{})
29## @deftypefnx {} {} plot3 (@var{x}, @var{y}, @var{z}, @var{fmt})
30## @deftypefnx {} {} plot3 (@var{x}, @var{cplx})
31## @deftypefnx {} {} plot3 (@var{cplx})
32## @deftypefnx {} {} plot3 (@var{hax}, @dots{})
33## @deftypefnx {} {@var{h} =} plot3 (@dots{})
34## Produce 3-D plots.
35##
36## Many different combinations of arguments are possible.  The simplest
37## form is
38##
39## @example
40## plot3 (@var{x}, @var{y}, @var{z})
41## @end example
42##
43## @noindent
44## in which the arguments are taken to be the vertices of the points to
45## be plotted in three dimensions.  If all arguments are vectors of the
46## same length, then a single continuous line is drawn.  If all arguments
47## are matrices, then each column of is treated as a separate line.  No attempt
48## is made to transpose the arguments to make the number of rows match.
49##
50## If only two arguments are given, as
51##
52## @example
53## plot3 (@var{x}, @var{cplx})
54## @end example
55##
56## @noindent
57## the real and imaginary parts of the second argument are used
58## as the @var{y} and @var{z} coordinates, respectively.
59##
60## If only one argument is given, as
61##
62## @example
63## plot3 (@var{cplx})
64## @end example
65##
66## @noindent
67## the real and imaginary parts of the argument are used as the @var{y}
68## and @var{z} values, and they are plotted versus their index.
69##
70## Arguments may also be given in groups of three as
71##
72## @example
73## plot3 (@var{x1}, @var{y1}, @var{z1}, @var{x2}, @var{y2}, @var{z2}, @dots{})
74## @end example
75##
76## @noindent
77## in which each set of three arguments is treated as a separate line or
78## set of lines in three dimensions.
79##
80## To plot multiple one- or two-argument groups, separate each group
81## with an empty format string, as
82##
83## @example
84## plot3 (@var{x1}, @var{c1}, "", @var{c2}, "", @dots{})
85## @end example
86##
87## Multiple property-value pairs may be specified which will affect the line
88## objects drawn by @code{plot3}.  If the @var{fmt} argument is supplied it
89## will format the line objects in the same manner as @code{plot}.
90## The full list of properties is documented at
91## @ref{Line Properties}.
92##
93## If the first argument @var{hax} is an axes handle, then plot into this axes,
94## rather than the current axes returned by @code{gca}.
95##
96## The optional return value @var{h} is a graphics handle to the created plot.
97##
98## Example:
99##
100## @example
101## @group
102## z = [0:0.05:5];
103## plot3 (cos (2*pi*z), sin (2*pi*z), z, ";helix;");
104## plot3 (z, exp (2i*pi*z), ";complex sinusoid;");
105## @end group
106## @end example
107## @seealso{ezplot3, plot}
108## @end deftypefn
109
110function h = plot3 (varargin)
111
112  [hax, varargin, nargs] = __plt_get_axis_arg__ ("plot3", varargin{:});
113
114  if (nargs < 1)
115    print_usage ();
116  endif
117
118  oldfig = [];
119  if (! isempty (hax))
120    oldfig = get (0, "currentfigure");
121  endif
122  unwind_protect
123    hax = newplot (hax);
124
125    x_set = 0;
126    y_set = 0;
127    z_set = 0;
128    property_set = 0;
129    fmt_set = 0;
130    properties = {};
131    tlgnd = {};
132    hlgnd = [];
133    htmp = [];
134    idx = 0;
135
136    ## Gather arguments, decode format, and plot lines.
137    arg = 0;
138    while (arg++ < nargs)
139      new = varargin{arg};
140      new_cell = varargin(arg);
141
142      if (property_set)
143        properties = [properties, new_cell];
144        property_set = 0;
145        continue;
146      endif
147
148      if (ischar (new))
149        if (! z_set)
150          if (! y_set)
151            if (! x_set)
152              error ("plot3: needs X, [ Y, [ Z ] ]");
153            else
154              y = real (x);
155              z = imag (x);
156              y_set = 1;
157              z_set = 1;
158              if (rows (x) > 1)
159                x = repmat ((1:rows (x))', 1, columns (x));
160              else
161                x = 1:columns (x);
162              endif
163            endif
164          else
165            z = imag (y);
166            y = real (y);
167            z_set = 1;
168          endif
169        endif
170
171        if (! fmt_set)
172          [options, valid] = __pltopt__ ("plot3", new, false);
173          if (! valid)
174            properties = [properties, new_cell];
175            property_set = 1;
176            continue;
177          else
178            fmt_set = 1;
179            while (arg < nargs && ischar (varargin{arg+1}))
180              if (nargs - arg < 2)
181                error ("plot3: properties must appear followed by a value");
182              endif
183              properties = [properties, varargin(arg+1:arg+2)];
184              arg += 2;
185            endwhile
186          endif
187        else
188          properties = [properties, new_cell];
189          property_set = 1;
190          continue;
191        endif
192
193        if (isvector (x) && isvector (y))
194          if (isvector (z))
195            x = x(:);
196            y = y(:);
197            z = z(:);
198          elseif (length (x) == rows (z) && length (y) == columns (z))
199            [x, y] = meshgrid (x, y);
200          else
201            error ("plot3: [length(X), length(Y)] must match size (Z)");
202          endif
203        endif
204
205        if (! size_equal (x, y, z))
206          error ("plot3: X, Y, and Z must have the same shape");
207        elseif (ndims (x) > 2)
208          error ("plot3: X, Y, and Z must not have more than two dimensions");
209        endif
210
211        for i = 1 : columns (x)
212          linestyle = options.linestyle;
213          marker = options.marker;
214          if (isempty (marker) && isempty (linestyle))
215             [linestyle, marker] = __next_line_style__ ();
216          endif
217          color = options.color;
218          if (isempty (color))
219            color = __next_line_color__ ();
220          endif
221
222          htmp(++idx) = __go_line__ (hax, "xdata", x(:, i), "ydata", y(:, i),
223                                     "zdata", z(:, i),
224                                     "color", color, "linestyle", linestyle,
225                                     "marker", marker, properties{:});
226          key = options.key;
227          if (! isempty (key))
228            hlgnd = [hlgnd, htmp(idx)];
229            tlgnd = {tlgnd{:}, key};
230          endif
231        endfor
232
233        x_set = 0;
234        y_set = 0;
235        z_set = 0;
236        fmt_set = 0;
237        properties = {};
238      elseif (! x_set)
239        x = new;
240        x_set = 1;
241      elseif (! y_set)
242        y = new;
243        y_set = 1;
244      elseif (! z_set)
245        z = new;
246        z_set = 1;
247      else
248        if (isvector (x) && isvector (y))
249          if (isvector (z))
250            x = x(:);
251            y = y(:);
252            z = z(:);
253          elseif (length (x) == rows (z) && length (y) == columns (z))
254            [x, y] = meshgrid (x, y);
255          else
256            error ("plot3: [length(X), length(Y)] must match size (Z)");
257          endif
258        endif
259
260        if (! size_equal (x, y, z))
261          error ("plot3: X, Y, and Z must have the same shape");
262        elseif (ndims (x) > 2)
263          error ("plot3: X, Y, and Z must not have more than two dimensions");
264        endif
265
266        options = __default_plot_options__ ();
267        for i = 1 : columns (x)
268          linestyle = options.linestyle;
269          marker = options.marker;
270          if (isempty (marker) && isempty (linestyle))
271            [linestyle, marker] = __next_line_style__ ();
272          endif
273          color = options.color;
274          if (isempty (color))
275            color = __next_line_color__ ();
276          endif
277
278          htmp(++idx) = __go_line__ (hax, "xdata", x(:, i), "ydata", y(:, i),
279                                     "zdata", z(:, i),
280                                     "color", color, "linestyle", linestyle,
281                                     "marker", marker, properties{:});
282          key = options.key;
283          if (! isempty (key))
284            hlgnd = [hlgnd, htmp(idx)];
285            tlgnd = {tlgnd{:}, key};
286          endif
287        endfor
288
289        x = new;
290        y_set = 0;
291        z_set = 0;
292        fmt_set = 0;
293        properties = {};
294      endif
295
296    endwhile
297
298    if (property_set)
299      error ("plot3: properties must appear followed by a value");
300    endif
301
302    ## Handle last plot.
303
304    if (x_set)
305      if (y_set)
306        if (! z_set)
307          z = imag (y);
308          y = real (y);
309          z_set = 1;
310        endif
311      else
312        y = real (x);
313        z = imag (x);
314        y_set = 1;
315        z_set = 1;
316        if (rows (x) > 1)
317          x = repmat ((1:rows (x))', 1, columns (x));
318        else
319          x = 1:columns (x);
320        endif
321      endif
322
323      if (isvector (x) && isvector (y))
324        if (isvector (z))
325          x = x(:);
326          y = y(:);
327          z = z(:);
328        elseif (length (x) == rows (z) && length (y) == columns (z))
329          [x, y] = meshgrid (x, y);
330        else
331          error ("plot3: [length(X), length(Y)] must match size (Z)");
332        endif
333      endif
334
335      if (! size_equal (x, y, z))
336        error ("plot3: X, Y, and Z must have the same shape");
337      elseif (ndims (x) > 2)
338        error ("plot3: X, Y, and Z must not have more than two dimensions");
339      endif
340
341      options = __default_plot_options__ ();
342
343      for i = 1 : columns (x)
344        linestyle = options.linestyle;
345        marker = options.marker;
346        if (isempty (marker) && isempty (linestyle))
347          [linestyle, marker] = __next_line_style__ ();
348        endif
349        color = options.color;
350        if (isempty (color))
351          color = __next_line_color__ ();
352        endif
353
354        htmp(++idx) = __go_line__ (hax, "xdata", x(:, i), "ydata", y(:, i),
355                                   "zdata", z(:, i),
356                                   "color", color, "linestyle", linestyle,
357                                   "marker", marker, properties{:});
358        key = options.key;
359        if (! isempty (key))
360          hlgnd = [hlgnd, htmp(idx)];
361          tlgnd = {tlgnd{:}, key};
362        endif
363      endfor
364    endif
365
366    if (! isempty (hlgnd))
367      legend (hax, hlgnd, tlgnd);
368    endif
369
370    if (! ishold ())
371      set (hax, "view", [-37.5, 30]);
372    endif
373
374  unwind_protect_cleanup
375    if (! isempty (oldfig))
376      set (0, "currentfigure", oldfig);
377    endif
378  end_unwind_protect
379
380  if (nargout > 0)
381    h = htmp;
382  endif
383
384endfunction
385
386
387%!demo
388%! clf;
389%! z = [0:0.05:5];
390%! plot3 (cos (2*pi*z), sin (2*pi*z), z);
391%! legend ("helix");
392%! title ("plot3() of a helix");
393
394%!demo
395%! clf;
396%! z = [0:0.05:5];
397%! plot3 (z, exp (2i*pi*z));
398%! legend ("complex sinusoid");
399%! title ("plot3() with complex input");
400