Calculating individual spline centers in a curve object

I want to compute the center of each spline from object.data.splines.

To do that I try something like:

for obj in curves:
    for index in range(len(obj.data.splines)):
        temp_obj = obj.evaluated_get(depsgraph).copy()
        temp_obj.data = temp_obj.to_curve(depsgraph).copy()
        # Remove all splines but the current one based on the `index` taken from `obj.data.splines`
        for temp_spline in chain(temp_obj.data.splines[:index], temp_obj.data.splines[index + 1:]):
            temp_obj.data.splines.remove(temp_spline)
        temp_mesh = temp_obj.to_mesh()
        # Compute mean of `temp_mesh.vertices`
        temp_obj.to_mesh_clear()
        temp_obj.to_curve_clear()
        bpy.data.curves.remove(temp_obj.data)

This doesn’t work though because temp_obj.to_mesh() seems to rely on some cached or something based on the splines I remove.

This results in WARN (bke.anim): source/blender/blenkernel/intern/anim_path.c:68 BKE_anim_path_calc_data: No bev list data! after which the spline data gets removed so to_mesh() fails and results in a mesh with no vertices for any index > 0.

In the worst case scenario I have to reconstruct a curve object with a new spline and copy all the individual points taking into account the curve type poly - nurbs, bezier which I’d rather avoid if possible.

Any other ideas on how to get the center of splines or if there’s something I’m missing that would make my implementation work?

You can calculate it in this way:

import bpy


def calculate_centers_of_splines():
        
    for curve in bpy.data.curves:

        for spline in curve.splines:
            
            if spline.type != 'BEZIER':
                print('only bezier splines allowed')
                continue
            
            
            sum_points = [0,0,0]            
            for p in spline.bezier_points:
                for i in range(3):
                    sum_points[i] += p.co[0]
            
            
            total_points = len(spline.bezier_points)
            if total_points == 0:
                continue
            
            result = [0,0,0]
            for i in range(3):
                result[i] = sum_points[i] / total_points
                
            
            print('spline:', spline, "center:", result)
                
                
calculate_centers_of_splines()

Chances that you have to access an enormous amount of curves is slim, from what I see if you have more than a few thousand splines with hundreds of thousands of points then you would have to seek better ways to optimize the code (better to write internal C code within Blender and eject the data as binary file :cowboy_hat_face:).

Your solution doesn’t take into account the shape of the spline based on bezier handles (if they’re used), that’s why I wanted to convert to mesh with temp_obj.to_mesh() because it takes in account all parameters, including resolution, handles etc.

Yeah this way is better, to convert it so you get the final shape.

I figured out a solution that works, I don’t really like it cause it’s definitely a hack due to caching, but it does work for my need:

for obj in source:
    obj_data = obj.data
    for index in range(len(obj.data.splines)):
        temp_obj = obj.evaluated_get(depsgraph).copy()
        temp_obj.data = temp_obj.data.copy()
        for temp_spline in chain(temp_obj.data.splines[:index], temp_obj.data.splines[index + 1:]):
            temp_obj.data.splines.remove(temp_spline)
        obj.data = temp_obj.data
        temp_mesh = obj.to_mesh()
        obj.data = obj_data
        if len(temp_mesh.vertices) > 0:
            vector_mean = temp_obj.matrix_world @ sum((v.co for v in temp_mesh.vertices), Vector()) / len(temp_mesh.vertices)
            # Do something with vector_mean
        obj.to_mesh_clear()
        temp_obj.to_mesh_clear()
        bpy.data.curves.remove(temp_obj.data)

Awesome. :slight_smile:

on what kind of curves can this be use ?

thanks for feedback

happy bl

For POLY or BEZIER curves unless you want to get the handles, then is only BEZIER.

can’t you find the centre with the object spec ?

happy bl

No because it has to do with how the points on average are calculated. I got it wrong at first myself and then understood the problem better.

Screenshot (487)

A: Curve: any object by default can have it’s origin location anywhere so you can’t be sure about which is which (unless you do set origin to cursor etc...)

B: Curve: If you select all points and do set cursor to selection this is where you get the center point. But is only based on three points as the curve has.

C: Mesh: In this converted curve to mesh, now you get all of the vertices in all positions and the result is the real deal.

This algorithm can be used on any type of curve object type: POLY, BEZER & NURBS. I mean to be more specific, on any spline type. Cause in a curve object you can have at the very least POLY & BEZIER at the same time.

how do you define the Source variable in little script

and is it for 2.79 or BL 3.4 ?

and is your script working only for depsgraph or can work for curves in 3D viewport too ?

thanks
happy bl

The source variable is just an iterator of objects (list, tuple, generator etc.) as long as they’re of bpy.types.Object type so for example source = [bpy.data.objects['Cube']] # assuming the Cube object exists.

How is it defined specifically in my script? It’s based on a drop-down property that the user uses to pic objects or collections of objects and I convert those to a list of objects like I mentioned.

Only tried with 3.4.

I don’t understand the question. It works with any object that Blender has in the database. I use depsgraph because I want the “evaluated” object - so objects with modifiers, shape keys etc. applied. I’m pretty sure that the evaluated objects and what we see in the 3D viewport is basically the same thing, cause what we see is the state of the evaluated objects if they have modifiers etc.