Skip to content

Commit 735d502

Browse files
committed
Add additional example animations
1 parent 6dfa938 commit 735d502

26 files changed

Lines changed: 3780 additions & 1996 deletions

_extensions/whitphx/stlite/stlite.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ window.addEventListener("load", function(event) {
44
minHeight: 140,
55
sizeWidth: true,
66
maxWidth: 800,
7+
maxHeight: 675,
78
widthCalculationMethod: 'rightMostElement' // Or 'bodyOffset'
89
}, ".stlite");
910
});
863 KB
Loading
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0" version="27.1.3">
2+
<diagram name="Page-1" id="RVeoq7JkO4piARM7jQIV">
3+
<mxGraphModel dx="1489" dy="941" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="600" math="0" shadow="0">
4+
<root>
5+
<mxCell id="0" />
6+
<mxCell id="1" parent="0" />
7+
<mxCell id="8jv0vOWLXyXSIXB2e-R--5" value="" style="rounded=0;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;fillColor=#b1ddf0;strokeColor=#10739e;" vertex="1" parent="1">
8+
<mxGeometry x="10" y="8" width="1080" height="272" as="geometry" />
9+
</mxCell>
10+
<mxCell id="8jv0vOWLXyXSIXB2e-R--3" value="" style="rounded=0;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;fillColor=#fad9d5;strokeColor=#ae4132;" vertex="1" parent="1">
11+
<mxGeometry x="10" y="280" width="1080" height="300" as="geometry" />
12+
</mxCell>
13+
<mxCell id="8jv0vOWLXyXSIXB2e-R--4" value="Sinks" style="rounded=0;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;fontStyle=0;fontFamily=Caveat;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DCaveat;fontSize=29;" vertex="1" parent="1">
14+
<mxGeometry x="350" y="280" width="370" height="60" as="geometry" />
15+
</mxCell>
16+
<mxCell id="8jv0vOWLXyXSIXB2e-R--2" value="Pathway" style="rounded=0;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;fontStyle=0;fontFamily=Caveat;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DCaveat;fontSize=29;" vertex="1" parent="1">
17+
<mxGeometry x="350" y="220" width="370" height="60" as="geometry" />
18+
</mxCell>
19+
<mxCell id="8jv0vOWLXyXSIXB2e-R--1" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=6;sketch=1;curveFitting=1;jiggle=2;" edge="1" parent="1">
20+
<mxGeometry width="50" height="50" relative="1" as="geometry">
21+
<mxPoint x="40" y="280" as="sourcePoint" />
22+
<mxPoint x="1070" y="280" as="targetPoint" />
23+
</mxGeometry>
24+
</mxCell>
25+
</root>
26+
</mxGraphModel>
27+
</diagram>
28+
</mxfile>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from vidigi.logging import EventLogger
2+
from vidigi.utils import EventPosition, create_event_position_df
3+
from vidigi.animation import animate_activity_log
4+
import simpy
5+
import numpy as np
6+
7+
class Patient:
8+
def __init__(self, p_id):
9+
self.id = p_id
10+
11+
class SimpleActivityModelThreeSequentialActivities:
12+
def __init__(self, master_seed=42):
13+
self.env = simpy.Environment()
14+
self.patient_counter = 0
15+
self.patient_inter = 4
16+
self.logger = EventLogger(env=self.env)
17+
18+
# Seed setup using numpy's SeedSequence
19+
self.master_seed = master_seed
20+
self.seed_seq = np.random.SeedSequence(master_seed)
21+
self.rng = np.random.default_rng(self.seed_seq)
22+
23+
def generate_arrivals(self):
24+
while True:
25+
self.patient_counter += 1
26+
27+
p = Patient(self.patient_counter)
28+
29+
self.logger.log_arrival(entity_id=p.id)
30+
self.env.process(self.patient_journey(p))
31+
sampled_inter = self.rng.exponential(scale=self.patient_inter)
32+
yield self.env.timeout(sampled_inter)
33+
34+
def patient_journey(self, patient):
35+
36+
self.logger.log_queue(entity_id=patient.id, event="wait_here_receptionist")
37+
38+
yield self.env.timeout(abs(self.rng.normal(loc=3, scale=1)))
39+
40+
self.logger.log_queue(entity_id=patient.id, event="wait_here_blood_test")
41+
42+
yield self.env.timeout(abs(self.rng.normal(loc=14, scale=3)))
43+
44+
self.logger.log_queue(entity_id=patient.id, event="wait_here_doctor_consult")
45+
46+
yield self.env.timeout(abs(self.rng.normal(loc=8, scale=2)))
47+
48+
self.logger.log_departure(entity_id=patient.id)
49+
50+
def run(self):
51+
self.env.process(self.generate_arrivals())
52+
self.env.run(until=180)
53+
54+
model = SimpleActivityModelThreeSequentialActivities()
55+
model.run()
56+
event_log = model.logger.to_dataframe()
57+
# event_log.to_csv("test_log.csv")
58+
59+
animate_activity_log(
60+
event_log = event_log,
61+
event_position_df = create_event_position_df([
62+
EventPosition(event="wait_here_receptionist", x=50 , y=25 , label="Registering<br>with receptionist"),
63+
EventPosition(event="wait_here_blood_test", x=125 , y=125 , label="Having a<br>blood test"),
64+
EventPosition(event="wait_here_doctor_consult", x=200 , y=225 , label="Seeing<br>a doctor"),
65+
EventPosition(event="depart", x=400, y=125, label="Exit")
66+
]),
67+
every_x_time_units=1,
68+
limit_duration=60,
69+
override_x_max=300,
70+
override_y_max=275,
71+
plotly_height=600,
72+
plotly_width=1100,
73+
display_stage_labels=True,
74+
# time_display_units="%M minutes",
75+
gap_between_entities=15,
76+
wrap_queues_at=5,
77+
entity_icon_size=30,
78+
simulation_time_unit="minutes",
79+
debug_write_intermediate_objects=True
80+
).update_layout(
81+
plot_bgcolor='white',
82+
)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
from vidigi.logging import EventLogger
2+
from vidigi.utils import EventPosition, create_event_position_df
3+
from vidigi.animation import animate_activity_log
4+
import simpy
5+
import numpy as np
6+
7+
class Patient:
8+
def __init__(self, p_id):
9+
self.id = p_id
10+
11+
class SimpleActivityModelSequentialActivitiesSinks:
12+
def __init__(self, master_seed=42):
13+
self.env = simpy.Environment()
14+
self.patient_counter = 0
15+
self.patient_inter = 4
16+
self.logger = EventLogger(env=self.env)
17+
18+
# Seed setup using numpy's SeedSequence
19+
self.master_seed = master_seed
20+
self.seed_seq = np.random.SeedSequence(master_seed)
21+
self.rng = np.random.default_rng(self.seed_seq)
22+
23+
def generate_arrivals(self):
24+
while True:
25+
self.patient_counter += 1
26+
27+
p = Patient(self.patient_counter)
28+
29+
self.logger.log_arrival(entity_id=p.id)
30+
self.env.process(self.patient_journey(p))
31+
sampled_inter = self.rng.exponential(scale=self.patient_inter)
32+
yield self.env.timeout(sampled_inter)
33+
34+
def patient_journey(self, patient):
35+
36+
self.logger.log_queue(entity_id=patient.id, event="wait_here_triage")
37+
38+
yield self.env.timeout(abs(self.rng.normal(loc=3, scale=1)))
39+
40+
if self.rng.uniform(low=0.0, high=1.0) < 0.2:
41+
self.logger.log_queue(entity_id=patient.id, event="died")
42+
yield self.env.timeout(2)
43+
self.logger.log_departure(entity_id=patient.id)
44+
45+
else:
46+
self.logger.log_queue(entity_id=patient.id, event="wait_here_stabilisation")
47+
yield self.env.timeout(abs(self.rng.normal(loc=14, scale=3)))
48+
49+
if self.rng.uniform(low=0.0, high=1.0) < 0.1:
50+
self.logger.log_queue(entity_id=patient.id, event="died")
51+
yield self.env.timeout(2)
52+
self.logger.log_departure(entity_id=patient.id)
53+
elif self.rng.uniform(low=0.0, high=1.0) < 0.2:
54+
self.logger.log_queue(entity_id=patient.id, event="transferred")
55+
yield self.env.timeout(2)
56+
self.logger.log_departure(entity_id=patient.id)
57+
elif self.rng.uniform(low=0.0, high=1.0) < 0.5:
58+
self.logger.log_queue(entity_id=patient.id, event="ward")
59+
yield self.env.timeout(2)
60+
self.logger.log_departure(entity_id=patient.id)
61+
else:
62+
self.logger.log_queue(entity_id=patient.id, event="wait_here_treatment")
63+
yield self.env.timeout(abs(self.rng.normal(loc=8, scale=2)))
64+
65+
if self.rng.uniform(low=0.0, high=1.0) < 0.2:
66+
self.logger.log_queue(entity_id=patient.id, event="transferred")
67+
yield self.env.timeout(2)
68+
self.logger.log_departure(entity_id=patient.id)
69+
elif self.rng.uniform(low=0.0, high=1.0) < 0.7:
70+
self.logger.log_queue(entity_id=patient.id, event="ward")
71+
yield self.env.timeout(2)
72+
self.logger.log_departure(entity_id=patient.id)
73+
else:
74+
self.logger.log_queue(entity_id=patient.id, event="home")
75+
yield self.env.timeout(2)
76+
self.logger.log_departure(entity_id=patient.id)
77+
78+
def run(self):
79+
self.env.process(self.generate_arrivals())
80+
self.env.run(until=180)
81+
82+
model = SimpleActivityModelSequentialActivitiesSinks()
83+
model.run()
84+
event_log = model.logger.to_dataframe()
85+
# event_log.to_csv("test_log.csv")
86+
87+
animate_activity_log(
88+
event_log = event_log,
89+
event_position_df = create_event_position_df([
90+
EventPosition(event="wait_here_triage", x=50 , y=225 , label="Arrived<br>in ED"),
91+
EventPosition(event="wait_here_stabilisation", x=150 , y=225 , label="Being<br>Stabilised"),
92+
EventPosition(event="wait_here_treatment", x=250 , y=225 , label="Being<br>Treated"),
93+
94+
95+
EventPosition(event="died", x=40 , y=50 , label="Died"),
96+
EventPosition(event="transferred", x=100 , y=50 , label="Transferred<br>to other<br>Hospital"),
97+
EventPosition(event="ward", x=160 , y=50 , label="Admitted<br>to Ward"),
98+
EventPosition(event="home", x=220 , y=50 , label="Discharged<br>home"),
99+
100+
101+
EventPosition(event="depart", x=150, y=-30, label="Exit")
102+
]),
103+
every_x_time_units=1,
104+
limit_duration=60,
105+
override_x_max=300,
106+
override_y_max=275,
107+
plotly_height=600,
108+
plotly_width=1100,
109+
display_stage_labels=True,
110+
# time_display_units="%M minutes",
111+
gap_between_entities=15,
112+
wrap_queues_at=5,
113+
entity_icon_size=30,
114+
simulation_time_unit="minutes",
115+
debug_write_intermediate_objects=True,
116+
add_background_image="animation_examples/background_files/sinks.drawio.png"
117+
).update_layout(
118+
plot_bgcolor='white',
119+
)
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
from vidigi.logging import EventLogger
2+
from vidigi.utils import EventPosition, create_event_position_df
3+
from vidigi.animation import animate_activity_log
4+
import simpy
5+
import numpy as np
6+
7+
class Patient:
8+
def __init__(self, p_id):
9+
self.id = p_id
10+
11+
class SimpleActivityModelThreeSequentialActivities:
12+
def __init__(self, master_seed=42):
13+
self.env = simpy.Environment()
14+
self.patient_counter = 0
15+
self.patient_inter = 3
16+
self.logger = EventLogger(env=self.env)
17+
18+
# Seed setup using numpy's SeedSequence
19+
self.master_seed = master_seed
20+
self.seed_seq = np.random.SeedSequence(master_seed)
21+
self.rng = np.random.default_rng(self.seed_seq)
22+
23+
def generate_arrivals(self):
24+
while True:
25+
self.patient_counter += 1
26+
27+
p = Patient(self.patient_counter)
28+
29+
self.logger.log_arrival(entity_id=p.id)
30+
31+
if self.rng.uniform(low=0.0, high=1.0) < 0.4:
32+
self.env.process(self.patient_journey_1(p))
33+
else:
34+
self.env.process(self.patient_journey_2(p))
35+
sampled_inter = self.rng.exponential(scale=self.patient_inter)
36+
yield self.env.timeout(sampled_inter)
37+
38+
def patient_journey_1(self, patient):
39+
40+
if self.rng.uniform(low=0.0, high=1.0) < 0.4:
41+
self.logger.log_queue(entity_id=patient.id, event="wait_here_receptionist")
42+
yield self.env.timeout(abs(self.rng.normal(loc=3, scale=1)))
43+
else:
44+
self.logger.log_queue(entity_id=patient.id, event="wait_here_self_register")
45+
yield self.env.timeout(abs(self.rng.normal(loc=2, scale=1)))
46+
47+
# 50% of patients in this pathway have a blood test before their appointment
48+
if self.rng.uniform(low=0.0, high=1.0) < 0.5:
49+
self.logger.log_queue(entity_id=patient.id, event="wait_here_blood_test")
50+
yield self.env.timeout(abs(self.rng.normal(loc=14, scale=3)))
51+
52+
self.logger.log_queue(entity_id=patient.id, event="wait_here_doctor_consult")
53+
54+
yield self.env.timeout(abs(self.rng.normal(loc=8, scale=2)))
55+
56+
self.logger.log_departure(entity_id=patient.id)
57+
58+
def patient_journey_2(self, patient):
59+
if self.rng.uniform(low=0.0, high=1.0) < 0.7:
60+
self.logger.log_queue(entity_id=patient.id, event="wait_here_app")
61+
yield self.env.timeout(abs(self.rng.normal(loc=3, scale=1)))
62+
else:
63+
self.logger.log_queue(entity_id=patient.id, event="wait_here_self_register")
64+
yield self.env.timeout(abs(self.rng.normal(loc=3, scale=1)))
65+
66+
# 20% of patients in this pathway have a blood test before their appointment
67+
if self.rng.uniform(low=0.0, high=1.0) < 0.2:
68+
self.logger.log_queue(entity_id=patient.id, event="wait_here_blood_test")
69+
yield self.env.timeout(abs(self.rng.normal(loc=14, scale=3)))
70+
# Another 60% have a health check with a nurse
71+
elif self.rng.uniform(low=0.0, high=1.0) < 0.8:
72+
self.logger.log_queue(entity_id=patient.id, event="wait_here_health_check")
73+
yield self.env.timeout(abs(self.rng.normal(loc=14, scale=3)))
74+
# And some proportion of those also then have a blood test
75+
if self.rng.uniform(low=0.0, high=1.0) < 0.7:
76+
self.logger.log_queue(entity_id=patient.id, event="wait_here_blood_test")
77+
yield self.env.timeout(abs(self.rng.normal(loc=14, scale=3)))
78+
# And the remaining 20% go straight to the doctor
79+
if self.rng.uniform(low=0.0, high=1.0) < 0.7:
80+
self.logger.log_queue(entity_id=patient.id, event="wait_here_doctor_consult")
81+
yield self.env.timeout(abs(self.rng.normal(loc=8, scale=2)))
82+
83+
# some then return to the receptionist to book another appointment
84+
if self.rng.uniform(low=0.0, high=1.0) < 0.4:
85+
self.logger.log_queue(entity_id=patient.id, event="wait_here_receptionist")
86+
yield self.env.timeout(abs(self.rng.normal(loc=8, scale=2)))
87+
88+
def run(self):
89+
self.env.process(self.generate_arrivals())
90+
self.env.run(until=180)
91+
92+
93+
model = SimpleActivityModelThreeSequentialActivities()
94+
model.run()
95+
event_log = model.logger.to_dataframe()
96+
# event_log.to_csv("test_log.csv")
97+
98+
animate_activity_log(
99+
event_log = event_log,
100+
event_position_df = create_event_position_df([
101+
102+
EventPosition(event="wait_here_receptionist", x=75 , y=25 , label="Speaking<br>with receptionist"),
103+
EventPosition(event="wait_here_self_register", x=75 , y=125 , label="Registering<br>via machine"),
104+
EventPosition(event="wait_here_app", x=75 , y=225 , label="Checked in<br>via app"),
105+
106+
107+
EventPosition(event="wait_here_blood_test", x=175 , y=125 , label="Having a<br>blood test"),
108+
EventPosition(event="wait_here_health_check", x=175 , y=225 , label="Having a<br>health check"),
109+
110+
EventPosition(event="wait_here_doctor_consult", x=275 , y=225 , label="Seeing<br>a<br>doctor"),
111+
112+
EventPosition(event="depart", x=400, y=125, label="Exit")
113+
]),
114+
every_x_time_units=1,
115+
limit_duration=60,
116+
override_x_max=300,
117+
override_y_max=325,
118+
plotly_height=600,
119+
plotly_width=1100,
120+
display_stage_labels=True,
121+
# time_display_units="%M minutes",
122+
gap_between_entities=10,
123+
wrap_queues_at=5,
124+
entity_icon_size=20,
125+
text_size=15,
126+
simulation_time_unit="minutes",
127+
debug_write_intermediate_objects=True
128+
).update_layout(
129+
plot_bgcolor='white',
130+
)

0 commit comments

Comments
 (0)