Curve Length and Converted Mesh Length Mismatch

Hello Everyone!

I have simple scene containing a Bezier curve (‘BezierCurve’) with a shrinkwrap modifier targeted to a UV sphere (‘Sphere’). I duplicated the curve and converted it to a mesh (‘BezierCurve_Mesh’) and tried calculating the length of the two objects (original and converted curve).
For the curve I used calc_length of the spline and for the mesh the sum of calc_length of all the edges (blend file: https://drive.google.com/file/d/1atr3E-4d7WAhWHm8pxF0sMxm-sVXXJMm/view?usp=sharing). Here’s the script for your quick reference:

    import bpy, bmesh

    depsgraph = bpy.context.evaluated_depsgraph_get()

    #Calculate curve length
    curve = bpy.data.objects['BezierCurve']
    curveLength = sum(s.calc_length() for s in curve.evaluated_get(depsgraph).data.splines)

    #Calculate curve mesh length
    curveMesh = bpy.data.objects['BezierCurve_Mesh']

    meshEvalData = curveMesh.evaluated_get(depsgraph).data
    bm = bmesh.new()
    bm.from_mesh(meshEvalData)
    bm.edges.ensure_lookup_table()

    meshLength = sum(e.calc_length() for e in bm.edges)
    bm.free()


    print('Mesh Length:', meshLength)
    print('Curve Length:', curveLength)

Now since the mesh has a large number of vertices, I expected the two lengths to be fairly close to each other.
But the output is: Mesh Length: 1.64 and Curve Length: 2.08.

What could be causing this discrepancy?

Thanks
Shrinivas

might be worth trying a bug report. With the file you provided, once you apply the Shrinkwrap modifier on the Curve, the lengths are:

Mesh Length: 1.6413958818884566
Curve Length: 1.6350018978118896

So it seems as if even though you call the evaluated_get() method, the modifier is not taken into account, which looks like a bug.

I was also suspecting that it could be a bug.
Thank you, I will open a bug report.

I don’t think there’s a bug in here, pretty sure that spline.calc_length() always operates on the undeformed points. Although, calling for an evaluated depsgraph should give you the points with their deformation applied-- In which case, maybe you’re still running the operation on the original object, not the evaluated data (as you are with the mesh)? That’s what it looks like to me.

I’ve been hoping for an easy way to calculate spline length for rigging for a while. I hope this will be addressed. Shameless plug for my RCS post about getting spline length in Python: https://blender.community/c/rightclickselect/4ncbbc/

@RainerTrummer @Josephbburg

Yes, looks like evaluated_get() doesn’t behave the same way in case of curves as with meshes.

Here’s the reply I got to the bug report:

curve.evaluated_get(depsgraph).data is not the curve with modifiers applied. This is inconsistent with the way meshes work currently, but it’s not considered a bug.

The reason I thought of comparing the two lengths is following:
I have created an add-on that generates writing animation keyframes for selected set of Bezier curves. I wrote (a rather crude) function to interpolate the points along the curve to position the writer object. I change this position in tandem with the offset value of the curve to create the writing animation.
This works fine with curves without modifiers. With curves having modifiers, the coordinates of the interpolated points seem to be correct (I tested this with another script), but the offset curve length is not in sync.
So there does seem to be a bug somewhere with curve length calculation when it has modifiers.
But of course, I could be totally wrong here and there could be some fundamental issue with my code as I am comparitively new to Blender and Python.

Here’s the link to code repository in case you are interested:

Script File: writinganim_2_8.py

Perhaps you can get the evaluated mesh datablock from the curve? A common way of measuring curves in Blender is to convert the object to a mesh and sum the length of its edges.
I think your script isn’t working because spline.calc_length() calculates the length of of the curve data, before any modifiers are applied. If you can find a way to tell Python the location of the points and handles of Bezier curves, you might try mathutils.geometry.interpolate_bezier(), but I’m not sure there’s an easy way to do that.

I did some research about this a while back, and found that subdividing a bezier curve results in identical shape if the handle types are not set to Vector, so you could subdivide it a bunch of times and measure the distance between curve points. Also, NURBS seems to use the same interpolation method as the subsurf modifier for wire-meshes (mesh with no faces). So perhaps you could use NURBS curves, convert the control points to a mesh and subdivide.

Meshes tend to be the easiest to handle in Python, though, and if what you’re doing is not time sensitive, summing the edges of a mesh is the best way to do it. take a look at the RCS post I linked, there’s a link to a stack exchange with code for what I’m describing.