function [y] = linspace_round(min_val,max_val,n)
%LINSPACE_ROUND Creates linearly spaced vector with "rounded" intervals.
% LINSPACE_ROUND(x1, x2) generates a row vector of 100 equally spaced
% points that bound x1 and x2. The spacing is chosen according to the
% vector round_sample_ints, defined below. At present, it is chosen such
% that tenths and quarters are considered "rounded" fractions.
%
% LINSPACE_ROUND(x1, x2, N) generates approximately N points that
% bound x1 and x2.
%
% Example: If X1 = 3.4, X2 = 7.5, N = 6,
%
% then LINSPACE_ROUND(X1,X2,N) is [2.8 3.5 4.2 4.9 5.6 6.3 7.0 7.7]
% (in words, choosing spacing of 0.7 will produce about 6 points that
% bound X1 and X2)
%
% This is useful in having automatically chosen data bins, contour
% intervals, graph tick marks, etc., that are pleasing to the eye.
%
% See also LINSPACE, COLON.
% Author: Andy Eichelberger
if nargin == 2
n = 100;
end
if ~isreal(min_val) || ~isreal(max_val)
warning('linspace_round:bad_input','LINSPACE_ROUND has been passed an imaginary number')
end
% if min_val == max_val, just return n of that value
if min_val == max_val
y = ones(1,n)*min_val;
return
end
approx_sample_int = (max_val - min_val)/n;
% want to have spacing in 1/10s or 1/4s of the appropriate sig fig
round_sample_ints = [.1 .2 .25 .3 .4 .5 .6 .7 .75 .9 1];
sample_int = find_nearest(approx_sample_int,round_sample_ints);
% pick start and end values that are within the bounds
start_val = floor(min_val/sample_int)*sample_int;
end_val = ceil(max_val/sample_int)*sample_int;
y = start_val:sample_int:end_val;
% --------------------------------------------------------------------
function out_val = find_nearest(in_val,possible_vals)
%FIND_NEAREST Find nearest appropriate spacing.
%
% See also LINSPACE_ROUND.
% check for zero since we'll be using log10
if in_val == 0
out_val = 0;
return
end
% find the exponent when put in scientific notation
exponent = floor(log10(abs(in_val)));
% divide it by (10 to that exponent plus one) so decimal preceeds the first sig fig
dec_val = in_val/10^(exponent+1);
% take absolute value so it can be compared against an all-positive vector of possible_vals
dec_val = abs(dec_val);
% prefer .1 to 1 for consistency with the other decimal values
if dec_val == 1
dec_val = .1;
end
% find the closest match
difference = abs(possible_vals - dec_val);
nearest_ind = find(min(difference) == difference);
% if it's half way between two round_sample_ints, take the first
nearest_ind = nearest_ind(1);
% put the (10 to the exponent plus one) back in, shifting the decimal back to its original location
out_val = possible_vals(nearest_ind)*10^(exponent+1);
% multiply by sign(in_val) to cancel out the abs() above
out_val = out_val*sign(in_val);