# https://towardsdatascience.com/blender-2-8-grease-pencil-scripting-and-generative-art-cbbfd3967590import bpy
import math
import numpy as np
defget_grease_pencil(gpencil_obj_name='GPencil')-> bpy.types.GreasePencil:"""
Return the grease-pencil object with the given name. Initialize one if not already present.
:param gpencil_obj_name: name/key of the grease pencil object in the scene
"""# If not present already, create grease pencil objectif gpencil_obj_name notin bpy.context.scene.objects:
bpy.ops.object.gpencil_add(location=(0,0,0),type='EMPTY')# rename grease pencil
bpy.context.scene.objects[-1].name = gpencil_obj_name
# Get grease pencil object
gpencil = bpy.context.scene.objects[gpencil_obj_name]return gpencil
defget_grease_pencil_layer(gpencil: bpy.types.GreasePencil, gpencil_layer_name='GP_Layer',
clear_layer=False)-> bpy.types.GPencilLayer:"""
Return the grease-pencil layer with the given name. Create one if not already present.
:param gpencil: grease-pencil object for the layer data
:param gpencil_layer_name: name/key of the grease pencil layer
:param clear_layer: whether to clear all previous layer data
"""# Get grease pencil layer or create one if none existsif gpencil.data.layers and gpencil_layer_name in gpencil.data.layers:
gpencil_layer = gpencil.data.layers[gpencil_layer_name]else:
gpencil_layer = gpencil.data.layers.new(gpencil_layer_name, set_active=True)if clear_layer:
gpencil_layer.clear()# clear all previous layer data# bpy.ops.gpencil.paintmode_toggle() # need to trigger otherwise there is no framereturn gpencil_layer
# Util for default behavior merging previous two methodsdefinit_grease_pencil(gpencil_obj_name='GPencil', gpencil_layer_name='GP_Layer',
clear_layer=True)-> bpy.types.GPencilLayer:
gpencil = get_grease_pencil(gpencil_obj_name)
gpencil_layer = get_grease_pencil_layer(gpencil, gpencil_layer_name, clear_layer=clear_layer)return gpencil_layer
defdraw_line(gp_frame, p0:tuple, p1:tuple):# Init new stroke
gp_stroke = gp_frame.strokes.new()
gp_stroke.display_mode ='3DSPACE'# allows for editing# Define stroke geometry
gp_stroke.points.add(count=2)
gp_stroke.points[0].co = p0
gp_stroke.points[1].co = p1
return gp_stroke
defdraw_circle(gp_frame, center:tuple, radius:float, segments:int):# Init new stroke
gp_stroke = gp_frame.strokes.new()
gp_stroke.display_mode ='3DSPACE'# allows for editing#gp_stroke.draw_cyclic = True # closes the stroke#gp_stroke.line_width = 100#gp_stroke.material_index = 1# Define stroke geometry
angle =2*math.pi/segments # angle in radians
gp_stroke.points.add(count=segments)for i inrange(segments):
x = center[0]+ radius*math.cos(angle*i)
y = center[1]+ radius*math.sin(angle*i)
z = center[2]
gp_stroke.points[i].co =(x, y, z)return gp_stroke
defrotate_stroke(stroke, angle, axis='z'):# Define rotation matrix based on axisif axis.lower()=='x':
transform_matrix = np.array([[1,0,0],[0, math.cos(angle),-math.sin(angle)],[0, math.sin(angle), math.cos(angle)]])elif axis.lower()=='y':
transform_matrix = np.array([[math.cos(angle),0,-math.sin(angle)],[0,1,0],[math.sin(angle),0, math.cos(angle)]])# default on zelse:
transform_matrix = np.array([[math.cos(angle),-math.sin(angle),0],[math.sin(angle), math.cos(angle),0],[0,0,1]])# Apply rotation matrix to each pointfor i, p inenumerate(stroke.points):
p.co = transform_matrix @ np.array(p.co).reshape(3,1)defdraw_sphere(gp_frame, center:tuple, radius:int, circles:int):
angle = math.pi / circles
for i inrange(circles):
circle = draw_circle(gp_frame, center, radius,32)
rotate_stroke(circle, angle*i,'x')print(angle * i)
gp_layer = init_grease_pencil()
total_frame =122
bpy.context.scene.frame_start =0
bpy.context.scene.frame_end = total_frame -1for frame inrange(0, total_frame):if frame ==0: gp_frame = gp_layer.frames.new(frame)# 注意这两行,实现了保存以前帧的内容else: gp_frame = gp_layer.frames.copy(gp_frame)
circle = draw_circle(gp_frame,(0,0,0), frame,30)
rotate_stroke(circle, math.pi/50*frame,'x')
rotate_stroke(circle, math.pi/100*frame,'y')
rotate_stroke(circle, math.pi/150*frame,'z')