def make_animation_frames(name, semimajor, planet_radius, star_radius, star_color, orbital_period):
    #not affiliated with Animal Planet.

    center = np.array([0.5,0.5])
    angle = np.random.uniform(0, 2*np.pi)
    planet_center = center+semimajor*np.array([1,0])

    fig = Figure(figsize=(6,6))
    axis = fig.add_subplot(1, 1, 1)

    star = Circle(center, radius=star_radius, color=star_color)
    orbit = Circle(center, radius=semimajor, fill=False, linestyle='dashed', color='gray')
    planet = Circle(planet_center, radius=planet_radius, color='green')

    axis.add_patch(star)
    axis.add_patch(orbit)
    axis.add_patch(planet)
    axis.axis('off')

    filenames = []
    #set 50 days to be 5 seconds
    orbital_period = orbital_period/100.*5.
    #let's put the period in seconds
    fps = 100.
    print 'orbital period = ', orbital_period
    nframe = int(orbital_period*fps)


    #round off 
    orbital_period = nframe/fps
    omega = 2*np.pi/orbital_period
    angles = np.linspace(0, 2*np.pi, nframe)
    if not os.path.exists("frames"):
        os.makekdir("frames")
    if not os.path.exists("gifs"):
        os.makekdir("gifss")
    print angles
    print "nframe = ", nframe
    for i,theta in enumerate(angles):
        canvas = FigureCanvas(fig)
        planet.center = center+semimajor*np.array([np.cos(theta),np.sin(theta)])
        filename = ("frames/%.4d_"%i)+remove_space(name)+'.png'
        fig.savefig(filename)
        filenames.append(filename)

    #animate
    gifname = "gifs/%s.gif" % remove_space(name)
    frame_names = " ".join(filenames)
    cmd = "convert -delay 1 %s %s" % (frame_names, gifname)
    print cmd   
    os.system(cmd)
Esempio n. 2
0
from matplotlib import animation
from matplotlib.patches import Circle
from math import sin
from random import randint
#plt.rcParams['animation.ffmpeg_path']='D:\\Apps\\ffmpeg\\ffmpeg-20171031-88c7aa1-win64-static\\bin\\ffmpeg.exe'

dpi = 96
noiseIm = mpim.imread('resources/simnoiseoriginal.png')
fig = plt.figure(figsize=(250 / dpi, 250 / dpi), dpi=dpi)
plt.ion()
im = plt.imshow(noiseIm)
plt.axis('off')
ax = plt.gca()
ax.set_position([0, 0, 1, 1])
circ = Circle((125, 175), 10)
ax.add_patch(circ)

FFMpegWriter = animation.writers['ffmpeg']
writer = FFMpegWriter(fps=30, bitrate=50000)

with writer.saving(fig, 'output/simdata.mp4', dpi * 2):
    for i in range(600):
        circ.center = (125 + i, 175 + i + 50 * sin(i / 50))
        r1 = randint(-5, 5)
        r2 = randint(-5, 5)
        plt.xlim(i + r1, 250 + i + r2)
        plt.ylim(50 + i + r1 + 50 * sin(i / 50),
                 300 + i + r2 + 50 * sin(i / 50))
        fig.canvas.draw()
        writer.grab_frame()
Esempio n. 3
0
def plot_video_with_sphere_cylinder(
    rods_history: Sequence[Dict],
    cylinders_history: Sequence[Dict],
    sphere_history: Sequence[Dict],
    video_name="video.mp4",
    fps=60,
    step=1,
    **kwargs,
):  # (time step, x/y/z, node)
    import matplotlib.animation as manimation
    from mpl_toolkits.mplot3d import proj3d, Axes3D

    plt.rcParams.update({"font.size": 22})

    # Cylinders first
    n_visualized_cylinders = len(cylinders_history)
    # n_cyl, n_time, 3,
    # cylinder_com = np.array([x["com"] for x in cylinders_history])
    # n_cyl floats
    cylinder_heights = [x["height"] for x in cylinders_history]
    cylinder_radii = [x["radius"] for x in cylinders_history]
    # sim_time = np.array(cylinders_history[0]["time"])
    sim_time = np.array(rods_history[0]["time"])

    cylinder_cmap = cm.get_cmap("Spectral", n_visualized_cylinders)

    # Rods next
    n_visualized_rods = len(rods_history)

    # TODO : Should be a generator rather a function
    rod_history_unpacker = lambda rod_idx, t_idx: (
        rods_history[rod_idx]["position"][time_idx],
        rods_history[rod_idx]["radius"][t_idx],
    )
    com_history_unpacker = lambda rod_idx, t_idx: rods_history[rod_idx]["com"][time_idx]
    cylinder_history_unpacker = lambda cyl_idx, t_idx: (
        cylinders_history[cyl_idx]["com"][t_idx]
        - 0.5
        * cylinder_heights[cyl_idx]
        * cylinders_history[cyl_idx]["direction"].reshape(3),
        cylinder_radii[cyl_idx],
        cylinder_heights[cyl_idx],
    )

    # Spheres next
    n_visualized_spheres = len(sphere_history)  # should be one for now
    # Sphere radius
    # sphere_radii = [x["radius"] for x in sphere_history]
    # Sphere info
    sphere_history_unpacker = lambda sph_idx, t_idx: (
        sphere_history[sph_idx]["position"][t_idx],
        sphere_history[sph_idx]["radius"][t_idx],
    )
    # color mapping
    sphere_cmap = cm.get_cmap("Spectral", n_visualized_spheres)

    print("Plotting videos!")
    FFMpegWriter = manimation.writers["ffmpeg"]
    metadata = dict(title="Movie Test", artist="Matplotlib", comment="Movie support!")
    writer = FFMpegWriter(fps=fps, metadata=metadata)
    dpi = kwargs.get("dpi", 100)

    def make_data_for_cylinder_along_z(cstart, cradius, cheight):
        center_x, center_y = cstart[0], cstart[2]
        z = np.linspace(0, cheight, 3)
        theta = np.linspace(0, 2 * np.pi, 25)
        theta_grid, z_grid = np.meshgrid(theta, z)
        x_grid = cradius * np.cos(theta_grid) + center_x
        y_grid = cradius * np.sin(theta_grid) + center_y
        z_grid += cstart[1]
        return [x_grid, y_grid, z_grid]

    xlim = kwargs.get("x_limits", (-1.0, 1.0))
    ylim = kwargs.get("y_limits", (-1.0, 1.0))
    zlim = kwargs.get("z_limits", (-0.05, 1.0))
    difference = lambda x: x[1] - x[0]
    max_axis_length = max(difference(xlim), difference(ylim))
    # The scaling factor from physical space to matplotlib space
    scaling_factor = (2 * 0.1) / max_axis_length  # Octopus head dimension
    scaling_factor *= 2.6e3  # Along one-axis

    if kwargs.get("vis3D", True):
        fig = plt.figure(1, figsize=(10, 8), frameon=True, dpi=dpi)
        ax = plt.axes(projection="3d")
        ax.set_xlabel("x")
        ax.set_ylabel("y")
        ax.set_zlabel("z")

        ax.set_xlim(*xlim)
        ax.set_ylim(*ylim)
        ax.set_zlim(*zlim)
        ax.view_init(elev=20, azim=180)

        # Surfaces (cylinders, spheres) first
        time_idx = 0
        cylinder_surfs = [None for _ in range(n_visualized_cylinders)]

        for cylinder_idx in range(n_visualized_cylinders):
            XC, YC, ZC = make_data_for_cylinder_along_z(
                *cylinder_history_unpacker(cylinder_idx, time_idx)
            )
            cylinder_surfs[cylinder_idx] = ax.plot_surface(
                XC, YC, ZC, color=cylinder_cmap(cylinder_idx), alpha=1.0
            )

        # Rods next
        rod_scatters = [None for _ in range(n_visualized_rods)]

        for rod_idx in range(n_visualized_rods):
            inst_position, inst_radius = rod_history_unpacker(rod_idx, time_idx)
            inst_position = 0.5 * (inst_position[..., 1:] + inst_position[..., :-1])
            rod_scatters[rod_idx] = ax.scatter(
                inst_position[0],
                inst_position[2],
                inst_position[1],
                s=np.pi * inst_radius ** 2 * 1e4,
            )

        # sphere surfaces
        sphere_artists = [None for _ in range(n_visualized_spheres)]
        for sphere_idx in range(n_visualized_spheres):
            sphere_position, sphere_radius = sphere_history_unpacker(
                sphere_idx, time_idx
            )
            sphere_artists[sphere_idx] = ax.scatter(
                sphere_position[0],
                sphere_position[2],
                sphere_position[1],
                s=np.pi * (scaling_factor * sphere_radius) ** 2,
            )
            # sphere_radius,
            # color=sphere_cmap(sphere_idx),)
            ax.add_artist(sphere_artists[sphere_idx])

        # min_limits = global_rot_mat @ np.array([0.0, -0.5 * cylinder_height, 0.0])
        # min_limits = -np.abs(min_limits)
        # max_limits = min_limits + cylinder_height

        with writer.saving(fig, video_name, dpi):
            with plt.style.context("seaborn-whitegrid"):
                for time_idx in tqdm(range(0, sim_time.shape[0], int(step))):
                    for rod_idx in range(n_visualized_rods):
                        inst_position, inst_radius = rod_history_unpacker(
                            rod_idx, time_idx
                        )
                        inst_position = 0.5 * (
                            inst_position[..., 1:] + inst_position[..., :-1]
                        )
                        rod_scatters[rod_idx]._offsets3d = (
                            inst_position[0],
                            inst_position[2],
                            inst_position[1],
                        )
                        rod_scatters[rod_idx].set_sizes(np.pi * inst_radius ** 2 * 1e4)

                    for cylinder_idx in range(n_visualized_cylinders):
                        XC, YC, ZC = make_data_for_cylinder_along_z(
                            *cylinder_history_unpacker(cylinder_idx, time_idx)
                        )
                        cylinder_surfs[cylinder_idx].remove()
                        cylinder_surfs[cylinder_idx] = ax.plot_surface(
                            XC, YC, ZC, color=cylinder_cmap(cylinder_idx), alpha=1.0
                        )

                    for sphere_idx in range(n_visualized_spheres):
                        sphere_position, _ = sphere_history_unpacker(
                            sphere_idx, time_idx
                        )
                        sphere_artists[sphere_idx]._offsets3d = (
                            sphere_position[0],
                            sphere_position[2],
                            sphere_position[1],
                        )

                    writer.grab_frame()

        # Delete all variables within scope
        # Painful
        del (rod_scatters, cylinder_surfs)
        del (time_idx,)  # rod_idx, cylinder_idx
        # del inst_position, inst_radius
        # del XC, YC, ZC

        # Be a good boy and close figures
        # https://stackoverflow.com/a/37451036
        # plt.close(fig) alone does not suffice
        # See https://github.com/matplotlib/matplotlib/issues/8560/
        plt.close(plt.gcf())

    if kwargs.get("vis2D", True):
        from matplotlib.patches import Circle

        fig = plt.figure(2, figsize=(10, 8), frameon=True, dpi=dpi)
        ax = fig.add_subplot(111)
        ax.set_xlim(*xlim)
        ax.set_ylim(*ylim)

        time_idx = 0
        rod_lines = [None for _ in range(n_visualized_rods)]
        rod_com_lines = [None for _ in range(n_visualized_rods)]
        rod_scatters = [None for _ in range(n_visualized_rods)]
        for rod_idx in range(n_visualized_rods):
            inst_position, inst_radius = rod_history_unpacker(rod_idx, time_idx)
            inst_position = 0.5 * (inst_position[..., 1:] + inst_position[..., :-1])
            rod_lines[rod_idx] = ax.plot(
                inst_position[0], inst_position[2], "r", lw=0.5
            )[0]
            inst_com = com_history_unpacker(rod_idx, time_idx)
            rod_com_lines[rod_idx] = ax.plot(inst_com[0], inst_com[2], "k--", lw=2.0)[0]

            rod_scatters[rod_idx] = ax.scatter(
                inst_position[0],
                inst_position[2],
                s=np.pi * (scaling_factor * inst_radius) ** 2,
            )

        # min_limits = np.array([0.0, -0.5 * cylinder_height, 0.0])
        # max_limits = min_limits + cylinder_height

        # ax.set_xlim([min_limits[0], max_limits[0]])
        # ax.set_ylim([min_limits[1], max_limits[1]])

        cylinder_artists = [None for _ in range(n_visualized_cylinders)]
        for cylinder_idx in range(n_visualized_cylinders):
            cylinder_origin, cylinder_radius, _ = cylinder_history_unpacker(
                cylinder_idx, time_idx
            )

            cylinder_artists[cylinder_idx] = Circle(
                (cylinder_origin[0], cylinder_origin[2]),
                cylinder_radius,
                color=cylinder_cmap(cylinder_idx),
            )
            ax.add_artist(cylinder_artists[cylinder_idx])

        sphere_artists = [None for _ in range(n_visualized_spheres)]
        for sphere_idx in range(n_visualized_spheres):
            sphere_position, sphere_radius = sphere_history_unpacker(
                sphere_idx, time_idx
            )
            sphere_artists[sphere_idx] = Circle(
                (sphere_position[0], sphere_position[2]),
                sphere_radius,
                color=sphere_cmap(sphere_idx),
            )
            ax.add_artist(sphere_artists[sphere_idx])

        ax.set_aspect("equal")
        video_name = "2D_" + video_name

        with writer.saving(fig, video_name, dpi):
            with plt.style.context("seaborn-whitegrid"):
                for time_idx in tqdm(range(0, sim_time.shape[0], int(step))):

                    for rod_idx in range(n_visualized_rods):
                        inst_position, inst_radius = rod_history_unpacker(
                            rod_idx, time_idx
                        )
                        inst_position = 0.5 * (
                            inst_position[..., 1:] + inst_position[..., :-1]
                        )

                        rod_lines[rod_idx].set_xdata(inst_position[0])
                        rod_lines[rod_idx].set_ydata(inst_position[2])

                        com = com_history_unpacker(rod_idx, time_idx)
                        rod_com_lines[rod_idx].set_xdata(com[0])
                        rod_com_lines[rod_idx].set_ydata(com[2])

                        # rod_scatters[rod_idx].set_offsets(inst_position[:2].T)
                        rod_scatters[rod_idx].set_offsets(
                            np.vstack((inst_position[0], inst_position[2])).T
                        )
                        rod_scatters[rod_idx].set_sizes(
                            np.pi * (scaling_factor * inst_radius) ** 2
                        )

                    for cylinder_idx in range(n_visualized_cylinders):
                        cylinder_origin, _, _ = cylinder_history_unpacker(
                            cylinder_idx, time_idx
                        )
                        cylinder_artists[cylinder_idx].center = (
                            cylinder_origin[0],
                            cylinder_origin[2],
                        )

                    for sphere_idx in range(n_visualized_spheres):
                        sphere_position, _ = sphere_history_unpacker(
                            sphere_idx, time_idx
                        )
                        sphere_artists[sphere_idx].center = (
                            sphere_position[0],
                            sphere_position[2],
                        )

                    writer.grab_frame()

        # Be a good boy and close figures
        # https://stackoverflow.com/a/37451036
        # plt.close(fig) alone does not suffice
        # See https://github.com/matplotlib/matplotlib/issues/8560/
        plt.close(plt.gcf())
Esempio n. 4
0
vrep.simxGetObjectOrientation(clientID, robotHandle, stageHandle,
                              vrep.simx_opmode_streaming)
vrep.simxGetVisionSensorDepthBuffer(clientID, sensorHandle,
                                    vrep.simx_opmode_streaming)

try:
    while vrep.simxGetConnectionId(clientID) != -1 and not grid.finish:
        # read sensor position
        returnCode, position = vrep.simxGetObjectPosition(
            clientID, sensorHandle, stageHandle, vrep.simx_opmode_buffer)
        if returnCode == vrep.simx_error_noerror:
            (x, y, _) = position
            # adjust by stage size
            x, y = (x - stageMinX) / stageWidth, (y - stageMinY) / stageHeight
            robot.update_position((x, y, None))
            robotCircleToken.center = grid.position((x, y))

        # read robot orientation
        returnCode, orientation = vrep.simxGetObjectOrientation(
            clientID, robotHandle, stageHandle, vrep.simx_opmode_buffer)
        if returnCode == vrep.simx_error_noerror:
            (_, _, angle) = orientation
            robot.update_angle(angle)

        # read depth data
        returnCode, resolution, data = vrep.simxGetVisionSensorDepthBuffer(
            clientID, sensorHandle, vrep.simx_opmode_buffer)
        if returnCode == vrep.simx_error_noerror:
            # get positions from rays
            walls = robot.gen_wall_position(data, stageWidth, stageHeight)
            if len(walls) == 0:
Esempio n. 5
0
        elif len(positions) >= 1:
            collided_points.append(pointi)
            possible_coords.append(positions[0])
        elif len(possible_coords) > 0:
            break

    prev_pointi = collided_points[0] if len(collided_points) > 0 else 1

    plane = np.array([np.cos(car.state[2]), np.sin(car.state[2])])
    best_coords = sorted(possible_coords,
                         key=lambda x: -plane.dot(np.array(x) - plane))

    if len(best_coords) >= 1:
        update_pursuit(best_coords[0])

    print('-----------------------')

    plt.pause(0.0000000001)
    line.set_data(xa, ya)

    circle.center = car.state[0], car.state[1]
    circle.set_radius(lookahead)

    # newpath = list(map(lambda x: [
    # 	(x[0] - car.state[0]) * np.cos(np.pi / 2 - car.state[2]) - (x[1] - car.state[1]) * np.sin(np.pi / 2 - car.state[2]) - car.state[0],
    # 	(x[1] - car.state[1]) * np.cos(np.pi / 2 - car.state[2]) + (x[0] - car.state[0]) * np.sin(np.pi / 2 - car.state[2]) - car.state[1]
    # ], path))
    # track.set_data([x[0] for x in newpath], [x[1] for x in newpath])

plt.show()
def Visualize(Date,
              PlotID=1,
              FPS=15,
              Steps=20,
              MP4_quality=300,
              Name="LSST Scheduler Simulator.mp4",
              showClouds=True):

    # Import data
    All_Fields = np.loadtxt("NightDataInLIS/Constants/fieldID.lis",
                            unpack=True)
    N_Fields = len(All_Fields[0])

    if showClouds:
        Time_slots = np.loadtxt("NightDataInLIS/TimeSlots{}.lis".format(
            int(ephem.julian_date(Date))),
                                unpack=True)
        All_Cloud_cover = np.loadtxt("NightDataInLIS/Clouds{}.lis".format(
            int(ephem.julian_date(Date))),
                                     unpack=True)

    Site = ephem.Observer()
    Site.lon = -1.2320792
    Site.lat = -0.517781017
    Site.elevation = 2650
    Site.pressure = 0.
    Site.horizon = 0.

    #Initialize date and time
    lastN_start = float(Date) - 1
    lastN_end = float(Date)
    toN_start = float(Date)
    toN_end = float(Date) + 1

    #Connect to the History data base
    con = lite.connect('FBDE.db')
    cur = con.cursor()

    # Prepare to save in MP4 format
    FFMpegWriter = animation.writers['ffmpeg']
    metadata = dict(title='LSST Simulation', artist='Elahe', comment='Test')
    writer = FFMpegWriter(fps=FPS, metadata=metadata)

    #Progress bar initialization

    pbar = ProgressBar()

    # Initialize plot
    Fig = plt.figure()
    if PlotID == 1:
        ax = plt.subplot(111, axisbg='black')
    if PlotID == 2:
        ax = plt.subplot(211, axisbg='black')

    unobserved, Observed_lastN, Obseved_toN,\
    ToN_History_line, last_10_History_line,\
    Horizon, airmass_horizon, S_Pole,\
    LSST,\
    Clouds\
        = ax.plot([], [], '*',[], [], '*',[], [], '*',
                  [], [], '-',[], [], '*',
                  [], [], '-',[], [], '-',[], [], 'D',
                  [], [], 'o',
                  [], [], 'o')

    ax.set_xlim(-1.5, 1.5)
    ax.set_ylim(-1.5, 1.5)
    ax.set_aspect('equal', adjustable='box')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # Coloring
    Horizon.set_color('white')
    airmass_horizon.set_color('red')
    S_Pole.set_markersize(3)
    S_Pole.set_markerfacecolor('red')
    star_size = 4

    unobserved.set_color('dimgray')
    unobserved.set_markersize(star_size)
    Observed_lastN.set_color('blue')
    Observed_lastN.set_markersize(star_size)
    Obseved_toN.set_color('chartreuse')
    Obseved_toN.set_markersize(star_size + 2)
    Clouds.set_color('white')
    Clouds.set_markersize(10)

    ToN_History_line.set_color('orange')
    ToN_History_line.set_lw(.5)
    last_10_History_line.set_color('red')
    last_10_History_line.set_lw(.5)

    LSST.set_color('red')
    LSST.set_markersize(8)

    if PlotID == 2:
        freqAX = plt.subplot(212)
        cur.execute(
            'SELECT N_visit, Last_visit, Second_last_visit, Third_last_visit, Fourth_last_visit From FieldsStatistics'
        )
        row = cur.fetchall()
        N_visit = [x[0] for x in row]
        Last_visit = [x[1] for x in row]
        Second_last_visit = [x[2] for x in row]
        Third_last_visit = [x[3] for x in row]
        Fourth_last_visit = [x[4] for x in row]

        initHistoricalcoverage = N_visit
        for index, id in enumerate(All_Fields):
            if Last_visit[index] > toN_start:
                initHistoricalcoverage[index] -= 1
                if Second_last_visit[index] > toN_start:
                    initHistoricalcoverage[index] -= 1
                    if Third_last_visit > toN_start:
                        initHistoricalcoverage[index] -= 1

        covering, current_cover = freqAX.plot(All_Fields[0],
                                              initHistoricalcoverage, '-', [],
                                              [], 'o')

        freqAX.set_xlim(0, N_Fields)
        freqAX.set_ylim(0, np.max(initHistoricalcoverage) + 5)
        covering.set_color('chartreuse')
        covering.set_markersize(2)
        current_cover.set_color('red')
        current_cover.set_markersize(6)

    cur.execute(
        'SELECT Night_count, T_start, T_end FROM NightSummary WHERE T_start BETWEEN (?) AND (?)',
        (toN_start, toN_end))
    row = cur.fetchone()
    vID = row[0]
    t_start = row[1]
    t_end = row[2]
    t = t_start

    # Figure labels and fixed elements
    Phi = np.arange(0, 2 * np.pi, 0.05)
    Horizon.set_data(1.01 * np.cos(Phi), 1.01 * np.sin(Phi))
    ax.text(-1.3, 0, 'West', color='white', fontsize=7)
    ax.text(1.15, 0, 'East', color='white', fontsize=7)
    ax.text(0, 1.1, 'North', color='white', fontsize=7)
    airmass_horizon.set_data(
        np.cos(np.pi / 4) * np.cos(Phi),
        np.cos(np.pi / 4) * np.sin(Phi))
    ax.text(-.3,
            0.6,
            'Acceptable airmass horizon',
            color='white',
            fontsize=5,
            fontweight='bold')
    Alt, Az = Fields_local_coordinate(180, -90, t, Site)
    x, y = AltAz2XY(Alt, Az)
    S_Pole.set_data(x, y)
    ax.text(x + .05, y, 'S-Pole', color='white', fontsize=7)

    # Observed last night fields
    cur.execute(
        'SELECT Field_id FROM Schedule WHERE ephemDate BETWEEN (?) AND (?)',
        (lastN_start, lastN_end))
    row = cur.fetchall()
    if row is not None:
        F1 = [x[0] for x in row]
    else:
        F1 = []

    # Tonight observation path
    cur.execute(
        'SELECT Field_id, ephemDate FROM Schedule WHERE ephemDate BETWEEN (?) AND (?)',
        (toN_start, toN_end))
    row = cur.fetchall()
    if row[0][0] is not None:
        F2 = [x[0] for x in row]
        F2_timing = [x[1] for x in row]
    else:
        F2 = []
        F2_timing = []

    # Sky elements
    Moon = Circle((0, 0), 0, color='silver', zorder=3)
    ax.add_patch(Moon)
    Moon_text = ax.text([], [], 'Moon', color='white', fontsize=7)

    with writer.saving(Fig, Name, MP4_quality):
        for t in pbar(np.linspace(t_start, t_end, num=Steps)):

            # Find the index of the current time
            time_index = 0
            while t > F2_timing[time_index]:
                time_index += 1
            if showClouds:
                Slot_n = 0
                while t > Time_slots[Slot_n]:
                    Slot_n += 1

            visit_index = 0
            visited_field = 0

            # Object fields: F1)Observed last night F2)Observed tonight F3)Unobserved F4)Covered by clouds
            F1_X = []
            F1_Y = []
            F2_X = []
            F2_Y = []
            F3_X = []
            F3_Y = []
            F4_X = []
            F4_Y = []

            # F1  coordinate:
            for i in F1:
                Alt, Az = Fields_local_coordinate(All_Fields[1, i - 1],
                                                  All_Fields[2,
                                                             i - 1], t, Site)
                if Alt > 0:
                    X, Y = AltAz2XY(Alt, Az)
                    F1_X.append(X)
                    F1_Y.append(Y)

            # F2  coordinate:
            for i, tau in zip(F2, F2_timing):
                Alt, Az = Fields_local_coordinate(All_Fields[1, i - 1],
                                                  All_Fields[2,
                                                             i - 1], t, Site)
                if Alt > 0:
                    X, Y = AltAz2XY(Alt, Az)
                    F2_X.append(X)
                    F2_Y.append(Y)
                    if t >= tau:
                        visit_index = len(F2_X) - 1
                        visited_field = i

            # F3  coordinate:
            for i in range(0, N_Fields):
                if True:
                    Alt, Az = Fields_local_coordinate(All_Fields[1, i],
                                                      All_Fields[2,
                                                                 i], t, Site)
                    if Alt > 0:
                        X, Y = AltAz2XY(Alt, Az)
                        F3_X.append(X)
                        F3_Y.append(Y)

            # F4 coordinates
            if showClouds:
                for i in range(0, N_Fields):
                    if All_Cloud_cover[i, Slot_n] == 1:
                        Alt, Az = Fields_local_coordinate(
                            All_Fields[1, i], All_Fields[2, i], t, Site)
                    if Alt > 0:
                        X, Y = AltAz2XY(Alt, Az)
                        F4_X.append(X)
                        F4_Y.append(Y)

            # Update plot
            unobserved.set_data([F3_X, F3_Y])
            Observed_lastN.set_data([F1_X, F1_Y])
            Obseved_toN.set_data([F2_X[0:visit_index], F2_Y[0:visit_index]])

            ToN_History_line.set_data(
                [F2_X[0:visit_index], F2_Y[0:visit_index]])
            last_10_History_line.set_data([
                F2_X[visit_index - 10:visit_index],
                F2_Y[visit_index - 10:visit_index]
            ])
            LSST.set_data([F2_X[visit_index], F2_Y[visit_index]])
            Clouds.set_data([F4_X, F4_Y])

            # Update Moon
            X, Y, r, alt = update_moon(t, Site)
            Moon.center = X, Y
            Moon.radius = r
            if alt > 0:
                #Moon.set_visible(True)
                Moon_text.set_visible(True)
                Moon_text.set_x(X + .002)
                Moon_text.set_y(Y + .002)
            else:
                Moon.set_visible(False)
                Moon_text.set_visible(False)

            #Update coverage
            if PlotID == 2:
                Historicalcoverage = np.zeros(N_Fields)
                for i, tau in zip(F2, F2_timing):
                    if tau <= t:
                        Historicalcoverage[i - 1] += 1
                    else:
                        break
                tot = Historicalcoverage + initHistoricalcoverage
                current_cover.set_data(visited_field - 1,
                                       tot[visited_field - 1])
                covering.set_data(All_Fields[0], tot)

            #Observation statistics
            leg = plt.legend([Observed_lastN, Obseved_toN],
                             ['Visited last night', time_index])
            for l in leg.get_texts():
                l.set_fontsize(6)
            date = ephem.date(t)
            Fig.suptitle('Top view of the LSST site on {}, GMT'.format(date))
            '''
            # progress
            perc= int(100*(t - t_start)/(t_end - t_start))
            if perc <= 100:
                print('{} %'.format(perc))
            else:
                print('100 %')
            '''
            #Save current frame
            writer.grab_frame()
def visualize(night,
              file_name,
              PlotID=1,
              FPS=15,
              Steps=20,
              MP4_quality=300,
              Name="Visualization.mp4",
              showClouds=False,
              nside=64,
              fancy_slow=True):

    Site = ephem.Observer()
    Site.lon = -1.2320792
    Site.lat = -0.517781017
    Site.elevation = 2650
    Site.pressure = 0.
    Site.horizon = 0.

    # create pix
    hpid = np.arange(hp.nside2npix(16))
    ra, dec = _hpid2RaDec(16, hpid)
    SCP_indx, NES_indx, GP_indx, WFD_indx = mutually_exclusive_regions(
        nside=16)
    DD_indx = []

    #Initialize date and time
    last_night = night - 1

    #Connect to the History data base
    con = lite.connect(file_name)
    cur = con.cursor()

    # Prepare to save in MP4 format
    FFMpegWriter = animation.writers['ffmpeg']
    metadata = dict(title='LSST Simulation', artist='Elahe', comment='Test')
    writer = FFMpegWriter(fps=FPS, metadata=metadata)

    # Initialize plot
    Fig = plt.figure()
    if PlotID == 1:
        ax = plt.subplot(111, axisbg='black')
    if PlotID == 2:
        ax = plt.subplot(211, axisbg='black')

    unobserved, Observed_lastN, Obseved_toN,\
    WFD, GP, NE, SE, DD,\
    ToN_History_line,\
    uu,gg,rr,ii,zz,yy,\
    last_10_History_line,\
    Horizon, airmass_horizon, S_Pole,\
    LSST,\
    Clouds\
        = ax.plot([], [], '*',[], [], '*',[], [], '*',
                  [], [], '*',[], [], '*',[], [], '*', [], [], '*', [], [], '*', # regions
                  [], [], '*',
                  [], [], '*',[], [], '*',[], [], '*',
                  [], [], '*',[], [], '*',[], [], '*',
                  [], [], '-',
                  [], [], '-',[], [], '-',[], [], 'D',
                  [], [], 'o',
                  [], [], 'o')

    ax.set_xlim(-1.5, 1.5)
    ax.set_ylim(-1.5, 1.5)
    ax.set_aspect('equal', adjustable='box')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # Coloring
    Horizon.set_color('white')
    airmass_horizon.set_color('red')
    S_Pole.set_markersize(3)
    S_Pole.set_markerfacecolor('red')
    star_size = 4

    unobserved.set_color('dimgray')
    unobserved.set_markersize(star_size)
    Observed_lastN.set_color('blue')
    Observed_lastN.set_markersize(star_size)
    Obseved_toN.set_color('chartreuse')
    Obseved_toN.set_markersize(0)

    # regions
    WFD.set_color('black')
    WFD.set_alpha(0.3)
    SE.set_color('orange')
    SE.set_alpha(0.3)
    NE.set_color('orange')
    NE.set_alpha(0.3)
    GP.set_color('orange')
    GP.set_alpha(0.3)
    DD.set_color('orange')
    DD.set_alpha(0.3)
    DD.set_markersize(7)

    # filters
    uu.set_color('purple')
    gg.set_color('green')
    rr.set_color('red')
    ii.set_color('orange')
    zz.set_color('pink')
    yy.set_color('deeppink')

    # clouds
    Clouds.set_color('white')
    Clouds.set_markersize(10)
    Clouds.set_alpha(0.2)
    Clouds.set_markeredgecolor(None)

    ToN_History_line.set_color('orange')
    ToN_History_line.set_lw(.5)
    last_10_History_line.set_color('gray')
    last_10_History_line.set_lw(.5)

    LSST.set_color('red')
    LSST.set_markersize(8)

    if PlotID == 2:
        freqAX = plt.subplot(212)
        cur.execute(
            'SELECT N_visit, Last_visit, Second_last_visit, Third_last_visit, Fourth_last_visit From FieldsStatistics'
        )
        row = cur.fetchall()
        N_visit = [x[0] for x in row]
        Last_visit = [x[1] for x in row]
        Second_last_visit = [x[2] for x in row]
        Third_last_visit = [x[3] for x in row]
        Fourth_last_visit = [x[4] for x in row]

        initHistoricalcoverage = N_visit
        for index, id in enumerate(All_Fields):
            if Last_visit[index] > toN_start:
                initHistoricalcoverage[index] -= 1
                if Second_last_visit[index] > toN_start:
                    initHistoricalcoverage[index] -= 1
                    if Third_last_visit > toN_start:
                        initHistoricalcoverage[index] -= 1

        covering, current_cover = freqAX.plot(All_Fields[0],
                                              initHistoricalcoverage, '-', [],
                                              [], 'o')

        freqAX.set_xlim(0, N_Fields)
        freqAX.set_ylim(0, np.max(initHistoricalcoverage) + 5)
        covering.set_color('chartreuse')
        covering.set_markersize(2)
        current_cover.set_color('red')
        current_cover.set_markersize(6)

    # Figure labels and fixed elements
    Phi = np.arange(0, 2 * np.pi, 0.05)
    Horizon.set_data(1.01 * np.cos(Phi), 1.01 * np.sin(Phi))
    ax.text(-1.3, 0, 'East', color='white', fontsize=7)
    ax.text(1.15, 0, 'West', color='white', fontsize=7)
    ax.text(0, 1.1, 'North', color='white', fontsize=7)
    airmass_horizon.set_data(
        np.cos(np.pi / 4) * np.cos(Phi),
        np.cos(np.pi / 4) * np.sin(Phi))
    ax.text(-.3,
            0.6,
            'airmass horizon',
            color='white',
            fontsize=5,
            fontweight='bold')
    Alt, Az = Fields_local_coordinate(180, -90, 59581.0381944435, Site)
    x, y = AltAz2XY(Alt, Az)
    S_Pole.set_data(x, y)
    ax.text(x + .05, y, 'S-Pole', color='white', fontsize=7)
    DD_indicator = ax.text(-1.4,
                           1.3,
                           'Deep Drilling Observation',
                           color='red',
                           fontsize=9,
                           visible=False)
    WFD_indicator = ax.text(-1.4,
                            1.3,
                            'Wide Fast Deep Observation',
                            color='white',
                            fontsize=9,
                            visible=False)
    GP_indicator = ax.text(-1.4,
                           1.3,
                           'Galactic Plane Observation',
                           color='white',
                           fontsize=9,
                           visible=False)
    NES_indicator = ax.text(-1.4,
                            1.3,
                            'Notrh Ecliptic Spur Observation',
                            color='white',
                            fontsize=9,
                            visible=False)
    SCP_indicator = ax.text(-1.4,
                            1.3,
                            'South Celestial Pole Observation',
                            color='white',
                            fontsize=9,
                            visible=False)

    # Observed last night fields
    cur.execute(
        'SELECT RA, dec, mjd, filter FROM SummaryAllProps WHERE night=%i' %
        (last_night))
    row = cur.fetchall()
    if row is not None:
        F1 = [x[0:2] for x in row]
    else:
        F1 = []

    # Tonight observation path
    cur.execute(
        'SELECT RA, dec, mjd, filter FROM SummaryAllProps WHERE night=%i' %
        (night))
    row = cur.fetchall()
    if row[0][0] is not None:
        F2 = [x[0:2] for x in row]
        RA = np.asanyarray([x[0] for x in row])
        DEC = np.asarray([x[1] for x in row])
        F2_timing = [x[2] for x in row]
        F2_filtering = [x[3] for x in row]
        F2_region = RaDec2region(RA, DEC, nside)
    else:
        F2 = []
        F2_timing = []
        F2_filtering = []
        F2_region = []

    # Sky elements
    Moon = Circle((0, 0), 0, color='silver', zorder=3)
    ax.add_patch(Moon)
    Moon_text = ax.text([], [], 'Moon', color='white', fontsize=7)

    doff = ephem.Date(0) - ephem.Date('1858/11/17')
    with writer.saving(Fig, Name, MP4_quality):
        for t in np.linspace(F2_timing[0], F2_timing[-1], num=Steps):

            # Find the index of the current time
            time_index = 0
            while t > F2_timing[time_index]:
                time_index += 1
            if showClouds:
                Slot_n = 0
                while t > Time_slots[Slot_n]:
                    Slot_n += 1

            visit_index = 0
            visited_field = 0
            visit_index_u = 0
            visit_index_g = 0
            visit_index_r = 0
            visit_index_i = 0
            visit_index_z = 0
            visit_index_y = 0
            visit_filter = 'r'

            # Object fields: F1)Observed last night F2)Observed tonight F3)Unobserved F4)Covered by clouds
            F1_X = []
            F1_Y = []
            F2_X = []
            F2_Y = []
            F3_X = []
            F3_Y = []
            F4_X = []
            F4_Y = []
            # Filter coloring for tonight observation
            U_X = []
            U_Y = []
            G_X = []
            G_Y = []
            R_X = []
            R_Y = []
            I_X = []
            I_Y = []
            Z_X = []
            Z_Y = []
            Y_X = []
            Y_Y = []
            # Coloring different proposals
            WFD_X = []
            WFD_Y = []
            NE_X = []
            NE_Y = []
            SE_X = []
            SE_Y = []
            GP_X = []
            GP_Y = []
            DD_X = []
            DD_Y = []

            # F1  coordinate:
            for i in F1:
                Alt, Az = Fields_local_coordinate(i[0], i[1], t - doff, Site)
                if Alt > 0:
                    X, Y = AltAz2XY(Alt, Az)
                    F1_X.append(X)
                    F1_Y.append(Y)

            # F2  coordinate:
            for i, tau, filter in zip(F2, F2_timing, F2_filtering):
                Alt, Az = Fields_local_coordinate(i[0], i[1], t - doff, Site)
                if Alt > 0:
                    X, Y = AltAz2XY(Alt, Az)
                    F2_X.append(X)
                    F2_Y.append(Y)
                    if filter == 'u':
                        U_X.append(X)
                        U_Y.append(Y)
                        if t >= tau:
                            visit_index_u = len(U_X) - 1
                    elif filter == 'g':
                        G_X.append(X)
                        G_Y.append(Y)
                        if t >= tau:
                            visit_index_g = len(G_Y) - 1
                    elif filter == 'r':
                        R_X.append(X)
                        R_Y.append(Y)
                        if t >= tau:
                            visit_index_r = len(R_Y) - 1
                    elif filter == 'i':
                        I_X.append(X)
                        I_Y.append(Y)
                        if t >= tau:
                            visit_index_i = len(I_Y) - 1
                    elif filter == 'z':
                        Z_X.append(X)
                        Z_Y.append(Y)
                        if t >= tau:
                            visit_index_z = len(Z_Y) - 1
                    elif filter == 'y':
                        Y_X.append(X)
                        Y_Y.append(Y)
                        if t >= tau:
                            visit_index_y = len(Y_Y) - 1

                    if t >= tau:
                        visit_index = len(F2_X) - 1
                        visited_field = i
                        visit_filter = filter

            # F3  coordinate:
            if fancy_slow:
                Alt, Az = stupidFast_RaDec2AltAz(ra, dec, t)
                for al, az, i in zip(Alt, Az, hpid):
                    if al > 0:
                        X, Y = AltAz2XY(al, az)
                        F3_X.append(X)
                        F3_Y.append(Y)
                        if i in DD_indx:
                            DD_X.append(X)
                            DD_Y.append(Y)
                        elif i in WFD_indx:
                            WFD_X.append(X)
                            WFD_Y.append(Y)
                        elif i in GP_indx:
                            GP_X.append(X)
                            GP_Y.append(Y)
                        elif i in NES_indx:
                            NE_X.append(X)
                            NE_Y.append(Y)
                        elif i in SCP_indx:
                            SE_X.append(X)
                            SE_Y.append(Y)

            # F4 coordinates
            if showClouds:
                for i in range(0, N_Fields):
                    if All_Cloud_cover[Slot_n, i] == 2 or All_Cloud_cover[
                            Slot_n, i] == 1 or All_Cloud_cover[Slot_n,
                                                               i] == -1:
                        Alt, Az = Fields_local_coordinate(
                            i[0], i[1], t - doff, Site)
                    if Alt > 0:
                        X, Y = AltAz2XY(Alt, Az)
                        F4_X.append(X)
                        F4_Y.append(Y)

            # Update plot
            unobserved.set_data([F3_X, F3_Y])
            Observed_lastN.set_data([F1_X, F1_Y])
            Obseved_toN.set_data([F2_X[0:visit_index], F2_Y[0:visit_index]])

            # filters
            uu.set_data([U_X[0:visit_index_u], U_Y[0:visit_index_u]])
            gg.set_data([G_X[0:visit_index_g], G_Y[0:visit_index_g]])
            rr.set_data([R_X[0:visit_index_r], R_Y[0:visit_index_r]])
            ii.set_data([I_X[0:visit_index_i], I_Y[0:visit_index_i]])
            zz.set_data([Z_X[0:visit_index_z], Z_Y[0:visit_index_z]])
            yy.set_data([Y_X[0:visit_index_y], Y_Y[0:visit_index_y]])

            ToN_History_line.set_data(
                [F2_X[0:visit_index], F2_Y[0:visit_index]])
            last_10_History_line.set_data([
                F2_X[visit_index - 10:visit_index],
                F2_Y[visit_index - 10:visit_index]
            ])

            # telescope position and color
            LSST.set_data([F2_X[visit_index], F2_Y[visit_index]])
            if visit_filter == 'u':
                LSST.set_color('purple')
            if visit_filter == 'g':
                LSST.set_color('green')
            if visit_filter == 'r':
                LSST.set_color('red')
            if visit_filter == 'i':
                LSST.set_color('orange')
            if visit_filter == 'z':
                LSST.set_color('pink')
            if visit_filter == 'y':
                LSST.set_color('deeppink')

            Clouds.set_data([F4_X, F4_Y])

            # regions
            WFD.set_data([WFD_X, WFD_Y])
            DD.set_data([DD_X, DD_Y])
            NE.set_data([NE_X, NE_Y])
            SE.set_data([SE_X, SE_Y])
            GP.set_data([GP_X, GP_Y])

            # Update Moon
            X, Y, r, alt = update_moon(t, Site)
            Moon.center = X, Y
            Moon.radius = r
            if alt > 0:
                #Moon.set_visible(True)
                Moon_text.set_visible(True)
                Moon_text.set_x(X + .002)
                Moon_text.set_y(Y + .002)
            else:
                Moon.set_visible(False)
                Moon_text.set_visible(False)

            #Update coverage
            if PlotID == 2:
                Historicalcoverage = np.zeros(N_Fields)
                for i, tau in zip(F2, F2_timing):
                    if tau <= t:
                        Historicalcoverage[i - 1] += 1
                    else:
                        break
                tot = Historicalcoverage + initHistoricalcoverage
                current_cover.set_data(visited_field - 1,
                                       tot[visited_field - 1])
                covering.set_data(All_Fields[0], tot)

            #Update indicators of the proposal
            if F2_region[time_index] == 'DD':
                DD_indicator.set_visible(True)
            else:
                DD_indicator.set_visible(False)
            if F2_region[time_index] == 'WFD':
                WFD_indicator.set_visible(True)
            else:
                WFD_indicator.set_visible(False)
            if F2_region[time_index] == 'GP':
                GP_indicator.set_visible(True)
            else:
                GP_indicator.set_visible(False)
            if F2_region[time_index] == 'NES':
                NES_indicator.set_visible(True)
            else:
                NES_indicator.set_visible(False)
            if F2_region[time_index] == 'SCP':
                SCP_indicator.set_visible(True)
            else:
                SCP_indicator.set_visible(False)

            #Observation statistics
            leg = plt.legend([Observed_lastN, Obseved_toN],
                             ['Visited last night', time_index])
            for l in leg.get_texts():
                l.set_fontsize(6)
            date = t
            Fig.suptitle('Top view of the LSST site on {}, GMT'.format(date))
            '''
            # progress
            perc= int(100*(t - t_start)/(t_end - t_start))
            if perc <= 100:
                print('{} %'.format(perc))
            else:
                print('100 %')
            '''
            #Save current frame
            writer.grab_frame()
