0

I have a bunch of data points each associated with a time, and I want to render them in an animation where I fade points in and out based on this time. The problem I'm running into is that the points are being rendered out of order. I've reduced the problem down to the minimal failing example below. Any help in figuring out the cause would be greatly appreciated.

import os
import numpy as np   
                               
import matplotlib                               
matplotlib.use('Agg')                        
import matplotlib.pyplot as plt              
from matplotlib.animation import ArtistAnimation
from matplotlib.animation import PillowWriter
from matplotlib.animation import FFMpegWriter


def plot_data_3d_video_time(
        data: np.ndarray,
        steps: np.ndarray,
        directory: str = './',
        filename: str = 'video.webm',
        fps: int = 30):
    """ Produce a video of a 3D scatter plot varying the opacity of the points based on their time step. """
    max_alpha = 1.0                             
                                 
    fig = plt.figure()           
    ax = fig.add_subplot(111, projection='3d')  
    ax.set_xlabel('x')                            
    ax.set_ylabel('y')           
    ax.set_zlabel('z')        
    artists = []                                                               

    for t in range(steps.max()):               
        artists.append([])                                                         

        alpha = steps == t                     
        points = data[alpha,:] # Point that should be rendered                     
        alpha = alpha * max_alpha
        
        print(f'{points}') 

        a = ax.scatter(data[:,0], data[:,1], data[:,2],
                alpha=alpha,
                c='b'
        )
        artists[-1].append(a)

        # This is what I expect to see. Why is it showing a different point?
        artists[-1].append(
            ax.scatter(
                [points[0,0]], [points[0,1]], [points[0,2]],
                c='r', marker='x', s=100
            )
        )

    # Code for saving the video. Can be ignored.
    video_format = filename.split('.')[-1]
    filename = os.path.join(directory, filename)
    animation = ArtistAnimation(plt.gcf(), artists, interval=50, blit=True)
    if video_format == 'gif':
        writer = PillowWriter(fps=fps)
    elif video_format == 'mp4':
        writer = FFMpegWriter(fps=fps)
    elif video_format == 'webm':
        writer = FFMpegWriter(fps=fps, codec='libvpx-vp9')
    else:
        raise ValueError('Unknown video format: {}'.format(video_format))
    animation.save(filename, writer=writer)
    print(f'Saved video to {os.path.abspath(filename)}')
    plt.close()


if __name__ == '__main__':
    # Generate some data
    n = 10
    steps = np.arange(n)
    data0 = np.arange(n)*0#[::-1]
    data1 = np.arange(n)*0#[::-1]
    data2 = np.arange(n)[::-1]
    data = np.stack([data0, data1, data2], axis=1)

    # Plot the data
    plot_data_3d_video_time(data=data, steps=steps, filename='test.gif', fps=5)

In this code, I'm plotting the real data as a blue circle and added a debugging point rendered as a red X. This X is where I expect the blue circle to be. The resulting video from the code above is Broken animation

If I set change the input data by changing the data2 = np.arange(n)[::-1] line to data2 = np.arange(n), then both the blue circle and red X coincide.

Working animation

I haven't been able to find a clear pattern on where it works and where it fails. Other setups where it's reversed:

data0 = np.arange(n)[::-1]         
data1 = np.arange(n)[::-1]                                           
data2 = np.arange(n)[::-1]
data0 = np.arange(n)*0      
data1 = np.arange(n)                                          
data2 = np.arange(n)*0
howard
  • 644
  • 2
  • 7
  • 15

0 Answers0