Verified Commit 0340b890 authored by Renato Alves's avatar Renato Alves 🌱
Browse files

Add session 12 - 2020

parent f84d9d6d
# Python and Blender
In this session Kimberly Meechan introduced how she uses Blender (https://blender.org) in her scientific projects.
The files used in this session can be found at: https://git.embl.de/meechan/blender_intro
A slightly more advanced example is included in this repository (see `cube_scrambler_panel.py`)
import bpy
from math import radians, degrees, isclose
import mathutils
global current_frame
current_frame = 1.0
def update_keyframes():
global current_frame
current_frame += 1.0
for obj in bpy.data.objects:
obj.keyframe_insert(data_path="location", frame=current_frame)
obj.keyframe_insert(data_path="rotation_quaternion", frame=current_frame)
def rotate_about_centre(cubie, centre_cube, angle, axis):
# Deal with cubie location
# Translate to centre, rotate, and translate back
rotation_centre = centre_cube.location
translate_to_centre = mathutils.Matrix.Translation(rotation_centre - cubie.location)
rotation = mathutils.Matrix.Rotation(radians(angle), 4, axis)
translate_back = translate_to_centre.copy()
translate_back.invert()
full_matrix = translate_to_centre @ rotation @ translate_back
cubie.location = cubie.location + full_matrix.to_translation()
# Deal with cubie rotation, using quaternions
if axis == 'X':
axis_vector = (1.0,0.0,0.0)
elif axis == 'Y':
axis_vector = (0.0,1.0,0.0)
elif axis == 'Z':
axis_vector = (0.0,0.0,1.0)
cubie.rotation_mode = 'QUATERNION'
cubie.rotation_quaternion = mathutils.Quaternion(axis_vector, radians(angle)) @ cubie.rotation_quaternion
def rotate_cube(command, cubie, centre_cube, angle):
if command == "F":
rotate_about_centre(cubie, centre_cube, angle, 'Y')
elif command == "F'":
rotate_about_centre(cubie, centre_cube, -angle, 'Y')
if command == "L":
rotate_about_centre(cubie, centre_cube, angle, 'X')
elif command == "L'":
rotate_about_centre(cubie, centre_cube, -angle, 'X')
if command == "R":
rotate_about_centre(cubie, centre_cube, -angle, 'X')
elif command == "R'":
rotate_about_centre(cubie, centre_cube, angle, 'X')
if command == "B":
rotate_about_centre(cubie, centre_cube, -angle, 'Y')
elif command == "B'":
rotate_about_centre(cubie, centre_cube, angle, 'Y')
if command == "U":
rotate_about_centre(cubie, centre_cube, -angle, 'Z')
elif command == "U'":
rotate_about_centre(cubie, centre_cube, angle, 'Z')
if command == "D":
rotate_about_centre(cubie, centre_cube, angle, 'Z')
elif command == "D'":
rotate_about_centre(cubie, centre_cube, -angle, 'Z')
def rotate_face(command, centre_cube, paired_cubes):
for i in range(0, 45):
for cubie in paired_cubes:
rotate_cube(command, cubie, centre_cube, 2)
rotate_cube(command, centre_cube, centre_cube, 2)
update_keyframes()
def find_face (command):
centre_cube = None
paired_cubes = []
# Distance tolerance for matching cube locations
tol =1e-4
for cube in bpy.data.collections['Cubies'].all_objects:
if command == "F" or command == "F'":
if isclose(cube.location.y, -2, abs_tol=tol) and isclose(cube.location.x, 0, abs_tol=tol) and isclose(cube.location.z, 0, abs_tol=tol):
centre_cube = cube
elif isclose(cube.location.y, -2, abs_tol=tol):
paired_cubes.append(cube)
if command == "R" or command == "R'":
if isclose(cube.location.y, 0, abs_tol=tol) and isclose(cube.location.x, 2, abs_tol=tol) and isclose(cube.location.z, 0, abs_tol=tol):
centre_cube = cube
elif isclose(cube.location.x, 2, abs_tol=tol):
paired_cubes.append(cube)
if command == "L" or command == "L'":
if isclose(cube.location.y, 0, abs_tol=tol) and isclose(cube.location.x, -2, abs_tol=tol) and isclose(cube.location.z, 0, abs_tol=tol):
centre_cube = cube
elif isclose(cube.location.x, -2, abs_tol=tol):
paired_cubes.append(cube)
if command == "B" or command == "B'":
if isclose(cube.location.y, 2, abs_tol=tol) and isclose(cube.location.x, 0, abs_tol=tol) and isclose(cube.location.z, 0, abs_tol=tol):
centre_cube = cube
elif isclose(cube.location.y, 2, abs_tol=tol):
paired_cubes.append(cube)
if command == "U" or command == "U'":
if isclose(cube.location.y, 0, abs_tol=tol) and isclose(cube.location.x, 0, abs_tol=tol) and isclose(cube.location.z, 2, abs_tol=tol):
centre_cube = cube
elif isclose(cube.location.z, 2, abs_tol=tol):
paired_cubes.append(cube)
if command == "D" or command == "D'":
if isclose(cube.location.y, 0, abs_tol=tol) and isclose(cube.location.x, 0, abs_tol=tol) and isclose(cube.location.z, -2, abs_tol=tol):
centre_cube = cube
elif isclose(cube.location.z, -2, abs_tol=tol):
paired_cubes.append(cube)
return centre_cube, paired_cubes
def rotate_whole_cube (command):
centre_cube, paired_cubes = find_face(command)
rotate_face(command, centre_cube, paired_cubes)
def scramble():
scramble_string = bpy.context.scene.cube_settings.scramble
bpy.context.scene.frame_set(1.0)
global current_frame
current_frame = 1.0
for obj in bpy.data.objects:
obj.animation_data_clear()
obj.keyframe_insert(data_path="location", frame=current_frame)
obj.keyframe_insert(data_path="rotation_quaternion", frame=current_frame)
for i in range(0, len(scramble_string)):
command = scramble_string[i]
if i+1 < len(scramble_string):
next_letter = scramble_string[i+1]
else:
next_letter = ""
if command == "'":
continue
if next_letter == "'":
command += next_letter
rotate_whole_cube(command)
bpy.context.scene.frame_set(current_frame)
class scrambler_settings(bpy.types.PropertyGroup):
scramble: bpy.props.StringProperty(name = 'scramble', default="", description="sequence of moves to scramble cube")
class SCRAMBLER_PT_panel(bpy.types.Panel):
"""Cube scrambler panel"""
bl_label = "Cube Scrambler"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
def draw(self, context):
layout = self.layout
row = layout.row()
row.label(text = 'Cube moves:')
row = layout.row()
row.prop(bpy.context.scene.cube_settings, 'scramble', text = 'moves')
row = layout.row()
row.operator('object.scrambler_operator', text = 'Scramble!')
class SCRAMBLER_OT_operator (bpy.types.Operator):
"""Scrambles cube"""
bl_idname = "object.scrambler_operator"
bl_label = "scrambles_cube"
def execute(self, context):
scramble()
return {'FINISHED'}
def register():
bpy.utils.register_class(SCRAMBLER_PT_panel)
bpy.utils.register_class(SCRAMBLER_OT_operator)
bpy.utils.register_class(scrambler_settings)
bpy.types.Scene.cube_settings = bpy.props.PointerProperty(type=scrambler_settings)
def unregister():
bpy.utils.unregister_class(SCRAMBLER_PT_panel)
bpy.utils.unregister_class(SCRAMBLER_OT_operator)
bpy.utils.unregister_class(scrambler_settings)
if __name__ == "__main__":
register()
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment