Skip to content

Commit

Permalink
bar and hist functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Nelson-numerical-software committed Nov 4, 2023
1 parent 556aad4 commit 9dd605a
Show file tree
Hide file tree
Showing 14 changed files with 7,320 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added

- `hist` Histogram plot.
- `bar` Bar graph.
- `scatter` Scatter plot.
- `stem` Plot discrete sequence data.
- `stairs` Stairstep graph.
Expand Down
172 changes: 172 additions & 0 deletions modules/graphics/functions/bar.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
%=============================================================================
% Copyright (c) 2023-present Allan CORNET (Nelson)
%=============================================================================
% This file is part of the Nelson.
%=============================================================================
% LICENCE_BLOCK_BEGIN
% SPDX-License-Identifier: LGPL-3.0-or-later
% LICENCE_BLOCK_END
%=============================================================================
function varargout = bar(varargin)
% basic implementation (stacked, group not yet implemented)
% bar(y)
% bar(x, y)
% bar(..., width)
% bar(..., color)
% bar(ax, ...)
% b = bar(...)
% b = bar(ax, x, y, width, color, ...)
narginchk(1, 100);
nargoutchk(0, 1);
[parent, X, Y, width, color, otherProperties] = parseArguments(varargin);
h = barPlot(parent, X, Y, width, color, otherProperties);
if (nargout == 1)
varargout{1} = h;
end
end
%=============================================================================
function [parent, X, Y, width, color, otherProperties] = parseArguments(inputArguments)
nbInputArguments = length(inputArguments);
if (nbInputArguments >= 1)
if (isscalar(inputArguments{1}) && (isgraphics(inputArguments{1}, 'axes') || isgraphics(inputArguments{1}, 'hggroup')))
parent = inputArguments{1}(1);
inputArguments = inputArguments(2:end);
nbInputArguments = nbInputArguments - 1;
else
parent = newplot();
end
else
parent = newplot();
end
defaultWidth = 0.8;
defaultColor = getColorAndUpdateIndex(parent);

supportedColorString = string(getColorShortNameList());
isString = @(x) (ischar(x) || isStringScalar(x)) && ~matches(convertCharsToStrings(x), supportedColorString);
firstString = find (cellfun(isString, inputArguments), 1);
if (isempty(firstString))
firstString = nbInputArguments + 1;
end

if (firstString > nbInputArguments)
otherProperties = struct([]);
else
otherProperties = inputArguments(firstString:end);
otherProperties = struct(otherProperties{:});
inputArguments = inputArguments(1:firstString-1);
end
nbInputArguments = length(inputArguments);

if nbInputArguments == 1
[X, Y, width, color] = parseOneArgument(inputArguments, defaultWidth, defaultColor);
end

if nbInputArguments == 2
[X, Y, width, color] = parseTwoArguments(inputArguments, defaultWidth, defaultColor);
end

if nbInputArguments == 3
[X, Y, width, color] = parseThreeArguments(inputArguments, defaultWidth, defaultColor);
end
if min(size(X)) == 1, X = X(:); end
if min(size(Y)) == 1, Y = Y(:); end
end
%=============================================================================
function [X, Y, width, color] = parseThreeArguments(inputArguments, defaultWidth, defaultColor)
width = defaultWidth;
color = defaultColor;
X = inputArguments{1};
X = X(:)';
if isscalar(inputArguments{2})
if isnumeric(inputArguments{2})
width = inputArguments{2};
elseif ischar(inputArguments{2}) || isStringScalar(inputArguments{2})
color = convertStringsToChars(inputArguments{2});
else
error(_('Unrecognized option for second argument.'));
end
else
Y = inputArguments{2};
Y = Y(:)';
end
if isnumeric(inputArguments{3})
width = inputArguments{3};
elseif ischar(inputArguments{3}) || isStringScalar(inputArguments{3})
color = convertStringsToChars(inputArguments{3});
else
error(_('Unrecognized option for third argument.'));
end
end
%=============================================================================
function [X, Y, width, color] = parseTwoArguments(inputArguments, defaultWidth, defaultColor)
width = defaultWidth;
color = defaultColor;
if isscalar(inputArguments{2})
if isnumeric(inputArguments{2})
width = inputArguments{2};
elseif ischar(inputArguments{2}) || isStringScalar(inputArguments{2})
color = convertStringsToChars(inputArguments{2})
else
error(_('Unrecognized option for second argument.'));
end
Y = inputArguments{1};
Y = Y(:)';
X = linspace(0, size(Y, 2) - 1, size(Y, 2)) + 1;
else
X = inputArguments{1};
X = X(:)';
Y = inputArguments{2};
Y = Y(:)';
end
end
%=============================================================================
function [X, Y, width, color] = parseOneArgument(inputArguments, defaultWidth, defaultColor)
width = defaultWidth;
color = defaultColor;
Y = inputArguments{1};
Y = Y(:)';
X = linspace(0, size(Y, 2) - 1, size(Y, 2)) + 1;
end
%=============================================================================
function h = barPlot(parent, X, Y,width, color, otherProperties)
needUpdateXLabel = false;
if isstring(X)
needUpdateXLabel = true;
xLabels = X;
X = 1:length(X);
X = reshape(X, size(xLabels))
end
df = width * diff(X) / 2;
df(end + 1) = df(end);
x_r = X(1:end) - df;
x_l = X(1:end) + df;

n = length(X);
onesXr = ones(size(x_r));
vertices = [x_r, zeros(size(x_r)), onesXr; ...
x_r, Y, onesXr; ...
x_l, Y, onesXr; ...
x_l, zeros(size(x_l)), onesXr];

ind = (1:n)';
faces = [ind, ind + n, ind + 2 * n, ind + 3 * n];
barProperties = struct('Parent', parent, 'Faces', faces, 'Vertices', vertices, 'FaceColor', color);
for name = fieldnames(otherProperties)'
barProperties.(name{1}) = otherProperties.(name{1});
end
barProperties = reshape([fieldnames(barProperties)'; struct2cell(barProperties)'], 1, []);
h = patch(barProperties{:});
if needUpdateXLabel
ax = gca();
ticks = ax.XTickLabel;
res = cell(1, numel(ticks));
for i = 1:numel(X)
idx = find(strcmp(ticks, num2str(X(i))));
if ~isempty(idx)
res{idx} = xLabels(i);
end
end
ax.XTickLabel = string(res);
end
end
%=============================================================================
126 changes: 126 additions & 0 deletions modules/graphics/functions/hist.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
%=============================================================================
% Copyright (c) 2023-present Allan CORNET (Nelson)
%=============================================================================
% This file is part of the Nelson.
%=============================================================================
% LICENCE_BLOCK_BEGIN
% SPDX-License-Identifier: LGPL-3.0-or-later
% LICENCE_BLOCK_END
%=============================================================================
function varargout = hist(varargin)

narginchk(1, 100);
nargoutchk(0, 2);

inputArguments = varargin;
nbInputArguments = length(inputArguments);
if (nargout == 0)
if (nbInputArguments >= 1)
if (isscalar(inputArguments{1}) && (isgraphics(inputArguments{1}, 'axes') || isgraphics(inputArguments{1}, 'hggroup')))
parent = inputArguments{1}(1);
inputArguments = inputArguments(2:end);
nbInputArguments = nbInputArguments - 1;
else
parent = newplot();
end
else
parent = newplot();
end
end

y = inputArguments{1};
inputArguments = inputArguments(2:end);
nbInputArguments = length(inputArguments);

asVector = isvector(y);
y = y(:);

if ~isreal(y)
y = real(y);
end
max_val = max (y(:));
min_val = min (y(:));

if (nbInputArguments == 0)
n = 10;
x = [0.5:n]'/n;
x = x * (max_val - min_val) + ones(size(x)) * min_val;
else
x = inputArguments{1};
inputArguments = inputArguments(2:end);
if (isscalar(x))
n = x;
if (n <= 0)
error(_('Second argument must be positive.'));
end
x = [0.5:n]'/n;
x = x * (max_val - min_val) + ones (size (x)) * min_val;
elseif (isreal (x))
if (isvector (x))
x = x(:);
end
x = sort (x);
else
error(_('Second argument must be a scalar or vector.'));
end
end

x = double (x);
y = double (y);
freq = computesFreq(x, y);

if (nbInputArguments > 2 && ~ischar(inputArguments{iarg}))
norm = inputArguments{iarg};
inputArguments = inputArguments(2:end);
freq = freq / size(y, 1) * norm;
end

if nargout == 0
if (size(freq, 2) == 1)
width = 1;
else
width = 0.8;
end
h = bar(parent, x, freq, width, inputArguments{:});
end

if nargout > 0
if ~asVector
varargout{1} = freq;
else
varargout{1} = freq';
end
end

if nargout > 1
if ~asVector
varargout{2} = x;
else
varargout{2} = x';
end
end
end
%=============================================================================
function freq = computesFreq(x, y)
cutout = (x(1:end - 1,:) + x(2:end, :)) / 2;
n = size(x, 1);
offset = 30;
withLoopOnN = (n < offset && size(x, 2) == 1);
if ~withLoopOnN
[s, idx] = sort([y; cutout]);
lenY = size(y, 1);
histValues = cumsum(idx <= lenY);
x1 = zeros(1, size(y, 2));
x2 = reshape(histValues(idx > lenY), size(cutout));
x3 = histValues(end, :) - sum(isnan(y));
histValues = [x1; x2; x3];
else
histValues = zeros (n + 1, size(y, 2));
for i = 1:n-1
histValues(i + 1, :) = sum(y <= cutout(i));
end
histValues(n + 1, :) = sum(~isnan(y));
end
freq = diff(histValues);
end
%=============================================================================
Loading

0 comments on commit 9dd605a

Please sign in to comment.