sequenceDiagram
participant Snapshots as Snapshot Data (Patient, Event, Time, Type, ResourceID?)
participant GADF as generate_animation_df
participant Layout as event_position_df
participant Output as Data with Positions (X, Y added)
GADF->>Snapshots: Receive snapshot data for a time point
loop For each patient in snapshot
GADF->>Layout: Find row where Layout.event == Snapshot.event
Layout-->>GADF: Return matching row (with base X, Y, Type)
alt event_type is 'queue'
GADF->>GADF: Calculate queue position (e.g., base_x - rank * gap)
else event_type is 'resource_use'
GADF->>GADF: Calculate resource position (e.g., base_x - resource_id * gap)
else Other types
GADF->>GADF: Use base position (x, y)
end
GADF->>Output: Store patient, time, calculated X, Y, etc.
end
Output-->>GADF: Complete data with positions for this time point
Chapter 3: Where Am I? Mapping the Grid with event_position_df
Greetings, Program! In Chapter 2: Log Everything! Like K.I.T.T.’s Mission Recorder, we learned how to create the crucial event_log, the detailed script telling us what happened and when in our system (like Maverick’s flight training log). But just knowing when Maverick started waiting for the simulator isn’t enough to make our animation. We need to know where on the screen the “Waiting Area” or the “Simulator Bay” should actually be.
Imagine trying to film Back to the Future without knowing where to put the Clock Tower or the Twin Pines Mall. Chaos! Or trying to navigate the Tron grid without coordinates. Double chaos! We need a map, a blueprint, a layout guide.
That’s exactly what the Layout Configuration (event_position_df) provides. It’s the stage map for your vidigi animation movie set.
The Mission: Setting the Stage
Our mission, should we choose to accept it (and not self-destruct this message), is to define the visual layout for our Top Gun training animation. We need to tell vidigi where the ‘Arrival Zone’, the ‘Waiting Area’, and the ‘Simulator Bays’ should appear on the screen. This allows vidigi to place the icons representing Maverick and Goose correctly as they move through their training steps.
The Blueprint: Anatomy of the event_position_df
The event_position_df is, like the event_log, a pandas DataFrame. It acts as a lookup table, connecting the event names from your log to specific locations and other visual properties on the animation canvas. Think of it as the director’s annotated script, marking where each scene takes place.
Here are the key columns you’ll typically use:
event: This column contains the specific event names you used in your Event Log (e.g., ‘arrival’, ‘wait_for_simulator’, ‘start_simulator’, ‘depart’). This is the keyvidigiuses to find the right location.x: The horizontal coordinate (like on an arcade screen) for this event’s base position. Lower numbers are typically further left.y: The vertical coordinate for this event’s base position. Lower numbers are typically higher up on the screen (like typical computer graphics coordinates, not graph paper!).label: A human-readable name for this stage or activity (e.g., “Arrival Zone”, “Wait for Simulator”, “Simulator Bay”). This is helpful for understanding the layout and can optionally be displayed directly on the animation (using thedisplay_stage_labels=Trueoption inanimate_activity_log). Think of it as the sign outside the location, like “Lou’s Cafe” or “Stark Industries”.resource(Optional): This column is used forresource_useevents (like ‘start_simulator’). It contains the name of the attribute in yourscenarioobject (we’ll discuss scenarios later, think of it as the inventory list from Weyland-Yutani) that holds the total number of available resources for this step (e.g., the name of the variable storing the number of flight simulators). This allowsvidigito draw placeholders for all available resource slots, even if they aren’t currently in use.
Important Note: You only need to define positions for events where entities stop or start something visually significant: * arrival and depart (mandatory, unless you filter them out later). * queue events (e.g., ‘wait_for_simulator’). * resource_use start events (e.g., ‘start_simulator’). You don’t typically need entries for resource_use_end events (like ‘end_simulator’), as the entity is usually already at the resource location.
Creating Your Layout: Engage!
Let’s build the event_position_df for our Top Gun example. We’ll use pandas again.
import pandas as pd
# Define the layout data as a list of dictionaries
position_data = [
# Where pilots first appear
{'event': 'arrival', 'x': 50, 'y': 200, 'label': 'Hangar Deck (Arrival)'},
# Where pilots wait if simulators are busy
{'event': 'wait_for_simulator', 'x': 200, 'y': 200, 'label': 'Ready Room (Queue)'},
# The *base* position for the simulator resource area
{'event': 'start_simulator', 'x': 350, 'y': 200, 'label': 'Simulator Bays', 'resource': 'n_simulators'},
# ^ We added 'resource' here! Let's assume our scenario object (g) will have g.n_simulators
# Where pilots go after finishing
{'event': 'depart', 'x': 500, 'y': 200, 'label': 'Debriefing (Departure)'},
# We also need an 'exit' position for vidigi's internal logic
{'event': 'exit', 'x': 550, 'y': 200, 'label': 'Off Duty'}
]
# Convert the list into a pandas DataFrame
event_pos_df = pd.DataFrame(position_data)
# Let's beam it up and see what it looks like!
print("Our Stage Layout Blueprint:")
print(event_pos_df)This code creates our layout definition. * We list each key event name from our Event Log. * We assign x and y coordinates. Think of the screen as a grid; (0, 0) is usually the top-left. Higher x moves right, higher y moves down. * We give each a readable label. * For ‘start_simulator’, we add the resource column, linking it to a hypothetical n_simulators variable in our setup (we’ll see this more in Chapter 4: Simpy Resource Enhancement (CustomResource, Store, populate_store)). * We include an ‘exit’ event, which is used internally by vidigi to make entities disappear cleanly.
This event_pos_df DataFrame is the second crucial input (after the event_log_df) that you pass to the main animate_activity_log function.
# --- Hypothetical call to the main function ---
# (Assuming event_log_df from Chapter 2 and event_pos_df from above exist)
# (Also assuming a 'scenario' object 'g' exists with g.n_simulators = 2)
# from vidigi.animation import animate_activity_log # If not already imported
# g = YourScenarioClass() # Define g with g.n_simulators = 2
# g.n_simulators = 2 # Need to define this for the 'resource' column link
# my_animation = animate_activity_log(
# event_log=event_log_df, # The script (What/When)
# event_position_df=event_pos_df, # The stage map (Where)
# scenario=g # Info about resource counts
# )
# my_animation.show() # Engage! Show the animation!
# --- End Hypothetical Call ---
print("Imagine the animation running with this layout!")If you ran this (with the actual data and scenario setup), vidigi would use event_pos_df to place the icons for Maverick and Goose at the ‘Hangar Deck’ (50, 200), then move them to the ‘Ready Room’ (base X=200, Y=200) if they queue, or to one of the ‘Simulator Bays’ (base X=350, Y=200) when they start training, and finally to ‘Debriefing’ (500, 200).
Under the Hood: How vidigi Uses the Layout
So, how does vidigi use this blueprint? It doesn’t build the set itself, but it tells the actors (our patient/pilot icons) where to stand based on the script (event_log) and the map (event_position_df).
The key function that uses the event_position_df is generate_animation_df. Remember from Chapter 1 that the event_log gets processed into “snapshots” – a table showing which event each patient is currently experiencing at each moment in time.
generate_animation_df takes these snapshots and the event_position_df and does the following:
- Lookup: For each patient in each time snapshot, it looks at their current
event. - Merge: It finds the matching
eventrow in theevent_position_df. - Get Base Position: It retrieves the base
xandycoordinates from that row. - Calculate Final Position: This is where it gets clever, like Johnny 5 calculating trajectories:
- If the
event_typeisarrival_departureorresource_use_end(or anything not queue/resource use), the final position is usually just the base (x, y). - If the
event_typeisqueue, it calculates an offset. Patients in a queue typically line up to the left of the basexcoordinate. The first patient might be atx - gap, the second atx - 2*gap, and so on. If the queue gets too long (wrap_queues_at), it starts a new row below. - If the
event_typeisresource_use, it uses theresource_id(from the event log, e.g., ‘Sim_1’, ‘Sim_2’) to calculate an offset from the basex. Resource 1 might be atx - gap, Resource 2 atx - 2*gap, etc., again wrapping if needed (wrap_resources_at). This ensures Maverick always appears at the same simulator bay (‘Sim_1’) while using it.
- If the
Here’s a simplified sequence diagram:
The generate_animation function also uses event_position_df, primarily to: * Draw the optional stage labels (display_stage_labels=True). * Draw the placeholder icons for all available resources (using the resource column and the linked scenario object) so you can see empty slots.
Internally, the code within vidigi/prep.py (specifically generate_animation_df) might look conceptually like this:
# --- Inside generate_animation_df (Simplified Concept) ---
# (Input: full_patient_df = snapshots, event_position_df = layout)
import pandas as pd
import numpy as np # For calculations
def calculate_positions(full_patient_df, event_position_df, gap_between_entities, wrap_queues_at, gap_between_rows):
# Merge snapshots with layout info based on the 'event' name
df_with_base_pos = pd.merge(full_patient_df, event_position_df, on='event', how='left')
# --- Calculate Queue Positions ---
queues = df_with_base_pos[df_with_base_pos['event_type'] == 'queue'].copy()
if not queues.empty:
# 'rank' tells us position in queue (1st, 2nd, etc.) - calculated earlier
queues['row'] = np.floor((queues['rank'] - 1) / wrap_queues_at)
# Base X minus offset for position in line, adjusted for wrapping
queues['x_final'] = queues['x'] - (queues['rank'] % wrap_queues_at) * gap_between_entities # Simplified!
# Base Y plus offset for row wrapping
queues['y_final'] = queues['y'] + queues['row'] * gap_between_rows
# --- Calculate Resource Positions (Similar logic using resource_id) ---
resources = df_with_base_pos[df_with_base_pos['event_type'] == 'resource_use'].copy()
# ... similar calculations for resources['x_final'], resources['y_final'] using resource_id ...
# Simplified: Assume resources just use base position for this example
if not resources.empty:
resources['x_final'] = resources['x']
resources['y_final'] = resources['y']
# --- Handle other event types (e.g., arrival, depart) ---
others = df_with_base_pos[~df_with_base_pos['event_type'].isin(['queue', 'resource_use'])].copy()
if not others.empty:
others['x_final'] = others['x'] # Just use the base position
others['y_final'] = others['y']
# Combine back into one DataFrame
final_df = pd.concat([queues, resources, others], ignore_index=True)
return final_df
# (Output: final_df contains the original snapshot data plus 'x_final' and 'y_final' columns)This simplified snippet shows the core idea: merge the layout’s base coordinates, then adjust them based on the event type and ranking/resource ID to get the final plotting position.
Conclusion: You Have the Touch! You Have the Power!
Yeah! You’ve now grasped the event_position_df, the essential blueprint that tells vidigi where to place everything in your animation. It maps your logical event names (like ‘wait_for_simulator’) to physical screen coordinates (X, Y), provides labels, and even helps visualize resource capacity.
Combined with the Event Log (the “What” and “When”), the event_position_df (the “Where”) gives vidigi almost everything it needs to create your visual masterpiece via the Animation Facade (animate_activity_log).
But what about those resources? How do we properly represent things like nurses, simulators, or maybe even proton packs that entities need to use? And how does that resource_id in the event log get generated? We need to enhance our simulation model slightly.
Don’t have a cow, man! Let’s find out in the next chapter: Chapter 4: Simpy Resource Enhancement (CustomResource, Store, populate_store).
Generated by AI Codebase Knowledge Builder