Skip to content

Instantly share code, notes, and snippets.

@alcarney
Created June 21, 2016 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alcarney/5cdae1242140c71902635acadc84da09 to your computer and use it in GitHub Desktop.
Save alcarney/5cdae1242140c71902635acadc84da09 to your computer and use it in GitHub Desktop.
This is the code used to generate the animation data for the video you can see here https://www.youtube.com/watch?v=AYvpyuF1bY0
import bpy
import csv
# This file is an attempt to work out the code needed to map
# results from Ciw to an animation in blender
# It requires the existence of objects with the following names
# in a blender scene:
# - Customer
# - Wait
# - Queue 1 Arrive
# - Queue 1 Exit
# - Queue 1 Service
# - Queue 1 Wait
# - Queue 2 Arrive
# - Queue 2 Exit
# - Queue 2 Service
# - Queue 2 Wait
def create_instance(obj):
"""
This function given an object will create an instance of an object
and link it to the scene
"""
# Get the mesh data from the original object
mesh = obj.data
# Create the new object
new_obj = bpy.data.objects.new(obj.name, mesh)
# Link it to the scene
bpy.context.scene.objects.link(new_obj)
# Put the person in the 'Waiting area'
new_obj.location = bpy.context.scene.objects['Wait'].location
# Return the object
return new_obj
def map_time_to_frame(time):
"""
Quite simple, given a time, it maps it to a frame number in the animation
"""
# Let's go with 1 time unit = 1 sec = 25 frames for now
# rounded to the nearest whole number
return round(float(time) * 50)
def insert_loc_keyframe(obj, time, loc):
"""
Given an object and a time inser a location keyframe for the
given position
"""
# Make sure only this object is selected
bpy.ops.object.select_all(action='DESELECT')
obj.select = True
# Set the time and location
bpy.context.scene.frame_current = time
obj.location = loc
# Add the keyframe
bpy.ops.anim.keyframe_insert(type='Location')
def animate(actor, record):
"""
This given, an actor and the relevant record will put the correct
key frames in place
"""
# Get the queue the actor is joining
queue = str(record['Node'])
# It's probably cleaner if we define all the locations in one place
waiting_area = bpy.context.scene.objects['Wait'].location
spawn_point = bpy.context.scene.objects['Queue ' + queue + ' Arrive'].location
waiting_line = bpy.context.scene.objects['Queue ' + queue + ' Wait'].location
service_point = bpy.context.scene.objects['Queue ' + queue + ' Service'].location
exit_point = bpy.context.scene.objects['Queue ' + queue + ' Exit'].location
# Along with the times
arrival_time = map_time_to_frame(record['Arrival Date'])
wait_time = map_time_to_frame(record['Waiting Time'])
service_start_time = map_time_to_frame(record['Service Start Date'])
service_end_time = map_time_to_frame(record['Service End Date'])
exit_time = service_end_time + 5
# Time for the logic
# "Spawn" the actor by setting a keframe at 'arrival_time -1' and another at
# arrival time at the arrival point
insert_loc_keyframe(actor, arrival_time - 1, waiting_area)
insert_loc_keyframe(actor, arrival_time, spawn_point)
# Is this peron waiting, if so, set the keyframe at the waiting line
# else go straight to be served
if arrival_time < service_start_time:
insert_loc_keyframe(actor, service_start_time - 1, waiting_line)
insert_loc_keyframe(actor, service_start_time, service_point)
else:
insert_loc_keyframe(actor, service_start_time, service_point)
# After service go away
insert_loc_keyframe(actor, service_end_time, service_point)
insert_loc_keyframe(actor, exit_time, exit_point)
insert_loc_keyframe(actor, exit_time + 1, waiting_area)
def create_actor(customer, record):
"""
This function handles everything needed to create an actor for the
queue
"""
# Create a new actor
actor = create_instance(customer)
# Animate the object.
animate(actor, record)
customer = bpy.context.scene.objects['Customer']
# Load the simulation data
with open('/path/to/file.csv', 'r') as f:
sim = csv.DictReader(f)
for record in sim:
create_actor(customer, record)
"""
This is just the example simulation given in the documentation for the Ciw library, which
you can find here
http://ciw.readthedocs.io/en/latest/Basics/getting_started.html
"""
import ciw
params = {
'Arrival_distributions': {'Class 0': [['Exponential', 6.0], ['Exponential', 2.5]]},
'Number_of_nodes': 2,
'Detect_deadlock': False,
'Number_of_servers': [1, 1],
'Queue_capacities': ['Inf', 4],
'Number_of_classes': 1,
'Service_distributions': {'Class 0': [['Exponential', 8.5], ['Exponential', 5.5]]},
'Transition_matrices': {'Class 0': [[0.0, 0.2], [0.1, 0.0]]}
}
N = ciw.create_network(params)
Q = ciw.Simulation(N)
Q.simulate_until_max_time(1000)
Q.write_records_to_file('/path/to/file.csv')
@geraintpalmer
Copy link

Very neat!

Am I right in saying that here:

with open('/path/to/file.csv', 'r') as f:
    sim = csv.DictReader(f)

    for record in sim:
        create_actor(customer, record)

you're assuming that each record is a new customer? In fact each record is a service, and so more than one record could be mapped to the same customer, they're just going round and round the network. I think this would only matter if you were to animate the transitions between nodes.

Anyway, very cool!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment