sequenceDiagram participant Caller as Your Code / animate_activity_log participant GA as generate_animation participant DataIn as data_with_positions (DataFrame) participant Layout as event_position_df (DataFrame) participant Scenario as Scenario Object participant PlotlyEx as Plotly Express participant PlotlyGO as Plotly Graph Objects participant PlotlyFig as Plotly Figure Output Caller->>GA: Call(data_with_positions, layout, scenario, ...) GA->>DataIn: Read positions, icons, minute GA->>Layout: Read base positions, labels GA->>Scenario: Read resource counts (if provided) GA->>PlotlyEx: px.scatter(DataIn, animation_frame='minute_display', ...) PlotlyEx-->>GA: Return base animated Figure alt display_stage_labels is True GA->>PlotlyGO: fig.add_trace(go.Scatter(mode='text', ...)) end alt scenario is not None GA->>PlotlyGO: fig.add_trace(go.Scatter(mode='markers', ...)) # Resource placeholders end alt add_background_image is not None GA->>PlotlyFig: fig.add_layout_image(...) end GA->>PlotlyFig: fig.update_layout(axes, speed, ...) PlotlyFig-->>GA: Return configured Figure GA-->>Caller: Return final_figure
Chapter 6: Action! Making the Movie with generate_animation
Great Scott! In Chapter 5: Prepare for Snapshots, McFly! (reshape_for_animations
& generate_animation_df
), we saw how vidigi
acts like a skilled film crew, taking our raw Event Log (the script) and turning it into a detailed storyboard (full_patient_df_plus_pos
). This storyboard tells us exactly who (Maverick, Goose) is doing what (‘wait_for_simulator’, ‘start_simulator’), when (minute 10, 15, etc.), and precisely where on the screen (‘x_final’, ‘y_final’) they should be, complete with their assigned icon (like giving Johnny 5 his signature look!).
But a storyboard isn’t a movie! We need to actually project these frames onto the screen, make them move smoothly, add the background scenery, and get the VCR (or maybe the Betamax?) rolling. We need the final rendering engine, the movie projector, the director yelling “Action!”
That’s the role of the generate_animation
function! It takes the perfectly prepared snapshot data and uses the power of Plotly Express (think of it as the special effects department, maybe ILM back in the 80s) to create the final, interactive animation. It’s time to bring your data to life!
The Mission: Rolling Film!
Our mission, should we choose to accept it, is to take the full_patient_df_plus_pos
DataFrame – our meticulously prepared sequence of snapshots with precise coordinates – and turn it into a slick, animated visualization. We want to see Maverick and Goose (represented by cool 80s-style emoji icons, naturally) move through the ‘Hangar Deck’, queue in the ‘Ready Room’, use the ‘Simulator Bays’, and finally head to ‘Debriefing’, all smoothly animated over time with a playable timeline. Cue the Top Gun theme!
The Projector: How generate_animation
Works
Think of generate_animation
as the high-tech projector in the back of the cinema. It knows how to take the film reel (full_patient_df_plus_pos
) and display it frame by frame, creating the illusion of motion.
Its core mechanism relies on plotly.express.scatter
:
- Input Data: It receives the
full_patient_df_plus_pos
DataFrame, which has columns likeminute
(orminute_display
for formatted time),patient
,x_final
,y_final
, andicon
. This is the complete set of instructions for where each actor (icon) should be in every frame (minute). - Plotly Magic: It calls
plotly.express.scatter
, telling it:- Use
x_final
andy_final
for the position of each point. - Use
icon
as the text label for each point (making the points themselves invisible, so we only see the emoji!). - Use
minute_display
as theanimation_frame
. This tells Plotly to create a separate frame for each unique value in this column and add a slider to control time. - Use
patient
as theanimation_group
. This tells Plotly that rows with the samepatient
ID across different frames represent the same object moving, allowing Plotly to create smooth transitions between positions. It’s like telling the computer that the ‘Maverick’ icon in frame 10 is the same dude as the ‘Maverick’ icon in frame 11, just potentially in a different spot.
- Use
- Adding Static Layers: After Plotly creates the basic animated scatter plot,
generate_animation
adds extra, non-moving elements:- Stage Labels: If
display_stage_labels=True
, it adds text labels (from the Layout Configuration (event_position_df
)) at the specified base coordinates, like putting up signs for “Hangar Deck” or “Simulator Bays”. - Resource Placeholders: If a
scenario
object is provided, it uses the resource counts (e.g.,g.n_simulators
) and the layout information to draw placeholder icons (often light blue circles, but customizable!) for all available resource slots (like ‘Sim_1’, ‘Sim_2’). This lets you see empty bays waiting for pilots. It’s like showing the empty parking spots in the FLAG Mobile Unit for K.I.T.T. and K.A.R.R. - Background Image: If you provide
add_background_image
, it stretches your image across the plot area, like projecting a cool background matte painting from Labyrinth or the digital grid from Tron.
- Stage Labels: If
- Final Touches: It configures the plot’s appearance (hiding axes, setting height/width, adjusting animation speed) and returns the final Plotly Figure object.
Using the Projector: Code Example
You usually won’t call generate_animation
directly. The main animate_activity_log
function calls it for you after preparing the data with reshape_for_animations
and generate_animation_df
. However, understanding its signature helps you know what customization options are passed through.
Here’s a conceptual call, assuming data_ready_for_animation
(the output from generate_animation_df
) and layout_df
(our event_position_df
) exist:
import plotly.graph_objects as go
from vidigi.animation import generate_animation # Import the function
# Assume 'data_ready_for_animation' DataFrame exists (output of generate_animation_df)
# Assume 'layout_df' DataFrame exists (our event_position_df)
# Assume 'g' scenario object exists with g.n_simulators = 2
# --- This function is usually called internally by animate_activity_log ---
= generate_animation(
final_figure =data_ready_for_animation, # The movie reel
full_patient_df_plus_pos=layout_df, # Needed for labels/resource positions
event_position_df=g, # Needed for resource placeholders
scenario=600, # Make the screen shorter
plotly_height=30, # Bigger icons! RADICAL!
icon_and_text_size=True, # Show "Simulator Bays" etc.
display_stage_labels="path/to/your/awesome_grid.png", # Optional Tron background
add_background_image=500, # Slow down the frames a bit (milliseconds)
frame_duration=500 # Smoother transitions
frame_transition_duration
)
# If you called it directly, you'd show it like this:
# final_figure.show() # Uncomment to display!
print("Plotly Figure generated! Ready to rock and roll!")
This call tells generate_animation
to take the prepared data, use the layout for context, show resource placeholders based on the scenario
, customize the appearance, and return the finished Plotly Figure object, ready to be displayed.
Under the Hood: Inside the Projection Booth
Let’s peek inside generate_animation
without needing K.I.T.T.’s X-ray scanner.
Step-by-Step:
- Receive Inputs: Gets the prepared data (
full_patient_df_plus_pos
), layout (event_position_df
), scenario, and all the customization parameters. - Calculate Boundaries: Determines the X and Y axis ranges for the plot based on the layout or overrides.
- Format Time: If
time_display_units
is set (e.g., ‘dhm’), it converts the rawminute
numbers into nicely formatted date/time strings for the animation slider, storing them inminute_display
. It keeps the originalminute
column for sorting. It’s like adding subtitles to the film. - Core Animation (
px.scatter
): This is the main event! It callsplotly.express.scatter
, passing the prepared data and mapping columns:x="x_final"
y="y_final"
animation_frame="minute_display"
animation_group="patient"
text="icon"
(withopacity=0
for the underlying marker)hover_name
,hover_data
for interactivity. Plotly Express does the heavy lifting here, creating the base figure with animated points and the time slider. It’s like the core rendering engine turning vectors into pixels on the Tron grid.
- Add Stage Labels (
go.Scatter
): If enabled, it iterates through theevent_position_df
and adds a staticgo.Scatter
trace withmode="text"
to display thelabel
at the basex
,y
coordinates. - Add Resource Placeholders (
go.Scatter
): If ascenario
is provided, it calculates the positions for all resource slots (using the basex
,y
from the layout,gap_between_resources
,gap_between_rows
, andwrap_resources_at
). It then adds another staticgo.Scatter
trace (eithermode="markers"
for default circles ormode="markers+text"
ifcustom_resource_icon
is used) to show these placeholders. It’s like drawing the empty docking bays on Red Dwarf’s status screen. - Add Background (
add_layout_image
): If an image path is provided, it adds the image to the layout, stretched to fit the plot area. - Configure Layout: Updates the figure’s layout: sets height/width, hides axes and gridlines (unless
setup_mode=True
), ensures the play button is configured (or removed), and sets the animation frame/transition durations (frame_duration
,frame_transition_duration
). - Return Figure: Returns the fully constructed and configured
plotly.graph_objs.Figure
object.
Sequence Diagram:
Code Snippets (Simplified from vidigi/animation.py
):
The core animation command:
# --- Inside generate_animation (Simplified) ---
import plotly.express as px
# (Assume data_ready_for_animation has columns:
# 'x_final', 'y_final', 'minute_display', 'patient', 'icon', 'event', ...)
= px.scatter(
fig 'minute'), # Sort by time!
data_ready_for_animation.sort_values(="x_final",
x="y_final",
y="minute_display", # Tells Plotly how to make frames
animation_frame="patient", # Tells Plotly how to connect dots
animation_group="icon", # Display the emoji icon
text="event", # Show event name on hover
hover_name# ... other parameters like hover_data, ranges, height ...
=0 # Make the underlying point invisible
opacity
)# Status: Base animation created! Like the raw footage.
# --- End Snippet ---
This single call creates the moving emojis and the time slider. Everything else builds on this foundation.
Adding static stage labels:
# --- Inside generate_animation (Simplified) ---
import plotly.graph_objects as go
# (Assume 'layout_df' has columns 'x', 'y', 'label')
if display_stage_labels:
fig.add_trace(go.Scatter(=layout_df['x'], # Base X coordinates
x=layout_df['y'], # Base Y coordinates
y="text", # We just want text, no markers
mode="", # No legend entry needed
name=layout_df['label'], # The labels to display
text="middle right", # Position relative to x,y
textposition='none' # Don't show hover info for labels
hoverinfo
))# Status: Added signs like "Welcome to Hill Valley".
# --- End Snippet ---
Adding static resource placeholder icons:
# --- Inside generate_animation (Simplified) ---
import plotly.graph_objects as go
import pandas as pd
# (Assume 'events_with_resources' DataFrame is calculated with columns:
# 'x_final', 'y_final' for each resource slot position)
if scenario is not None:
# (Calculation of 'events_with_resources' positions omitted for brevity)
# ... calculates x_final, y_final for each resource slot ...
fig.add_trace(go.Scatter(=events_with_resources['x_final'],
x=[i - 10 for i in events_with_resources['y_final']], # Slightly offset Y
y="markers", # Draw markers (default: circles)
mode=dict(
marker='LightSkyBlue',
color=15), # Appearance of markers
size=resource_opacity, # Make them semi-transparent
opacity='none' # No hover needed
hoverinfo
))# Status: Added placeholders for empty simulator bays. Number 5 is... available!
# --- End Snippet ---
Adding the background image:
# --- Inside generate_animation (Simplified) ---
# (Assume 'add_background_image' holds the file path string)
if add_background_image is not None:
fig.add_layout_image(dict(
=add_background_image, # Path to the image file
source="x domain", yref="y domain", # Stretch across axes
xref=1, y=1, sizex=1, sizey=1, # Cover the whole area
x="right", yanchor="top",
xanchor="stretch", # Stretch to fit
sizing=0.5, # Make it slightly transparent
opacity="below" # Draw it underneath everything else
layer
)
)# Status: Laid down the background, maybe the entrance to Jareth's Labyrinth!
# --- End Snippet ---
These snippets show how generate_animation
builds the final plot layer by layer, starting with the core animation from Plotly Express and adding static elements using Plotly Graph Objects.
Conclusion: That’s a Wrap!
You’ve reached the end of the production line! generate_animation
is the final function in the vidigi
animation pipeline, the projector that takes the meticulously prepared snapshot data (full_patient_df_plus_pos
) and renders it into a dynamic, interactive Plotly animation. It leverages Plotly Express for the core animation and adds essential static layers like stage labels, resource placeholders, and background images.
This function, along with its partners reshape_for_animations
and generate_animation_df
, does the heavy lifting behind the scenes when you call the main animate_activity_log
function. Together, they turn your raw simulation Event Log and Layout Configuration (event_position_df
) into a visual story, letting you see your process unfold like never before – hopefully without disrupting the space-time continuum!
You now understand the key components and the flow of data required to create vidigi
animations. Go forth and visualize your processes – make them totally awesome, to the max!
Generated by AI Codebase Knowledge Builder