sequenceDiagram participant AAL as animate_activity_log (Optional Caller) participant GA as generate_animation (The Projector) participant Data as animation_ready_df (The Film Reel) participant Layout as event_position_df (Stage Map) participant Scenario as scenario object (Resource Info) participant PX as Plotly Express participant PlotlyFig as Plotly Figure (The Movie) AAL->>GA: Call with Data, Layout, Scenario, Options GA->>PX: px.scatter(data=Data, x='x_final', y='y_final', text='icon', animation_frame='minute_display', animation_group='patient', ...) PX-->>GA: Return basic animated figure (fig) GA->>Layout: Get stage label positions GA->>PlotlyFig: fig.add_trace(go.Scatter(...)) # Add stage labels alt Scenario provided GA->>Layout: Get resource base positions & names GA->>Scenario: Get resource counts GA->>PlotlyFig: fig.add_trace(go.Scatter(...)) # Add resource markers end opt Background image provided GA->>PlotlyFig: fig.add_layout_image(...) # Add background end GA->>PlotlyFig: Update layout, styles, animation speed GA-->>AAL: Return final Plotly Figure
Chapter 6: Animation Generation (generate_animation
)
Welcome to the final chapter in our vidigi
tutorial! In Chapter 5: Snapshot Preparation (reshape_for_animations
& generate_animation_df
), we saw how vidigi
transforms our raw Event Log and Layout Configuration (event_position_df
) into a detailed, frame-by-frame dataset. This dataset tells us exactly where each entity (like a patient) should be on the screen, with its specific icon, at every single snapshot in time.
Think of the output from Chapter 5 as a perfectly prepared film reel for a movie. Each frame on the reel shows the precise position of all actors. Now, we need the movie projector to actually display this film reel as a moving picture. That’s the job of the generate_animation
function!
The Movie Projector: Turning Data into Visuals
generate_animation
is the core rendering engine of vidigi
. It takes the “animation-ready” DataFrame prepared in the previous step (the one with minute
, patient
, icon
, x_final
, y_final
, etc.) and uses a powerful plotting library called Plotly Express to create the actual interactive animation.
Its main job is to:
- Draw Each Frame: For each time snapshot (
minute
), it plots each entity’sicon
at its calculatedx_final
andy_final
position. - Create Motion: It tells Plotly how to smoothly transition the icons from their positions in one frame to their positions in the next frame. This is what makes the entities appear to move through the different stages (queues, resources).
- Add Controls & Polish: It adds features like a timeline slider, play/pause buttons, tooltips (text that appears when you hover over an icon), stage labels, and optional background images.
Essentially, generate_animation
is the final artist that takes the detailed blueprint and brings it to life as an interactive animation.
How to Use generate_animation
(Usually Indirectly)
Most of the time, you won’t call generate_animation
directly. Remember the “Easy Button” from Chapter 1: Animation Facade (animate_activity_log
)? That main animate_activity_log
function calls generate_animation
internally as its final step.
However, if you wanted very fine-grained control or were building the animation step-by-step yourself, you could call it directly. You would first need to run the preparation steps from Chapter 5 to get the required input DataFrame.
Let’s imagine we have the final DataFrame from Chapter 5, called animation_ready_df
:
# --- Assume we have this from Chapter 5 ---
# animation_ready_df looks like this (simplified):
# minute patient icon x_final y_final label ...
# 0 0 1 '🧔🏼' 50.0 200.0 Entrance ...
# 1 0 1 '🧔🏼' 200.0 250.0 Waiting Area ...
# 2 5 1 '🧔🏼' 190.0 150.0 Treatment Bays ...
# 3 5 2 '👨🏿🦯' 200.0 250.0 Waiting Area ...
# ...
# --- Assume we also have these from previous chapters ---
# my_layout = pd.DataFrame(...) # From Chapter 3
# class SimpleScenario: n_cubicles = 2
# scenario_details = SimpleScenario() # From Chapter 1/3
# --- Import the function ---
from vidigi.animation import generate_animation
import pandas as pd # We'll use pandas DataFrames
# --- Call generate_animation directly ---
# (Normally done inside animate_activity_log)
= generate_animation(
final_animation =animation_ready_df, # The prepared data!
full_patient_df_plus_pos=my_layout, # Needed for stage labels, resources
event_position_df=scenario_details, # Needed for drawing resource markers
scenario=600, # Set the height of the animation
plotly_height=20, # Make icons smaller
icon_and_text_size='dhm', # Show time nicely
time_display_units=500, # Slow down playback slightly
frame_duration='floorplan.png' # Optional: Add a background
add_background_image
)
# You can now display the animation (e.g., in a Jupyter Notebook)
# final_animation.show()
What happens when you run this?
The variable final_animation
now holds a Plotly Figure object. If you displayed it (e.g., using final_animation.show()
in a Jupyter notebook), you would see the complete, interactive animation:
- Icons (like ‘🧔🏼’ and ‘👨🏿🦯’) representing patients moving between locations defined in
my_layout
. - Smooth transitions between the time steps recorded in
animation_ready_df
. - A slider at the bottom showing the time (formatted as Days/Hours/Minutes).
- Play/Pause buttons.
- Stage labels (“Entrance”, “Waiting Area”, etc.) displayed on the chart.
- Static markers showing the available treatment bays (based on
scenario_details
). - Optionally, the ‘floorplan.png’ image in the background.
What’s Happening Under the Hood?
generate_animation
relies heavily on the plotly.express.scatter
function. Here’s a simplified breakdown of how it works:
- Core Scatter Plot: It calls
px.scatter
, telling it:- Use
animation_ready_df
as the data source. - Plot points at
x="x_final"
andy="y_final"
. - Use the
icon
column as the text marker for each point (text="icon"
). This is how the emojis appear. - Set the
animation_frame
based on the time column (minute_display
). This tells Plotly which rows belong to which frame of the animation. - Set the
animation_group
based on thepatient
column. This tells Plotly that all rows with the samepatient
ID represent the same object moving across frames, allowing for smooth transitions. - Define hover text (
hover_name
,hover_data
) so useful information appears when you mouse over an icon. - Set the plot boundaries (
range_x
,range_y
) and size (height
,width
).
- Use
- Adding Static Layers: After creating the basic animated scatter plot,
generate_animation
adds extra, non-moving layers:- Stage Labels: If
display_stage_labels=True
, it adds anothergo.Scatter
trace (this time non-animated) to display the text labels from theevent_position_df
at their respective (x, y) coordinates. - Resource Markers: If a
scenario
object is provided, it calculates the positions for each individual resource slot (like each treatment bay) based on theevent_position_df
and the resource counts inscenario
. It then adds another staticgo.Scatter
trace to draw markers (like light blue circles or custom icons) at these positions. - Background Image: If
add_background_image
is specified, it usesfig.add_layout_image
to place the image underneath the animation layers.
- Stage Labels: If
- Styling and Controls: Finally, it adjusts the appearance:
- Sets the size of the icon/text markers (
icon_and_text_size
). - Hides axes and gridlines (unless
setup_mode=True
). - Configures the animation player (play button, slider speed using
frame_duration
andframe_transition_duration
). - Returns the complete Plotly
Figure
object.
- Sets the size of the icon/text markers (
Here’s a simplified view of the process:
Code Dive (Simplified):
Looking inside the vidigi/animation.py
file, the heart of generate_animation
is the plotly.express.scatter
call:
# Simplified from vidigi/animation.py
import plotly.express as px
import plotly.graph_objects as go
# ... other imports
def generate_animation(full_patient_df_plus_pos, event_position_df, scenario=None, ...):
# ... (setup code for time display, plot boundaries) ...
# === Core Animation Creation ===
= px.scatter(
fig 'minute'), # Use the prepared data
full_patient_df_plus_pos.sort_values(="x_final", # Horizontal position from data
x="y_final", # Vertical position from data
y="minute_display", # Column defining animation time steps
animation_frame="patient", # Column identifying entities across frames
animation_group="icon", # Column with the emoji/text to display
text="event", # Show event name on hover
hover_name=["patient", "time", "resource_id"], # Show other details
hover_data# ... (ranges, height, width) ...
=0 # Make the actual scatter points invisible (we only see the text)
opacity
)
# === Add Stage Labels (if requested) ===
if display_stage_labels:
fig.add_trace(go.Scatter(=event_position_df['x'] + 10, # Offset slightly
x=event_position_df['y'],
y="text", # Display text, not points
mode=event_position_df['label'], # Get labels from layout
text# ... (styling) ...
='none' # Don't show hover info for labels
hoverinfo
))
# === Add Resource Markers (if scenario provided) ===
if scenario is not None:
# ... (code to calculate positions for each resource instance) ...
# events_with_resources = calculate_resource_positions(...)
fig.add_trace(go.Scatter(=events_with_resources['x_final'], # Calculated X for each resource spot
x=events_with_resources['y_final'] - 10, # Place slightly below entity Y
y="markers", # Draw markers (e.g., circles)
mode=dict(color='LightSkyBlue', size=15),
marker=resource_opacity,
opacity='none'
hoverinfo
))# (Or use 'mode="markers+text"' if using custom_resource_icon)
# === Add Background Image (if requested) ===
if add_background_image is not None:
fig.add_layout_image(# ... (configuration for image source, position, opacity) ...
)
# === Final Styling and Controls ===
=icon_and_text_size) # Set icon size
fig.update_traces(textfont_size=False, showgrid=False) # Clean up axes
fig.update_xaxes(showticklabels=False, showgrid=False)
fig.update_yaxes(showticklabels# ... (configure animation speed, play button etc.) ...
return fig
This simplified view shows how generate_animation
layers the different visual elements (animated entities, static labels, static resource markers, background) using Plotly’s capabilities to produce the final interactive figure.
Conclusion
You’ve reached the end of the vidigi
core concepts tutorial! We’ve seen how generate_animation
acts as the final “movie projector”. It takes the meticulously prepared frame-by-frame data (the output of generate_animation_df
from Chapter 5: Snapshot Preparation (reshape_for_animations
& generate_animation_df
)) and uses Plotly Express to render the interactive animation. It plots entities, handles smooth transitions, adds labels, resource markers, and controls, bringing your process simulation to life visually.
While often called behind the scenes by the main animate_activity_log
function, understanding generate_animation
shows you how the final visualization is constructed.
You now have a complete picture of the vidigi
pipeline:
- Starting with an Event Log (the script) and a Layout Configuration (
event_position_df
) (the map). - Ensuring resources are uniquely identified using the pattern from Chapter 4: Simpy Resource Enhancement (
CustomResource
,Store
,populate_store
). - Preparing frame-by-frame snapshot data using the functions covered in Chapter 5: Snapshot Preparation (
reshape_for_animations
&generate_animation_df
). - Finally, rendering the animation with
generate_animation
(this chapter), often orchestrated by the mainanimate_activity_log
facade.
Congratulations! You’re now equipped with the knowledge to understand and use vidigi
to create insightful animations of your own processes. Happy visualizing!
Generated by AI Codebase Knowledge Builder