Created
June 21, 2016 16:24
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very neat!
Am I right in saying that here:
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!