1 ## Copyright (C) 2006 Francesco Potortì <potorti@isti.cnr.it>
3 ## This program is free software; you can redistribute it and/or modify
4 ## it under the terms of the GNU General Public License as published by
5 ## the Free Software Foundation; either version 2 of the License, or
6 ## (at your option) any later version.
8 ## This program is distributed in the hope that it will be useful,
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 ## GNU General Public License for more details.
13 ## You should have received a copy of the GNU General Public License
14 ## along with this program; if not, write to the Free Software Foundation,
15 ## Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA.
18 ## @deftypefn {Function File} {} plotdecimate (@var{P})
19 ## @deftypefnx {Function File} {} plotdecimate (@var{P}, @var{so})
20 ## @deftypefnx {Function File} {} plotdecimate (@var{P}, @var{so}, @var{res})
21 ## Optimise plot data by removing redundant points and segments
23 ## The first parameter @var{P} is a two-column matrix to be plotted as X and
24 ## Y coordinates. The second optional argument @var{so} disables segment
25 ## optimisation when set to @var{false} (default is @var{true}). The third
26 ## optional argument @var{res} is the size of the largest error on the plot:
27 ## if it is a scalar, it is meant relative to the range of X and Y values
28 ## (default 1e-3); if it is a 2x1 array, it contains the absolute errors for
29 ## X and Y. Returns a two-column matrix containing a subset of the rows of
30 ## @var{P}. A line plot of @var{P} has the same appearance as a line plot of
31 ## the output, with errors smaller than @var{res}. When creating point
32 ## plots, set @var{so} to @var{false}.
35 function C = plotdecimate (P, so, res)
37 if (!ismatrix(P) || columns(P) != 2)
38 error("P must be a matrix with two columns");
41 so = true; # do segment optimisation
44 res = 1e-3; # default resolution is 1000 dots/axis
47 ## Slack: admissible error on coordinates on the output plot
50 error("res must be positive");
52 E = range(P)' * res; # build error vector using range of data
53 elseif (ismatrix(res))
54 if (!all(size(res) == [2 1]) || any(res <= 0))
55 error("res must be a 2x1 matrix with positive values");
57 E = res; # take error vector as it is
59 error("res should be a scalar or matrix");
64 return; # nothing to do
66 P ./= repmat(E',rows(P),1); # normalize P
67 rot = [0,-1;1,0]; # rotate a vector pi/4 anticlockwise
69 ## Iteratively remove points too near to the previous point
71 V = [true; sumsq(diff(P),2) > 1]; # points far from the previous ones
72 if (all(V)) break; endif
73 V = [true; diff(V) >= 0]; # identify the sequence leaders
74 P = P(V,:); # remove them
77 ## Remove points laying near to a segment: for each segment R->S, build a
78 ## unitary-lenght projection vector D perpendicular to R->S, and project
79 ## R->T over D to compute the distance ot T from R->S.
80 if (so) # segment optimisation
81 ## For each segment, r and s are its extremes
82 r = 1; R = P(1,:)'; # start of segment
83 s = 2; S = P(2,:)'; # end of the segment
84 rebuild = true; # build first projection vector
87 if (rebuild) # build projection vector
88 D = rot*(S-R)/sqrt(sumsq(S-R)); # projection vector for distance
89 rebuild = false; # keep current projection vector
92 T = P(t,:)'; # next point
94 if (abs(sum((T-R).*D)) < 1 # T is aligned
95 && sum((T-R).*(S-R)) > 0) # going forward
96 V(s) = false; # do not plot s
97 else # set a new segment
98 r = s; R = S; # new start of segment
99 rebuild = true; # rebuild projection vector
101 s = t; S = T; # new end of segment
105 C = P(V,:) .* repmat(E',sum(V),1); # denormalize P
109 %! x = [ 0 1 2 3 4 8 8 8 8 8 9 ]';
110 %! y = [ 0 1 1 1 1 1 1 2 3 4 5 ]';
112 %! x1 = [0 1 8 8 9]';
113 %! y1 = [0 1 1 4 5]';
114 %! # optimised for segment plot
116 %! x2 = [ 0 1 2 3 4 8 8 8 8 9 ]';
117 %! y2 = [ 0 1 1 1 1 1 2 3 4 5 ]';
118 %! # double points removed
123 %! # optimised segments
125 %! # double points removed
127 %! assert(plotdecimate(P), P1);
128 %! assert(plotdecimate(P, false), P2);