Esempio n. 8
0
def plot_video_with_sphere_2D(
    rods_history: Sequence[Dict],
    sphere_history: Sequence[Dict],
    video_name="video_2D.mp4",
    fps=60,
    step=1,
    vis2D=True,
    **kwargs,
):
    plt.rcParams.update({"font.size": 22})

    # 2d case <always 2d case for now>
    import matplotlib.animation as animation
    from matplotlib.patches import Circle

    # simulation time
    sim_time = np.array(sphere_history[0]["time"])

    # Sphere
    n_visualized_spheres = len(sphere_history)  # should be one for now
    # Sphere radius
    sphere_radii = [x["radius"] for x in sphere_history]
    # Sphere info
    sphere_history_unpacker = lambda sph_idx, t_idx: (
        sphere_history[sph_idx]["position"][t_idx],
        sphere_history[sph_idx]["radius"][t_idx],
    )

    # color mapping
    sphere_cmap = cm.get_cmap("Spectral", n_visualized_spheres)

    # Rod
    n_visualized_rods = len(rods_history)  # should be one for now
    # Rod info
    rod_history_unpacker = lambda rod_idx, t_idx: (
        rods_history[rod_idx]["position"][t_idx],
        rods_history[rod_idx]["radius"][t_idx],
    )

    # Rod center of mass
    com_history_unpacker = lambda rod_idx, t_idx: rods_history[rod_idx]["com"][time_idx]

    # video pre-processing
    print("plot scene visualization video")
    FFMpegWriter = animation.writers["ffmpeg"]
    metadata = dict(title="Movie Test", artist="Matplotlib", comment="Movie support!")
    writer = FFMpegWriter(fps=fps, metadata=metadata)
    dpi = kwargs.get("dpi", 100)

    xlim = kwargs.get("x_limits", (-1.1, 1.1))
    ylim = kwargs.get("y_limits", (-1.1, 1.1))
    zlim = kwargs.get("z_limits", (-0.05, 1.0))

    difference = lambda x: x[1] - x[0]
    max_axis_length = max(difference(xlim), difference(ylim))
    # The scaling factor from physical space to matplotlib space
    scaling_factor = (2 * 0.1) / max_axis_length  # Octopus head dimension
    scaling_factor *= 2.6e3  # Along one-axis

    fig = plt.figure(2, figsize=(10, 8), frameon=True, dpi=dpi)
    ax = fig.add_subplot(111)
    ax.set_xlim(*xlim)
    ax.set_ylim(*ylim)

    sphere_history_directors = lambda sph_idx, t_idx: (
        sphere_history[sph_idx]["directors"][t_idx],
    )
    rod_history_directors = lambda sph_idx, t_idx: (
        rods_history[sph_idx]["directors"][t_idx],
    )
    rod_directors1 = [None for _ in range(n_visualized_rods)]
    rod_directors2 = [None for _ in range(n_visualized_rods)]
    rod_directors3 = [None for _ in range(n_visualized_rods)]
    sphere_directors1 = [None for _ in range(n_visualized_spheres)]
    sphere_directors2 = [None for _ in range(n_visualized_spheres)]
    sphere_directors3 = [None for _ in range(n_visualized_spheres)]

    time_idx = 0
    rod_lines = [None for _ in range(n_visualized_rods)]
    rod_com_lines = [None for _ in range(n_visualized_rods)]
    rod_scatters = [None for _ in range(n_visualized_rods)]
    for rod_idx in range(n_visualized_rods):
        inst_position, inst_radius = rod_history_unpacker(rod_idx, time_idx)
        inst_position = 0.5 * (inst_position[..., 1:] + inst_position[..., :-1])
        rod_lines[rod_idx] = ax.plot(inst_position[0], inst_position[1], "r", lw=0.5)[0]
        inst_com = com_history_unpacker(rod_idx, time_idx)
        rod_com_lines[rod_idx] = ax.plot(inst_com[0], inst_com[1], "k--", lw=2.0)[0]

        rod_scatters[rod_idx] = ax.scatter(
            inst_position[0],
            inst_position[1],
            s=np.pi * (scaling_factor * inst_radius) ** 2,
        )

        rod_dir = rod_history_directors(rod_idx, time_idx)
        scale = 0.2
        dir1 = rod_dir[0][..., -1][0] * scale
        dir2 = rod_dir[0][..., -1][1] * scale
        dir3 = rod_dir[0][..., -1][2] * scale
        tip_position = inst_position[..., -1]
        rod_directors1[rod_idx] = ax.plot(
            [tip_position[0], tip_position[0] + dir1[0]],
            [tip_position[1], tip_position[1] + dir1[1]],
            "r-",
            lw=2.0,
        )[0]
        rod_directors2[rod_idx] = ax.plot(
            [tip_position[0], tip_position[0] + dir2[0]],
            [tip_position[1], tip_position[1] + dir2[1]],
            "g-",
            lw=2.0,
        )[0]
        rod_directors3[rod_idx] = ax.plot(
            [tip_position[0], tip_position[0] + dir3[0]],
            [tip_position[1], tip_position[1] + dir3[1]],
            "b-",
            lw=2.0,
        )[0]

    sphere_artists = [None for _ in range(n_visualized_spheres)]
    for sphere_idx in range(n_visualized_spheres):
        sphere_position, sphere_radius = sphere_history_unpacker(sphere_idx, time_idx)
        sphere_artists[sphere_idx] = Circle(
            (sphere_position[0], sphere_position[1]),
            sphere_radius,
            color=sphere_cmap(sphere_idx),
        )
        ax.add_artist(sphere_artists[sphere_idx])
        sphere_dir = sphere_history_directors(sphere_idx, time_idx)
        scale = 0.2
        dir1 = sphere_dir[0][0] * scale
        dir2 = sphere_dir[0][1] * scale
        dir3 = sphere_dir[0][2] * scale
        sphere_directors1[sphere_idx] = ax.plot(
            [sphere_position[0], sphere_position[0] + dir1[0]],
            [sphere_position[1], sphere_position[1] + dir1[1]],
            "r-",
            lw=2.0,
        )[0]
        sphere_directors2[sphere_idx] = ax.plot(
            [sphere_position[0], sphere_position[0] + dir2[0]],
            [sphere_position[1], sphere_position[1] + dir2[1]],
            "g-",
            lw=2.0,
        )[0]
        sphere_directors3[sphere_idx] = ax.plot(
            [sphere_position[0], sphere_position[0] + dir3[0]],
            [sphere_position[1], sphere_position[1] + dir3[1]],
            "b-",
            lw=2.0,
        )[0]

    ax.set_aspect("equal")
    video_name = "2D_" + video_name

    with writer.saving(fig, video_name, dpi):
        with plt.style.context("seaborn-whitegrid"):
            for time_idx in tqdm(range(0, sim_time.shape[0], int(step))):

                for rod_idx in range(n_visualized_rods):
                    inst_position, inst_radius = rod_history_unpacker(rod_idx, time_idx)
                    inst_position = 0.5 * (
                        inst_position[..., 1:] + inst_position[..., :-1]
                    )

                    rod_lines[rod_idx].set_xdata(inst_position[0])
                    rod_lines[rod_idx].set_ydata(inst_position[1])

                    com = com_history_unpacker(rod_idx, time_idx)
                    rod_com_lines[rod_idx].set_xdata(com[0])
                    rod_com_lines[rod_idx].set_ydata(com[1])

                    rod_scatters[rod_idx].set_offsets(inst_position[:2].T)
                    rod_scatters[rod_idx].set_sizes(
                        np.pi * (scaling_factor * inst_radius) ** 2
                    )

                    rod_dir = rod_history_directors(sphere_idx, time_idx)
                    dir1 = rod_dir[0][..., -1][0] * scale
                    dir2 = rod_dir[0][..., -1][1] * scale
                    dir3 = rod_dir[0][..., -1][2] * scale
                    tip_position = inst_position[..., -1]
                    rod_directors1[rod_idx].set_xdata(
                        [tip_position[0], tip_position[0] + dir1[0]]
                    )
                    rod_directors1[rod_idx].set_ydata(
                        [tip_position[1], tip_position[1] + dir1[1]]
                    )

                    rod_directors2[rod_idx].set_xdata(
                        [tip_position[0], tip_position[0] + dir2[0]]
                    )
                    rod_directors2[rod_idx].set_ydata(
                        [tip_position[1], tip_position[1] + dir2[1]]
                    )

                    rod_directors3[rod_idx].set_xdata(
                        [tip_position[0], tip_position[0] + dir3[0]]
                    )
                    rod_directors3[rod_idx].set_ydata(
                        [tip_position[1], tip_position[1] + dir3[1]]
                    )

                for sphere_idx in range(n_visualized_spheres):
                    sphere_position, _ = sphere_history_unpacker(sphere_idx, time_idx)
                    sphere_artists[sphere_idx].center = (
                        sphere_position[0],
                        sphere_position[1],
                    )

                    sphere_dir = sphere_history_directors(sphere_idx, time_idx)
                    dir1 = sphere_dir[0][0] * scale
                    dir2 = sphere_dir[0][1] * scale
                    dir3 = sphere_dir[0][2] * scale

                    sphere_directors1[sphere_idx].set_xdata(
                        [sphere_position[0], sphere_position[0] + dir1[0]]
                    )
                    sphere_directors1[sphere_idx].set_ydata(
                        [sphere_position[1], sphere_position[1] + dir1[1]]
                    )

                    sphere_directors2[sphere_idx].set_xdata(
                        [sphere_position[0], sphere_position[0] + dir2[0]]
                    )
                    sphere_directors2[sphere_idx].set_ydata(
                        [sphere_position[1], sphere_position[1] + dir2[1]]
                    )

                    sphere_directors3[sphere_idx].set_xdata(
                        [sphere_position[0], sphere_position[0] + dir3[0]]
                    )
                    sphere_directors3[sphere_idx].set_ydata(
                        [sphere_position[1], sphere_position[1] + dir3[1]]
                    )
                writer.grab_frame()