# Updating objects parameters using python creates multiple objects

I can update a text curve object when I change the frame and it doesn't create multiple objects but how can I do the same with a mesh object.

Code:

import bpy

scene = bpy.context.scene

font_curve = bpy.data.curves.new(type="FONT",name="Font Curve")
font_curve.body = 'Current Frame: ' + str(scene.frame_current)
font_obj = bpy.data.objects.new("Font Object", font_curve)

def recalculate_obj(scene):
font_curve.body = 'Current Frame: ' + str(scene.frame_current)

def register():
bpy.app.handlers.frame_change_post.append(recalculate_obj)

def unregister():
bpy.app.handlers.frame_change_post.remove(recalculate_obj)

register()


It doesn't create multiple Font objects (this is what I want it just replaces the last one)

But when I try and do it with a mesh object I run into problems due to the "_add"

import bpy

scene = bpy.context.scene

font_curve = bpy.data.curves.new(type="FONT",name="Font Curve")
font_curve.body = 'Current Frame: ' + str(scene.frame_current)
font_obj = bpy.data.objects.new("Font Object", font_curve)

def recalculate_obj(scene):
font_curve.body = 'Current Frame: ' + str(scene.frame_current)

def register():
bpy.app.handlers.frame_change_post.append(recalculate_obj)

def unregister():
bpy.app.handlers.frame_change_post.remove(recalculate_obj)

register()


My goal is to replace the line bpy.ops.mesh.primitive_cylinder_add command with the python line below but have the a, b, c and d variables in the x_eq, y_eq, z_eq animated with out creating multiple objects.

cyl_obj = bpy.ops.mesh.primitive_xyz_function_surface(x_eq = "a+v", y_eq = "b*sin(2*pi*u+c)", z_eq = "cos(2*pi*u)/d")


Swap out the mesh

Just like the font change handler is swapping out the body of the font, ie the data part of the text object, can do same with meshes.

Proof of concept

Use the create surface operator to create an object stepping along v changing the steps and the point to step to, max.

After each call, save the mesh, remove the object.

Script to create the meshes via operator

import bpy
from math import pi

from bpy import context

# default 4pi in 128 steps
step_value = pi / 32
steps = 10
for step in range(1, 129, steps):
bpy.ops.mesh.primitive_xyz_function_surface(
range_v_max=step * step_value,
range_v_step=step,
)
me = context.object.data
me.name = f"XXXX{step}"
me.use_fake_user = True
bpy.data.objects.remove(context.object)



After running this script have a number of "XXXX" meshes

>>> D.meshes['XXXX
1']
101']
11']
111']
121']
21']
31']
41']
51']
61']
71']
81']
91']


Now simply a case of writing a handler to use these.

Handler to swap in the meshes created

import bpy

def swap_ob_mesh(ob_name, mesh_base_name):

def handler(scene, depsgraph):
ob = scene.objects.get(ob_name)
f = scene.frame_current
me = bpy.data.meshes.get(f"{mesh_base_name}{f}")
if ob and me:
ob.data = me

return handler

# while testing
bpy.app.handlers.frame_change_post.clear()

# test call

bpy.app.handlers.frame_change_post.append(
swap_ob_mesh("Cube", "XXXX")
)


Notes: IMO never mix context and handlers. When it comes to render most likely wont work.

Consider shape keys. If instead simply ran the operator only changing v max (not steps) if all the meshes produced have the same number of verts / edges / faces simply join them as shapes and animate the shape key.

To demonstrate the "stepping" nature of swapping the mesh have used 10 frame steps. Reducing this to 1 will be as smooth as it gets.

On a side note this is a "proof of concept" scrubbing the timeline may produce unexpected results since the mesh is only swapped when exists.

• I hear you. if not done "correctly" and you mix context and handlers incorrectly when scrubbing with the timeline it will cause the memory to increase every time you scub the time line (and the memory usage will NOT go down) also some other strange things happen when switching between edit mode and object mode. I found some useful python code and connected it to Animation Nodes and will post it as a workaround / one of the correct ways to do this. – Rick T Jul 23 '20 at 2:47

Another way you can do this is by just using Python. (Don't use this way, unless you have to. This will increase your memory usage every-time you scrub the time-line and the memory WILL NOT GO DOWN!!!)

Notice the memory usage!:

Python code to run:

import bpy
import math

def my_handler(scene):
# Deselect all
bpy.ops.object.select_all(action='DESELECT')

# Select the object
if bpy.data.objects.get("Hyperboloid") is not None:
bpy.data.objects['Hyperboloid'].select_set(True)

bpy.ops.object.delete()

# mesh arrays
verts = []
faces = []

# mesh variables
numX = 50
numY = 50

# get frame value
frame = bpy.context.scene.frame_current

# fill verts array
for i in range (0, numX):
for j in range(0,numY):
# nomalize range
u = 8*(i/numX-1/2)
v = 2*math.pi*(j/(numY-1)-1/2)

x = 2*math.sqrt(1+u*u)*math.cos(v)*(frame/12)
y = 2*math.sqrt(1+u*u)*math.sin(v)*(frame/12)
z = 12*u/(frame/12+0.01)

vert = (x,y,z)
verts.append(vert)

# fill faces array
count = 0
for i in range (0, numY *(numX-1)):
if count < numY-1:
A = i
B = i+1
C = (i+numY)+1
D = (i+numY)
face = (A,B,C,D)
faces.append(face)
count = count + 1
else:
count = 0

# create mesh and object
mesh = bpy.data.meshes.new("Hyperboloid")
object = bpy.data.objects.new("Hyperboloid",mesh)

# set mesh location
object.location = bpy.context.scene.cursor.location

# create mesh from python data
mesh.from_pydata(verts,[],faces)
mesh.update(calc_edges=True)

# assign a material to the newly created curve
mat = bpy.data.materials.get("Material")
object.data.materials.append(mat)

# active smooth shading to object
bpy.data.objects['Hyperboloid'].select_set(True)
bpy.ops.object.select_all(action='DESELECT')

# Add the handler to handlers
bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.frame_change_pre.append(my_handler)


To avoid mixing context and handlers together as @batFINGER mentioned a workaround is below.

Combine some Python code with the Animation Nodes plugin.

Python Code Used in eq_2:

import bpy
import math

def parametricfunc(val1):

# mesh arrays
verts = []
faces = []

# mesh variables
numX = 50
numY = 50

# fill verts array
for i in range (0, numX):
for j in range(0,numY):
# nomalize range
u = 8*(i/numX-1/2)
v = 2*math.pi*(j/(numY-1)-1/2)

x = 2*math.sqrt(1+u*u)*math.cos(v)*(val1/12)
y = 2*math.sqrt(1+u*u)*math.sin(v)*(val1/12)
z = 12*u/(val1/12+0.01)

vert = (x,y,z)
verts.append(vert)

count = 0
for i in range (0, numY *(numX-1)):
if count < numY-1:
A = i
B = i+1
C = (i+numY)+1
D = (i+numY)
face = (A,B,C,D)
faces.append(face)
count = count + 1
else:
count = 0