Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
8 views

Excel to 3D Graph Script

Uploaded by

shazshahil
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Excel to 3D Graph Script

Uploaded by

shazshahil
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 7

###########################################################################

#
# Python Script To Read Data From An Excel File & Create Animated 3D Bars
#
# This script is originally written by 5 Minutes Blender YouTube channel
#
###########################################################################

import bpy
import math
import pandas as pds

context = bpy.context
scene = context.scene

###########################################################################
###########################################################################
#
# CHANGE THE FOLLOWING INPUT AS PER YOUR EXCEL FILE
# BE CAREFUL - ANY WRONG INPUT WILL RAISE AN ERROR.

excel_file_path = r"C:\Data\Database.xlsx"
data_column = 3
month_column = 2
currency_symbol = "$"

###########################################################################
###########################################################################

anim_start_frame = 2
anim_length_data = 20
graph_start_position = 1
distance_bet_points = 2

MAJOR_VERSION, MINOR_VERSION, SUB_VERSION = bpy.app.version

# Save the current location of the 3D cursor


saved_cursor_loc = scene.cursor.location.xyz

# Read excel file and store the data in an array


xls_data = pds.read_excel(excel_file_path)
data_list = xls_data.iloc[:,data_column-1]
month_list = xls_data.iloc[:,month_column-1]
number_of_data = len(month_list)
data_height_mean = sum(data_list) / number_of_data

# Initialize the variables.


position_count = graph_start_position
anim_length_text = anim_length_data / 2
anim_curr_frame = anim_start_frame
anim_end_frame = anim_start_frame + anim_length_data * (number_of_data-1)

normalized_data = []
for data in data_list:
normalized_data.append(data * 10/data_height_mean)

data_height_mean = sum(normalized_data) / number_of_data


data_height_min = min(normalized_data)
display_data = []
if (data_height_min > abs(data_height_mean - data_height_min)):
for data in normalized_data:
display_data.append(data - data_height_min + abs(data_height_mean -
data_height_min))
else:
for data in normalized_data:
display_data.append(data)

# Create a new material for the curve


material_1 = bpy.data.materials.new(name = "anim_material_1")
material_1.use_nodes = True
if material_1.node_tree:
material_1.node_tree.links.clear()
material_1.node_tree.nodes.clear()
nodes = material_1.node_tree.nodes
links = material_1.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeEmission')
nodes["Emission"].inputs['Color'].default_value = (1.0, 0.3, 0.0, 1)
nodes["Emission"].inputs['Strength'].default_value = 1.5
links.new(shader.outputs[0], output.inputs[0])

# Create a new material for the text


material_2 = bpy.data.materials.new(name = "anim_material_2")
material_2.use_nodes = True
if material_2.node_tree:
material_2.node_tree.links.clear()
material_2.node_tree.nodes.clear()
nodes = material_2.node_tree.nodes
links = material_2.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeEmission')
nodes["Emission"].inputs['Strength'].default_value = 3.0
links.new(shader.outputs[0], output.inputs[0])

# Create a new material for the x-axis


material_3 = bpy.data.materials.new(name = "anim_material_3")
material_3.use_nodes = True
if material_3.node_tree:
material_3.node_tree.links.clear()
material_3.node_tree.nodes.clear()
nodes = material_3.node_tree.nodes
links = material_3.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeBsdfPrincipled')
nodes["Principled BSDF"].inputs[0].default_value = (1.0, 0.05, 0.135, 1)
links.new(shader.outputs[0], output.inputs[0])

# Create a new material for the z-axis


material_4 = bpy.data.materials.new(name = "anim_material_4")
material_4.use_nodes = True
if material_4.node_tree:
material_4.node_tree.links.clear()
material_4.node_tree.nodes.clear()
nodes = material_4.node_tree.nodes
links = material_4.node_tree.links
output = nodes.new(type='ShaderNodeOutputMaterial')
shader = nodes.new(type='ShaderNodeBsdfPrincipled')
nodes["Principled BSDF"].inputs[0].default_value = (0.0, 0.24, 0.6, 1)
links.new(shader.outputs[0], output.inputs[0])

# Create a curve and add it to the scene


curve = bpy.data.curves.new(name = "data_curve", type = 'CURVE')
curve.dimensions = '3D'
curve_path = bpy.data.objects.new("my_curve", curve)

bezier_curve = curve.splines.new('BEZIER')
bezier_curve.bezier_points.add(number_of_data-1)

for bezier, data in zip(bezier_curve.bezier_points, display_data):


bezier.co = (position_count, 0, data)
position_count = position_count + distance_bet_points

context.scene.collection.objects.link(curve_path)
curve_path.select_set(True)
context.view_layer.objects.active = curve_path
bpy.ops.object.editmode_toggle()
bpy.ops.curve.select_all(action='SELECT')
bpy.ops.curve.handle_type_set(type='AUTOMATIC')
bpy.ops.object.editmode_toggle()

