/usr/share/octave/packages/plot-1.1.0/plotdecimate.m is in octave-plot 1.1.0-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | ## Copyright (C) 2006 Francesco Potortì <potorti@isti.cnr.it>
##
## This program 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 2 of the License, or
## (at your option) any later version.
##
## This program 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 program; if not, write to the Free Software Foundation,
## Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA.
## -*- texinfo -*-
## @deftypefn {Function File} {} plotdecimate (@var{P})
## @deftypefnx {Function File} {} plotdecimate (@var{P}, @var{so})
## @deftypefnx {Function File} {} plotdecimate (@var{P}, @var{so}, @var{res})
## Optimise plot data by removing redundant points and segments
##
## The first parameter @var{P} is a two-column matrix to be plotted as X and
## Y coordinates. The second optional argument @var{so} disables segment
## optimisation when set to @var{false} (default is @var{true}). The third
## optional argument @var{res} is the size of the largest error on the plot:
## if it is a scalar, it is meant relative to the range of X and Y values
## (default 1e-3); if it is a 2x1 array, it contains the absolute errors for
## X and Y. Returns a two-column matrix containing a subset of the rows of
## @var{P}. A line plot of @var{P} has the same appearance as a line plot of
## the output, with errors smaller than @var{res}. When creating point
## plots, set @var{so} to @var{false}.
## @end deftypefn
function C = plotdecimate (P, so, res)
if (!ismatrix(P) || columns(P) != 2)
error("P must be a matrix with two columns");
endif
if (nargin < 2)
so = true; # do segment optimisation
endif
if (nargin < 3)
res = 1e-3; # default resolution is 1000 dots/axis
endif
## Slack: admissible error on coordinates on the output plot
if (isscalar(res))
if (res <= 0)
error("res must be positive");
endif
E = range(P)' * res; # build error vector using range of data
elseif (ismatrix(res))
if (!all(size(res) == [2 1]) || any(res <= 0))
error("res must be a 2x1 matrix with positive values");
endif
E = res; # take error vector as it is
else
error("res should be a scalar or matrix");
endif
if (rows(P) < 3)
C = P;
return; # nothing to do
endif
P ./= repmat(E',rows(P),1); # normalize P
rot = [0,-1;1,0]; # rotate a vector pi/4 anticlockwise
## Iteratively remove points too near to the previous point
while (1)
V = [true; sumsq(diff(P),2) > 1]; # points far from the previous ones
if (all(V)) break; endif
V = [true; diff(V) >= 0]; # identify the sequence leaders
P = P(V,:); # remove them
endwhile
## Remove points laying near to a segment: for each segment R->S, build a
## unitary-lenght projection vector D perpendicular to R->S, and project
## R->T over D to compute the distance ot T from R->S.
if (so) # segment optimisation
## For each segment, r and s are its extremes
r = 1; R = P(1,:)'; # start of segment
s = 2; S = P(2,:)'; # end of the segment
rebuild = true; # build first projection vector
for t = 3:rows(P)
if (rebuild) # build projection vector
D = rot*(S-R)/sqrt(sumsq(S-R)); # projection vector for distance
rebuild = false; # keep current projection vector
endif
T = P(t,:)'; # next point
if (abs(sum((T-R).*D)) < 1 # T is aligned
&& sum((T-R).*(S-R)) > 0) # going forward
V(s) = false; # do not plot s
else # set a new segment
r = s; R = S; # new start of segment
rebuild = true; # rebuild projection vector
endif
s = t; S = T; # new end of segment
endfor
endif
C = P(V,:) .* repmat(E',sum(V),1); # denormalize P
endfunction
%!test
%! x = [ 0 1 2 3 4 8 8 8 8 8 9 ]';
%! y = [ 0 1 1 1 1 1 1 2 3 4 5 ]';
%!
%! x1 = [0 1 8 8 9]';
%! y1 = [0 1 1 4 5]';
%! # optimised for segment plot
%!
%! x2 = [ 0 1 2 3 4 8 8 8 8 9 ]';
%! y2 = [ 0 1 1 1 1 1 2 3 4 5 ]';
%! # double points removed
%!
%! P = [x,y];
%! # Original
%! P1 = [x1, y1];
%! # optimised segments
%! P2 = [x2, y2];
%! # double points removed
%!
%! assert(plotdecimate(P), P1);
%! assert(plotdecimate(P, false), P2);
|