diff --git a/NEWS b/NEWS index a478aca78..a152126fe 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ octsympy 3.1.1+ =============== + * New symbolic commands: + + fplot + * Move repo to https://github.com/gnu-octave/symbolic * `sym(, ratflag)` now respects `ratflag` (issue #1273). * Changed metainfo.xml ID and corrected long-standing "Addon" diff --git a/inst/@sym/ezmesh.m b/inst/@sym/ezmesh.m index 0487e04ff..499422e21 100644 --- a/inst/@sym/ezmesh.m +++ b/inst/@sym/ezmesh.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2019 Colin B. Macdonald +%% Copyright (C) 2019, 2023 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -81,12 +81,10 @@ assert(length(thissym) <= 2, ... 'ezmesh: parameterized: functions should have at most two inputs'); if (isempty(thissym)) - % a number, create a constant function in a dummy variable - % (0*t works around some Octave oddity on 3.8 and hg Dec 2014) - thisf = inline(sprintf('%g + 0*t', double(varargin{i})), 't'); - %thisf = @(t) 0*t + double(varargin{i}); % no + % constant function + thisf = function_handle (varargin{i}, 'vars', sym ('t')); else - % check variables match (sanity check) + % check variables match over each function if (isempty(firstsym)) firstsym = thissym; else diff --git a/inst/@sym/ezplot.m b/inst/@sym/ezplot.m index f2afa3035..c972f4615 100644 --- a/inst/@sym/ezplot.m +++ b/inst/@sym/ezplot.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2014-2017 Colin B. Macdonald +%% Copyright (C) 2014-2017, 2023 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -73,7 +73,7 @@ %% The solution, as shown in the example, is to convert the sym to %% a double. %% -%% @seealso{ezplot, @@sym/ezplot3, @@sym/ezsurf, @@sym/function_handle} +%% @seealso{ezplot, @@sym/ezplot3, @@sym/ezsurf, @@sym/fplot, @@sym/function_handle} %% @end defmethod @@ -102,12 +102,10 @@ assert(length(thissym) <= 2, ... 'ezplot: plotting curves: functions should have at most two inputs'); if (isempty(thissym)) - % a number, create a constant function in a dummy variable - % (0*t works around some Octave oddity on 3.8 and hg Dec 2014) - thisf = inline(sprintf('%g + 0*t', double(varargin{i})), 't'); - %thisf = @(t) 0*t + double(varargin{i}); % no + % constant function + thisf = function_handle (varargin{i}, 'vars', sym ('t')); else - % check variables match (sanity check) + % check variables match over each function if (isempty(firstsym)) firstsym = thissym; else diff --git a/inst/@sym/ezplot3.m b/inst/@sym/ezplot3.m index 4e6a9aa7d..ebb1e35f6 100644 --- a/inst/@sym/ezplot3.m +++ b/inst/@sym/ezplot3.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2014-2016 Colin B. Macdonald +%% Copyright (C) 2014-2016, 2023 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -66,12 +66,10 @@ assert(length(thissym) <= 1, ... 'ezplot3: plotting curves: functions should have at most one input'); if (isempty(thissym)) - % a number, create a constant function in a dummy variable - % (0*t works around some Octave oddity on 3.8 and hg Dec 2014) - thisf = inline(sprintf('%g + 0*t', double(varargin{i})), 't'); - %thisf = @(t) 0*t + double(varargin{i}); % no + % constant function + thisf = function_handle (varargin{i}, 'vars', sym ('t')); else - % check variables match (sanity check) + % check variables match over each function if (isempty(firstsym)) firstsym = thissym; else diff --git a/inst/@sym/ezsurf.m b/inst/@sym/ezsurf.m index 7d33492d9..00396b1f6 100644 --- a/inst/@sym/ezsurf.m +++ b/inst/@sym/ezsurf.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2016-2017, 2019 Colin B. Macdonald +%% Copyright (C) 2016-2017, 2019, 2023 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -80,13 +80,11 @@ thissym = symvar(varargin{i}); assert(length(thissym) <= 2, ... 'ezsurf: parameterized: functions should have at most two inputs'); - if (isempty(thissym)) - % a number, create a constant function in a dummy variable - % (0*t works around some Octave oddity on 3.8 and hg Dec 2014) - thisf = inline(sprintf('%g + 0*t', double(varargin{i})), 't'); - %thisf = @(t) 0*t + double(varargin{i}); % no + if (isempty (thissym)) + % constant function + thisf = function_handle (varargin{i}, 'vars', sym ('t')); else - % check variables match (sanity check) + % check variables match over each function if (isempty(firstsym)) firstsym = thissym; else diff --git a/inst/@sym/fplot.m b/inst/@sym/fplot.m new file mode 100644 index 000000000..a84ffcfe3 --- /dev/null +++ b/inst/@sym/fplot.m @@ -0,0 +1,211 @@ +%% Copyright (C) 2023 Colin B. Macdonald +%% +%% This file is part of OctSymPy. +%% +%% OctSymPy 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 software 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 this software; see the file COPYING. +%% If not, see . + +%% -*- texinfo -*- +%% @documentencoding UTF-8 +%% @defmethod @@sym fplot (@var{f}) +%% @defmethodx @@sym fplot (@var{f}, @var{limits}) +%% @defmethodx @@sym fplot (@dots{}, @dots{}, @var{N}) +%% @defmethodx @@sym fplot (@dots{}, @dots{}, @var{tol}) +%% @defmethodx @@sym fplot (@dots{}, @dots{}, @var{fmt}) +%% @defmethodx @@sym {[@var{x}, @var{y}] =} fplot (@dots{}) +%% Plot a symbolic expression. +%% +%% Examples: +%% @example +%% @group +%% syms x +%% y = cos(3*x) +%% @result{} y = (sym) cos(3⋅x) +%% +%% @c doctest: +SKIP +%% fplot (y) +%% +%% @c doctest: +SKIP +%% fplot (y, [0 pi]) +%% @end group +%% @end example +%% +%% You can also grab the data that would be plotted and plot it +%% yourself: +%% @example +%% @group +%% syms x +%% +%% @c doctest: +SKIP_IF(compare_versions (OCTAVE_VERSION(), '6.0.0', '<')) +%% [xx, yy] = fplot (sin (x), [0 1]) +%% @result{} xx = +%% 0 +%% ... +%% 1.0000 +%% @result{} yy = +%% 0 +%% ... +%% 0.8415 +%% +%% @c doctest: +SKIP +%% plot (xx, yy) +%% @end group +%% @end example +%% +%% See help for the (non-symbolic) @code{fplot}, which this +%% routine calls after trying to convert sym inputs to +%% anonymous functions. +%% +%% @seealso{fplot, @@sym/ezplot, @@sym/function_handle} +%% @end defmethod + + +function [xx, yy] = fplot (varargin) + + % first input is handle, shift + if (ishandle (varargin{1})) + fshift = 1; + else + fshift = 0; + end + + for i = (1+fshift):nargin + if (isa (varargin{i}, 'sym')) + if (i == 1 + fshift) + thissym = symvar (varargin{i}); + assert (length (thissym) <= 1, ... + 'fplot: functions should have at most one input'); + if (isempty (thissym)) + thisf = function_handle (varargin{i}, 'vars', sym ('x')); + else + thisf = function_handle (varargin{i}); + end + + varargin{i} = thisf; + + else + % plot ranges, etc, convert syms to doubles + varargin{i} = double (varargin{i}); + end + end + end + + if (nargout) + [xx, yy] = fplot (varargin{:}); + else + fplot(varargin{:}); + end + +end + + +%!test +%! % simple +%! syms x +%! f = cos (x); +%! fplot (f); + +%!test +%! % constant function +%! fplot (sym (10)); + +%!test +%! syms x +%! f = cos (x); +%! [xx, yy] = fplot (f); +%! assert (xx(1), -5) +%! assert (xx(end), 5) +%! assert (min (yy), -1, 0.1) +%! assert (max (yy), 1, 0.1) + +%!test +%! syms x +%! f = cos (x); +%! dom = [1 3]; +%! [xx, yy] = fplot (f, dom); +%! assert (xx(1), dom(1)) +%! assert (xx(end), dom(2)) + +%!test +%! syms x +%! f = cos (x); +%! dom = [1 3]; +%! fplot (f, dom); +%! assert (get (gca, 'xlim'), dom) + +%!test +%! syms x +%! f = exp (x); +%! dom = [1 2 3 4]; +%! fplot (f, dom); +%! assert (get (gca, 'xlim'), dom(1:2)) +%! assert (get (gca, 'ylim'), dom(3:4)) + +%!test +%! % bounds as syms +%! syms x +%! f = cos (x); +%! dom = [1 2 3 4]; +%! fplot (f, sym (dom)); +%! assert (get (gca, 'xlim'), dom(1:2)) +%! assert (get (gca, 'ylim'), dom(3:4)) + +%!test +%! % bounds as syms, regular handle for function +%! % fails on 6.1.0, maybe earlier too? +%! if (compare_versions (OCTAVE_VERSION (), '6.1.0', '!=')) +%! dom = [1 2]; +%! fplot (@cos, sym (dom)); +%! assert (get (gca, 'xlim'), dom(1:2)) +%! end + +%!error +%! syms x y +%! fplot (x*y) + +%!test +%! % N parameter does something +%! syms x +%! [xx, yy] = fplot (sin (x), [0 2], 5); +%! N = length (xx); +%! assert (N >= 5) +%! [xx, yy] = fplot (sin (x), [0 2], 1000); +%! N = length (xx); +%! assert (N == 1000) + +%!test +%! % tolerance parameter does something +%! syms x +%! [xx, yy] = fplot (sin (exp (x/2)), [0 3], 0.1); +%! N1 = length (xx); +%! [xx, yy] = fplot (sin (exp (x/2)), [0 3], 0.01); +%! N2 = length (xx); +%! assert (N2 > N1) + +%!test +%! % fmt parameter does something +%! syms x +%! fplot (sin (x), [0 6], 'rx--', 'linewidth', 5); +%! l = get (gca (), 'children'); +%! assert (get (l, 'color'), [1 0 0]) +%! assert (get (l, 'linewidth'), 5) + +%! f = exp (x); +%! dom = [1 2 3 4]; +%! fplot (f, dom); +%! assert (get (gca, 'xlim'), dom(1:2)) +%! assert (get (gca, 'ylim'), dom(3:4)) + +%!test +%! close all diff --git a/inst/@sym/function_handle.m b/inst/@sym/function_handle.m index f02d0d2d5..86c34e229 100644 --- a/inst/@sym/function_handle.m +++ b/inst/@sym/function_handle.m @@ -122,7 +122,7 @@ M.name = out{1}{1}; M.code = out{1}{2}; - assert (strcmp (M.name, [fcnname '.m']), 'sanity check failed: names should match'); + assert (strcmp (M.name, [fcnname '.m']), 'correctness check failed: names should match'); file_to_write = fullfile(fcnpath, [fcnname '.m']); [fid,msg] = fopen(file_to_write, 'w'); diff --git a/util/convert_comments.m b/util/convert_comments.m index 26b5e267d..e339a5a82 100644 --- a/util/convert_comments.m +++ b/util/convert_comments.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2014-2018 Colin B. Macdonald +%% Copyright (C) 2014-2018, 2023 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -209,7 +209,7 @@ function convert_comments (basedir, subdir, dirout) N = Nfcn; fcn_line = ins{N}; - % sanity checks + % correctness checks I = strfind(ins{N+1}, '%'); if ~isempty(I) && I(1) == 1 ins{N}