Feature Example: Choosing which way a queue builds

Warning

This example has not yet been updated to reflect all of the new features and recommendations from vidigi 2.0.0, which have further simplified the process of adding vidigi to your model and accessing and modifying your animation.

For now, all of the code below will still work, but check out the getting started page for a full guide to the recommended way to use vidigi 2.0.0.

In every vidigi animation so far, a queue has built up from right to left: the x you give an event in event_position_df is the front of the queue (the bottom-right corner), and each waiting entity is drawn one gap_between_entities further to the left.

That is a fine default, but it fights with a lot of entity icons. The person-walking emoji, the ambulance, the car - plenty of them face right, so a queue that grows leftwards has everyone walking away from the thing they are waiting for. Putting the front of the queue at the bottom-left, with the tail extending right, lines the icons up with the direction of travel.

queue_direction controls this. It takes "left" (the default, unchanged behaviour) or "right", and it is accepted by animate_activity_log, generate_animation and generate_animation_df. For finer control, an individual EventPosition can carry its own direction, which overrides the animation-wide setting for just that stage.

Model setup

The single-step clinic model from example_1_simplest_case - patients arrive, queue for a treatment cubicle, are treated, and leave. Arrivals comfortably outpace the four cubicles, so a long queue builds - exactly what we want to look at.

import random

import plotly.io as pio
from queue_direction_model import Model, g

from vidigi.animation import animate_activity_log
from vidigi.utils import EventPosition, create_event_position_df

pio.renderers.default = "notebook"

model = Model(run_number=1)
event_log = model.run()["event_log"]
event_log.head()
patient pathway event_type event time resource_id run
0 1 Simplest arrival_departure arrival 0.000000 NaN 1
1 1 Simplest queue treatment_wait_begins 0.000000 NaN 1
2 1 Simplest resource_use treatment_begins 0.000000 1.0 1
3 2 Simplest arrival_departure arrival 12.021043 NaN 1
4 2 Simplest queue treatment_wait_begins 12.021043 NaN 1
icons = [
    "🚶‍♀️",
    "🚶🏻‍♂️",
    "🚶🏼‍♂️",
    "🚶🏽‍♂️",
    "🚶🏽‍♂️",
    "🚶🏿‍♂️",
    "🚶🏿‍♀️",
    "🚶🏾‍♀️",
    "🚶🏽‍♀️",
    "🚶🏼‍♀️",
    "🚶🏻‍♀️",
]
random.shuffle(icons)

The default: queues build to the left

Nothing new here - queue_direction is not passed, so it takes its default of "left". The x values in event_position_df are the front of each queue and the rightmost cubicle; everything stacks up leftwards from there.

event_position_df = create_event_position_df(
    [
        EventPosition(event="arrival", x=50, y=300, label="Arrival"),
        EventPosition(
            event="treatment_wait_begins", x=450, y=275, label="Waiting for Treatment"
        ),
        EventPosition(
            event="treatment_begins",
            x=250,
            y=175,
            resource="n_cubicles",
            label="Being Treated",
        ),
        EventPosition(event="depart", x=170, y=70, label="Exit"),
    ]
)

animate_activity_log(
    event_log=event_log,
    event_position_df=event_position_df,
    entity_col_name="patient",
    scenario=g(),
    every_x_time_units=10,
    limit_duration=200,
    wrap_queues_at=15,
    frame_duration=1000,
    gap_between_entities=20,
    gap_between_resources=20,
    plotly_height=500,
    plotly_width=1200,
    custom_entity_icon_list=icons,
)

Flipping the whole animation: queue_direction="right"

The same call with one extra argument. Now each x is the bottom-left corner: the front of every queue sits there and the line extends to the right, wrapped rows included. The resource cubicles lay out rightwards too, so a patient in treatment stays on the same side they queued on, and the stage labels move to the left of each anchor so the queue no longer runs over them.

animate_activity_log(
    event_log=event_log,
    event_position_df=event_position_df,
    entity_col_name="patient",
    scenario=g(),
    every_x_time_units=10,
    limit_duration=200,
    wrap_queues_at=15,
    gap_between_entities=20,
    gap_between_resources=20,
    frame_duration=1000,
    plotly_height=500,
    plotly_width=1200,
    queue_direction="right",
    custom_entity_icon_list=icons,
)

One stage at a time: the per-event direction column

queue_direction sets the default for the animation; any EventPosition with its own direction overrides it. Here the global setting is left at "left", but the treatment queue and the cubicles are told to build right - handy when a background image or floor plan means different parts of the pathway face different ways.

mixed_position_df = create_event_position_df(
    [
        EventPosition(event="arrival", x=50, y=300, label="Arrival"),
        EventPosition(
            event="treatment_wait_begins",
            x=450,
            y=275,
            label="Waiting for Treatment",
            direction="right",
        ),
        EventPosition(
            event="treatment_begins",
            x=450,
            y=175,
            resource="n_cubicles",
            label="Being Treated",
            direction="right",
        ),
        EventPosition(event="depart", x=270, y=70, label="Exit"),
    ]
)

mixed_position_df
event x y label resource direction flip_icons resource_icon
0 arrival 50 300 Arrival NaN NaN None None
1 treatment_wait_begins 450 275 Waiting for Treatment NaN right None None
2 treatment_begins 450 175 Being Treated n_cubicles right None None
3 depart 270 70 Exit NaN NaN None None
animate_activity_log(
    event_log=event_log,
    event_position_df=mixed_position_df,
    entity_col_name="patient",
    scenario=g(),
    every_x_time_units=10,
    limit_duration=200,
    wrap_queues_at=15,
    gap_between_entities=20,
    gap_between_resources=20,
    frame_duration=1000,
    plotly_height=500,
    plotly_width=1200,
    custom_entity_icon_list=icons,
)

Notes

  • If you use the three-step pipeline (reshape_for_animations → generate_animation_df → generate_animation) rather than animate_activity_log, pass queue_direction to both generate_animation_df and generate_animation.
  • A right-building queue can run past the right edge of the plot; vidigi grows the figure margin on that side automatically, the same way it already does on the left.
  • v2_release_additions tours the rest of what shipped in 2.0.0.
Back to top