Animating the (curves) Mean Tilt property.

Hey everyone,

Just wondering if it’s possible to animate the curves “Mean Tilt” property with python.

I know it’s not keyframable but would it be possible to add X amount of degrees to all points in a curve per frame incrementally?

For example I have a rope bezier curve and I’d like to rotate it longitudely along the rope, I can do this by entering edit mode, selecting all curve points and Ctrl+T to rotate the mean tilt, but I really need to animate this.

Thanks a lot for any and all help.

Cheers.

You could write an operator and load it to the frame change handler. I’ve done that for custom, un-keyframe-able animations before.

I wrote some code that may help here. It involves creating your own property that calculates the mean twist. Then you could probably animate that.

Thanks a lot for the code snippet. If it’s not too much to ask would you be able to do a simple blend including the code?

I’ve added a custom property on my curve and I can kind of see what your code is doing, just not 100% on how to implement it.

Or would you be able to put the code into a script that includes import modules etc? Not entirely sure how to implement your code… Thanks

This script creates a panel(used the panel template so it called “Layout Demo”) that has a animatable control for the mean tilt. You’ll find it in Scene properties.


import bpy


def my_handler(scene):
    get_tilt()


def tilt_update(self, context):
    get_tilt()


def get_tilt():
    obj = bpy.context.active_object
    cur = obj.data
    points = cur.splines.active.bezier_points
    rot = cur.mean_tilt
    tilts = [p.tilt for p in points]
    tsum = sum(tilts)
    if tsum > 0:
        mean = tsum/len(points)
    else:
        mean=0
    new_tilt = [t + rot - mean for t in tilts]
    points.foreach_set('tilt', new_tilt)


class LayoutDemoPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Layout Demo"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"


    @classmethod
    def poll(cls, context):
        return context.active_object is not None and context.active_object.type == 'CURVE'


    def draw(self, context):
        layout = self.layout


        scene = context.scene
        cur = context.active_object.data
        
        row = layout.row()
        row.prop(cur, "mean_tilt")
        
def register():
    bpy.utils.register_class(LayoutDemoPanel)
    bpy.types.Curve.mean_tilt = bpy.props.FloatProperty(name='Mean Tilt', step=100.0, default=0, unit='ROTATION', update=tilt_update)
    bpy.app.handlers.frame_change_pre.append(my_handler)
    
def unregister():
    bpy.utils.unregister_class(LayoutDemoPanel)
    del bpy.types.Curve.mean_tilt
    bpy.app.handlers.frame_change_pre.remove(my_handler)
if __name__ == "__main__":
    register()

Thanks so much! One thing I’m noticing is the script only animates 1 curve in the scene, is this due to it using the scene layout panel? If I changed it to the transform panel or somewhere else would that work individually on each curve? Thanks again

Nah, that’s because my original code was made to operate on the active object. I’ll fix it, but tell me exactly what you want it to do.

Ah! I see.

So I have 6 curves that I need spiraling (mean tilt) all at the same rate, not 100% sure on the rate yet as I’ll need to do a few test render sequences. If it’s easy enough to hard code an array of curve object names, I can fill out the curve object names in the script?

Thanks, I really appreciate your help.


import bpy


def my_handler(scene):
    get_tilt()


def get_tilt():
    scn = bpy.context.scene
    rot = scn.mytilt * scn.frame_current
    for obj in bpy.data.objects:
        if obj.type == 'CURVE':
            cur = obj.data
            points = cur.splines.active.bezier_points
            tilts = [p.tilt for p in points]
            tsum = sum(tilts)
            if tsum > 0:
                mean = tsum/len(points)
            else:
                mean=0
            new_tilt = [t + rot - mean for t in tilts]
            points.foreach_set('tilt', new_tilt)
            points[0].tilt = points[0].tilt #force curve to update :P


class LayoutDemoPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Layout Demo"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"


    @classmethod
    def poll(cls, context):
        return context.active_object is not None and context.active_object.type == 'CURVE'


    def draw(self, context):
        layout = self.layout


        scene = context.scene
        
        row = layout.row()
        row.prop(scene, "mytilt")
        
def register():
    bpy.utils.register_class(LayoutDemoPanel)
    bpy.types.Scene.mytilt = bpy.props.FloatProperty(name='Tilt', step=100.0, default=0, min=0, unit='ROTATION')
    bpy.app.handlers.frame_change_pre.append(my_handler)
    
def unregister():
    bpy.utils.unregister_class(LayoutDemoPanel)
    del bpy.types.Curve.mytilt
    bpy.app.handlers.frame_change_pre.remove(my_handler)
if __name__ == "__main__":
    register()



I changed the value in the panel to be used as a multiplier, i.e. tilt * currentFrame, so tilt=2 will change the mean tilt 2 degrees per frame. Things could get wonky if you use high numbers and/or long animations.

why all so complicated?
use shape keys!
https://www.blender.org/manual/animation/shape_keys/index.html
they work same as mesh shape keys, and animate also the tilt.

You can also deform a curve by another curve. I did a lot of animation with it, like dna creation e.t.c. complex stuff but easy if you know your way around.

Hell, I didn’t even know curves had shape keys. :stuck_out_tongue:

Thanks heaps for the update, I will definitely take a look at this! Cheers

Thanks for pointing that out! Can shape keys have linear extrapolation? i.e run forever? I’ll look into this also. Cheers!