Как сделать предыдущие входы постепенно исчезать на графике Matlab, когда я добавляю новые входы

Скажем, у меня есть эта очень простая петля

for i=1:10
    [xO, yO, xA, yA, xB, yB, xC, yC] = DoSomething(i);
    line([xO,xA,xB,xC],[yO,yA,yB,yC]);
    pause(0.1);
end

Координаты, которые я рисую, соответствуют суставам многоярусной системы, и я имитирую их позиции с течением времени (см. Образец сюжета здесь):

Periodic plot

Поскольку некоторые из ссылок перемещаются периодическим образом, становится путаным отслеживать визуально движение. По этой причине теперь возникает вопрос: как я могу построить линии таким образом, чтобы при построении новой строки предыдущие строки постепенно исчезали? Другими словами, так, что у меня есть градиент от самых последних отложенных данных (наиболее непрозрачных) до самых старых данных (все более прозрачный, пока он полностью не исчезнет).

Таким образом, когда новая строка рисуется в том же положении, что и очень старые данные, я заметил, что она новая.

Ответ 1

Я добавляю второй ответ, чтобы четко разделить два совершенно разных подхода. Мой первый ответ использует недокументированную (и по состоянию на 2018b, амортизированную) прозрачность для линий.

Этот ответ предлагает другой подход для рисования линий, который не имеет проблем с совместимостью (эти две "функции" могут быть реализованы независимо):

  • Создайте фиксированные n строк и обновляйте их положение, а не создавайте растущее количество линий.
  • Перекрасьте линии, выцветая до белого, а не меняя прозрачность.

Вот код, смотрите комментарии для деталей:

% "Buffer" size, number of historic lines to keep, and governs the 
% corresponding fade increments.
nFade = 100;

% Set up some demo values for plotting around a circle
dt = 0.05; a = 0:dt:2*pi+(dt*nFade); n = numel(a); b = a.*4;
[x1,y1] = pol2cart( a, ones(1,n) ); [x2,y2] = pol2cart( b, 0.4*ones(1,n) ); 
x = [zeros(1,n); x1; x1+x2]; y = [zeros(1,n); y1; y1+y2]; 

% Initialise the figure, set up axes etc
f = figure(1); clf; xlim([-1.5,1.5]); ylim([-1.5,1.5]);

% Draw all of the lines, initially not showing because NaN vs NaN
lines = arrayfun( @(x)line(NaN,NaN), 1:nFade, 'uni', 0 );
% Set up shorthand for recolouring all the lines
recolour = @(lines) arrayfun( @(x) set( lines{x},'Color',ones(1,3)*(x/nFade) ), 1:nFade );

for ii = 1:n
    % Shift the lines around so newest is at the start
    lines = [ lines(end), lines(1:end-1) ]; 
    % Overwrite x/y data for oldest line to be newest line
    set( lines{1}, 'XData', x(:,ii), 'YData', y(:,ii) );
    % Update all colours
    recolour( lines );           
    % Pause for animation           
    pause(0.01);
end

Результат:

demo

Ответ 2

Вы можете сделать это, изменив атрибут 4-го Color прошлых линий.

Вот демо-результат, где я потушил 10% прозрачности каждого кадра, так что видны только самые последние 10 строк.

fade plot demo

Вот код, смотрите мои комментарии для деталей:

% Set up some demo values for plotting around a circle
a = 0:0.1:2*pi; n = numel(a);
[x,y] = pol2cart( a, ones(1,n) );

% Initialise the figure, set up axes etc
f = figure(1); clf; xlim([-1,1]); ylim([-1,1]);
% Array of graphics objects to store the lines. Could use a cell array.
lines = gobjects( 1, n );
% "Buffer" size, number of historic lines to keep, and governs the 
% corresponding fade increments.
nFade = 10;

% Main plotting loop
for ii = 1:n
    % Plot the line
    lines(ii) = line( [0,x(ii)], [0,y(ii)] );
    % Loop over past lines.
    % Note that we only need to go back as far as ii-nFade, earlier lines
    % will already by transparent with this method!
    for ip = max(1,ii-nFade):ii
        % Set the 4th Color attribute value (the alpha) as a percentage
        % from the current index. Could do this various ways.
        lines(ip).Color(4) = max( 0, 1 - (ii-ip)/nFade );
    end
    % Delay for animation
    pause(0.1);