# Assign the yellow material created above


curve_path.data.materials.append(material_1)

# Add a sphere and set its dimensions


bpy.ops.mesh.primitive_uv_sphere_add(radius = 0.15)
sphere = context.active_object
sphere.location = [0,0,0]

# Assign the yellow material created above


sphere.data.materials.append(material_1)

follow_path = sphere.constraints.new(type='FOLLOW_PATH')
follow_path.target = curve_path
follow_path.forward_axis = 'TRACK_NEGATIVE_Z'
follow_path.up_axis = 'UP_Y'
follow_path.use_fixed_location = True
follow_path.offset_factor = 0.0
follow_path.keyframe_insert("offset_factor", frame=anim_start_frame)
follow_path.offset_factor = 1.0
follow_path.keyframe_insert("offset_factor", frame=anim_end_frame)

fcurves = sphere.animation_data.action.fcurves
for fcurve in fcurves:
for kf in fcurve.keyframe_points:
kf.interpolation = 'LINEAR'
kf.easing = 'AUTO'
bpy.ops.constraint.followpath_path_animate(constraint=follow_path.name)

def geometry_nodes_node_group(start_frame, end_frame, material):

geometry_nodes = bpy.data.node_groups.new(type = "GeometryNodeTree", name =


"Geometry Nodes")

if (MAJOR_VERSION >= 4):


geometry_nodes.interface.new_socket('NodeInterfaceInput', in_out='INPUT',
socket_type='NodeSocketGeometry')
else:
geometry_nodes.inputs.new("NodeSocketGeometry", "Geometry")

group_input = geometry_nodes.nodes.new("NodeGroupInput")
group_input.location = (-340.0, 0.0)
group_input.width, group_input.height = 140.0, 100.0

if (MAJOR_VERSION >= 4):


geometry_nodes.interface.new_socket('NodeInterfaceOutput', in_out='OUTPUT',
socket_type='NodeSocketGeometry')
else:
geometry_nodes.outputs.new("NodeSocketGeometry", "Geometry")

group_output = geometry_nodes.nodes.new("NodeGroupOutput")
group_output.location = (609.8951416015625, 0.0)
group_output.width, group_output.height = 140.0, 100.0

trim_curve = geometry_nodes.nodes.new("GeometryNodeTrimCurve")
trim_curve.location = (-63.592041015625, 22.438913345336914)
trim_curve.width, trim_curve.height = 140.0, 100.0
trim_curve.mode = 'FACTOR'
trim_curve.inputs[1].default_value = True
trim_curve.inputs[2].default_value = 0.0
trim_curve.inputs[3].default_value = 0.0
trim_curve.inputs[3].keyframe_insert('default_value', frame=start_frame)
trim_curve.inputs[3].default_value = 1.0
trim_curve.inputs[3].keyframe_insert('default_value', frame=end_frame)

curve_to_mesh = geometry_nodes.nodes.new("GeometryNodeCurveToMesh")
curve_to_mesh.location = (169.89512634277344, 18.004777908325195)
curve_to_mesh.width, curve_to_mesh.height = 140.0, 100.0
curve_to_mesh.inputs[2].default_value = False

curve_circle = geometry_nodes.nodes.new("GeometryNodeCurvePrimitiveCircle")
curve_circle.location = (-340.7394104003906, -86.51416015625)
curve_circle.width, curve_circle.height = 140.0, 100.0
curve_circle.mode = 'RADIUS'
curve_circle.inputs[0].default_value = 32
curve_circle.inputs[1].default_value = (-1.0, 0.0, 0.0)
curve_circle.inputs[2].default_value = (0.0, 1.0, 0.0)
curve_circle.inputs[3].default_value = (1.0, 0.0, 0.0)
curve_circle.inputs[4].default_value = 0.03

set_material = geometry_nodes.nodes.new("GeometryNodeSetMaterial")
set_material.location = (389.71429443359375, 25.688528060913086)
set_material.width, set_material.height = 140.0, 100.0
set_material.inputs[1].default_value = True
set_material.inputs[2].default_value = material

geometry_nodes.links.new(set_material.outputs[0], group_output.inputs[0])
geometry_nodes.links.new(group_input.outputs[0], trim_curve.inputs[0])
geometry_nodes.links.new(trim_curve.outputs[0], curve_to_mesh.inputs[0])
geometry_nodes.links.new(curve_circle.outputs[0], curve_to_mesh.inputs[1])
geometry_nodes.links.new(curve_to_mesh.outputs[0], set_material.inputs[0])

fcurves = geometry_nodes.animation_data.action.fcurves
for fcurve in fcurves:
for kf in fcurve.keyframe_points:
kf.interpolation = 'LINEAR'
kf.easing = 'AUTO'

return geometry_nodes

geometry_nodes = geometry_nodes_node_group(anim_start_frame, anim_end_frame,


material_1)
modifier = curve_path.modifiers.new("Geometry Nodes Temp", "NODES")
modifier.node_group = geometry_nodes

# Create the text fields in a loop


data_counter = 0
anim_curr_frame = anim_start_frame

while (data_counter < number_of_data):

text_month = str(month_list[data_counter])
text_data = currency_symbol + str(data_list[data_counter])

# Add a sphere, set its location and animate its size


bpy.ops.mesh.primitive_uv_sphere_add(radius = 0.15)
sph = context.active_object
sph.location = [graph_start_position+distance_bet_points*data_counter, 0,
display_data[data_counter]]
sph.scale = [0,0,0]
sph.keyframe_insert(data_path="scale", frame = anim_curr_frame+4)
sph.scale = [1,1,1]
sph.keyframe_insert(data_path="scale", frame = anim_curr_frame+6)

# Assign the yellow material created above


sph.data.materials.append(material_1)

# Add the 1st caption


bpy.ops.object.text_add()
ob = bpy.context.object
ob.data.body = text_month
ob.data.align_x = "CENTER"
ob.data.align_y = "CENTER"
ob.data.extrude = 0.01

ob.location = [graph_start_position+distance_bet_points*data_counter, 0,
display_data[data_counter]+1.5]
ob.rotation_euler = [math.radians(90),0,0]

# Animate the caption horizontally


ob.scale = [0,0,0]
ob.keyframe_insert(data_path="scale", frame = anim_curr_frame-1)
ob.scale = [0,0.5,0.5]
ob.keyframe_insert(data_path="scale", frame = anim_curr_frame)
anim_curr_frame += anim_length_text
ob.scale = [0.5,0.5,0.5]
ob.keyframe_insert(data_path="scale", frame = anim_curr_frame)

# Assign the white material created above


ob.data.materials.append(material_2)

anim_curr_frame -= anim_length_text

# Add the 2nd caption


bpy.ops.object.text_add()
ob = bpy.context.object
ob.data.body = text_data
ob.data.align_x = "CENTER"
ob.data.align_y = "CENTER"
ob.data.extrude = 0.01

ob.location = [graph_start_position+distance_bet_points*data_counter, 0,
display_data[data_counter]+1]
ob.rotation_euler = [math.radians(90),0,0]

# Animate the caption horizontally


ob.scale = [0,0,0]
ob.keyframe_insert(data_path="scale", frame = anim_curr_frame-1)
ob.scale = [0,0.5,0.5]
ob.keyframe_insert(data_path="scale", frame = anim_curr_frame)
anim_curr_frame += anim_length_text
ob.scale = [0.5,0.5,0.5]
ob.keyframe_insert(data_path="scale", frame = anim_curr_frame)

# Assign the white material created above


ob.data.materials.append(material_2)

#increase the loop counters


data_counter += 1
anim_curr_frame -= anim_length_text
anim_curr_frame += anim_length_data

# Add x-axis and set its dimensions


bpy.ops.mesh.primitive_cube_add()
ob = context.active_object
axis_length = graph_start_position + distance_bet_points * (number_of_data - 1) + 2
ob.dimensions = [axis_length,0.05,0.05]
ob.location = [axis_length/2,0,0]

# Assign the red material created above


ob.data.materials.append(material_3)

bpy.ops.mesh.primitive_cylinder_add(vertices = 3, radius = 0.3, depth = 0.1)


cyl1 = context.active_object
cyl1.location = [axis_length, 0, 0]
cyl1.scale = [1,1.7,1]
cyl1.rotation_euler = [0,math.radians(90),-math.radians(90)]

# Assign the red material created above


cyl1.data.materials.append(material_3)

# Add z-axis and set its dimensions


bpy.ops.mesh.primitive_cube_add()
ob = context.active_object
axis_height = max(display_data) + 3
ob.dimensions = [0.05,0.05,axis_height]
ob.location = [0,0,axis_height/2]

# Assign the blue material created above


ob.data.materials.append(material_4)

bpy.ops.mesh.primitive_cylinder_add(vertices = 3, radius = 0.3, depth = 0.1)


cyl2 = context.active_object
cyl2.location = [0, 0, axis_height]
cyl2.scale = [1,1.7,1]
cyl2.rotation_euler = [math.radians(90),0,0]

# Assign the blue material created above


cyl2.data.materials.append(material_4)

# Clean-up work
# Reset 3D cursor location back to the original
scene.cursor.location.xyz = saved_cursor_loc
context.active_object.select_set(False)

# Set the current frame to frame# 1


scene.frame_set(1)

# Set the scene length


scene.frame_start = 1
scene.frame_end = anim_end_frame + 50

# Turn on bloom effect


scene.render.engine = 'BLENDER_EEVEE'
scene.eevee.use_bloom = True

You might also like