end

Возможно, вы захотите сделать некоторые управление графиком/памятью, если у вас много строк. Вы можете удалить прозрачные линии, добавив что-то вроде

if lines(ii).Color(4) < 0.01
    delete(lines(ii));
end

В рамках цикла. Таким образом, ваша фигура не будет иметь множество прозрачных остатков.


Заметки:

  • Я сгенерировал реальный GIF, используя imwrite на случай, если это тоже интересно.
  • По-видимому, в R2018b амортизировано значение "4-го значения цвета" (не уверен, что оно когда-либо было официально задокументировано).

Получил достаточно голосов, чтобы мотивировать сделать немного более веселую демонстрацию...

fader

Ответ 3

Решение для Matlab 2018a или более поздней версии (или ранее, не позднее 2012a)

Поскольку четвертый параметр цвета в качестве альфа-значения больше не поддерживается в Matlab 2018a (и, видимо, никогда не предполагалось, как указывал Крис Луенго), здесь решение, которое работает в Matlab 2018a с использованием функции patchline из обмена файлами (предоставлено Brett Shoelson).

% init the figure
figure(); axes();
hold on; xlim([-1 0.5]); ylim([0 1]);

% set fraction of alpha value to take
alpha_fraction = 0.7;
n_iterations = 200;

% looping variable to prevent deleting and calling already deleted lines
% i.e. to keep track of which lines are already deleted
delete_from = 1;

for i=1:n_iterations
    % your x, y data
    [x, y] = doSomething(i);

    % create line with transparency using patchline
    p(i) = patchline(x,y, 'linewidth', 1, 'edgecolor', 'k');

    % set alpha of line to fraction of previous alpha value
    % only do when first line is already plotted
    if i > 1
        % loop over all the previous created lines up till this iteration
        % when it still exists (delete from that index)
        for j = delete_from:i-1       
            % Update the alpha to be a fraction of the previous alpha value
            p(j).EdgeAlpha = p(j).EdgeAlpha*alpha_fraction;

            % delete barely visible lines
            if p(j).EdgeAlpha < 0.01 && delete_from > j
                delete(p(j));
                % exclude deleted line from loop, so edgealpha is not
                % called again
                delete_from = j;
            end
        end
    end
    % pause and behold your mechanism 
    pause(0.1);
end

Я включил удаление едва видимых линий, как предложено @Wolfie (моя собственная, возможно, менее элегантная реализация)

И вот демонстрация механизма быстрого выпуска:

Quick-release-mechanism

Ответ 4

Вам нужно отслеживать созданные линии, а затем изменять их цвет (на линиях нет канала альфа/транскрипции)

nKeep=8;
L=[];

vanish = @(rgb) (rgb+[.5 .5 .5])./1.5;
for i=1:20
    %Draw some random line
    ang=rand*2*pi;
    a = cos(ang)*[-1 1];
    b = sin(ang)*[-1 1];
    thisLine=line(a,b); xlim([-1.5 1.5]),ylim([-1.5 1.5]);

    %Now, keep track of the new line in an array
    if i>nKeep
        delete(L(nKeep));
        L=[thisLine;L(1:end-1)];
    else
        L=[thisLine;L];
    end
    C=get(L(2:end),{'Color'});
    % Some function that change a color to a clearer one
    C=cellfun(vanish,C,'UniformOutput',false);
    set(L(2:end),{'Color'},C);
    pause(0.3);
end

Другим вариантом было бы создать непосредственно массив line с исчезающими цветами, сохранить X, Y для построения в массиве ячеек, который обновляется на каждой итерации (circshift), а затем обновлять свойства Xdata и Ydata объектов линии.

nKeep=8;
%initialize vector of lines with their color
for j=nKeep:-1:1
    L(j) = line(NaN,NaN,'Color',[(j-1)/nKeep (j-1)/nKeep 1]);
end
%initialize XY with NaN
XY=cell(nKeep,2);
XY=cellfun(@(x) NaN,XY,'uniformoutput',false);

for i=1:20
    %Random coordinates of line
    ang=rand*2*pi;
    a = cos(ang)*[-1 1];
    b = sin(ang)*[-1 1];

    %Update XY
    XY=circshift(XY,[1 0]);
    XY(1,:)={a b};

    set(L,{'XData','YData'},XY);
    pause(0.3);
end