Equal Bezier parameter steps do not mean equal travel

If waypoints bunch up near one end of a Bezier curve, equal parameter increments may be doing exactly what you requested. The parameter t is not distance along the path. On the cubic used here, 17 equally spaced parameter values produce consecutive straight-line displacements from about 9.409 mm to 155.483 mm—a ratio of 16.526. Giving those intervals equal timestamps does not make their distances equal.

For more even path spacing, one practical construction is to sample a denser reference polyline, accumulate its segment lengths, and place new points at equal cumulative distances. That is an approximation based on a finite polyline. It does not establish exact continuous-curve arc spacing, instantaneous speed, or what a controller does between supplied waypoints.

The first handle makes the difference visible

Use these four control points, in metres, in knot–handle–handle–knot order: P0=(0,0,0), P1=(1/64,0,0), P2=(1/8,3/4,0), and P3=(1,3/4,0). All points lie in one plane. The first handle is very close to the starting knot, while the final handle is much farther from the ending knot.

Evaluate the cubic at t=0, 1/16, 2/16, through 1. These are 17 points and 16 intervals. Equal steps through that list do not correspond to equal movements across the drawing. For example, the magnitude of the curve’s derivative is 0.046875 m per unit of t at the start and 2.625 m per unit of t at the end. Those are geometric derivatives, not measured speeds in metres per second.

Blender’s curve interpolation documentation defines resolution as the number of points returned. It does not promise equal arc-distance spacing. The examined Blender wrapper revision passes one fewer interval to a forward-difference routine. That source revision is not asserted to be the installed implementation.

An AI-operated Blender 5.2.2 LTS experiment on October 6, 2026 requested 17 and 129 points from the actual interpolation API. An independent calculation checked every returned vector against the Bernstein cubic at t=i/(N−1). The largest component difference was below 2.3e-8 m in this particular run. This is a numerical comparison of prescribed coordinates, not measured physical accuracy or a general error guarantee for other curves.

Actual cubic waypoint centers show bunching with equal parameter steps and more even finite-polyline spacing; consecutive endpoint chords are compared below
Top views of actual evaluated Blender reference edges, sample edges and finite marker boxes, with display dots at independently recovered box centers. Both alternatives use the same coordinates; the reference is displayed twice. Dot size is a display symbol and does not enlarge the 3.90625 mm boxes. Gray follows the 129-point reference. The lower chart uses measured-center endpoint chords, not traveled arc distances. Units are millimeters. This is a Matplotlib projection of recorded geometry, not a Blender camera render or motion test.

Choose the distance you want to equalize

The reference polyline connects 129 cubic samples at t=i/128. Its calculated length is approximately 1.357148 m. Divide that cumulative length into 16 equal parts: each target interval is about 84.822 mm along the reference polyline. Locate each target distance within a reference segment, then linearly interpolate its point on that segment.

The resulting 17 points are much more evenly distributed, but two different distance measurements remain:

Sampling method Smallest endpoint chord Largest endpoint chord Largest/smallest
17 equal parameter samples 9.409 mm 155.483 mm 16.526
17 equal reference-polyline-distance samples 84.528 mm 84.810 mm 1.003

An endpoint chord is the straight line between consecutive new points. A cumulative-distance interval follows the intervening reference segments and may cross several corners. The chord cuts across those turns, so its length can be smaller. In the second row, the target cumulative interval is the same 84.822 mm each time; the endpoint chords are still slightly unequal. Calling those chords exactly equal would erase a real distinction in the construction.

This matters when deciding what a waypoint consumer receives. If it connects the new points directly with straight segments, it receives the new chordal path, not every corner of the 129-point reference. Equal distances assigned along the reference do not guarantee equal distance along that different connected path. If it reconstructs the original smooth curve instead, the finite reference is only an approximation to that curve’s arc length.

For a numerical timing illustration only, assign 0.125 s to every interval. The equal-parameter chord distances divided by that interval range from approximately 0.075 to 1.244 m/s. The equal reference-polyline target distance divided by the same interval is approximately 0.678574 m/s. The latter is a path-distance interval average under the stated polyline-following interpretation. Neither quantity is a measurement of motion, and neither establishes instantaneous speed between endpoints.

Reproduce the spacing before attaching a motion claim

This standard-library example has been executed. It evaluates the specified curve directly, then resamples the finite reference; it does not invoke Blender or a motion API. The coordinates and point counts below reproduce the comparison without a model download or external dependency.

import bisect
import math

P = [(0, 0, 0), (1/64, 0, 0), (1/8, 3/4, 0), (1, 3/4, 0)]
def curve(t):
    weights = [(1-t)**3, 3*t*(1-t)**2, 3*t*t*(1-t), t**3]
    return tuple(sum(w*p[j] for w, p in zip(weights, P)) for j in range(3))

parameter = [curve(i/16) for i in range(17)]
reference = [curve(i/128) for i in range(129)]
distance = [0.0]
for a, b in zip(reference, reference[1:]):
    distance.append(distance[-1] + math.dist(a, b))
assert distance[-1] > 0
equal_distance = []
for i in range(17):
    target = distance[-1]*i/16
    k = min(bisect.bisect_right(distance, target)-1, 127)
    f = (target-distance[k])/(distance[k+1]-distance[k])
    equal_distance.append(tuple((1-f)*a+f*b for a, b in zip(reference[k], reference[k+1])))

for label, points in [("equal parameter", parameter), ("finite-polyline distance", equal_distance)]:
    chords = [math.dist(a, b) for a, b in zip(points, points[1:])]
    print(f"{label}: chord min={min(chords):.6f} m, max={max(chords):.6f} m, ratio={max(chords)/min(chords):.3f}")
print(f"129-point polyline length={distance[-1]:.6f} m")
print(f"Equal polyline target per interval={distance[-1]/16:.6f} m")
print(f"With a supplied 0.125 s interval: polyline-path average={distance[-1]/16/0.125:.6f} m/s")

The denominator in the interpolation step needs a positive segment length. For a different curve, reject a zero-length reference or explicitly handle repeated consecutive points before using this construction. The example assumes the stated curve, whose reference segments all have positive length. An equally parameterized straight cubic with equally spaced controls is a useful control: its chord intervals are uniform, so unequal spacing is not inevitable for every Bezier curve.

The native experiment retained 37 objects, 435 evaluated vertices, 204 faces and all 146 raw API vectors. Each sample marker is a closed cube of side 3.90625 mm. The two alternatives occupy separate collections at the same world coordinates and frame 1; they are a catalogue of sample sets, not simultaneous physical carriages. The reference and sample lines are edge meshes with no physical thickness. No between-frame playback, hardware timing, dynamics, or clearance test was executed.

The independent audit recovered each marker center from all eight actual world vertices and checked the full local and world meshes, edge and face topology, transforms, local normals and ordered correspondence with the sample lines. It separately reconstructed cumulative reference distances and checked the resampled points and stations. Complete raw API and geometry logs, vertex CSV, source, inputs and scene library were preserved; original scenes and context were compared before and after.

Predeclared bounds included 1e-5 m per component for API coordinates versus the polynomial, 1 µm for mesh coordinates, 2e-7 m for raw reference-polyline resampling and 4 µm for reconstructed center and distance checks. These are numerical acceptance criteria for this study. Standalone synthetic controls rejected 31 corruptions and eight invalid inputs, and the pure producer-function check rejected three invalid constructions. A straight uniform cubic passed as a positive control. A synthetic binary32 proxy rounded polynomial outputs and meshes; it did not reproduce the uninspected forward-difference implementation or count as an additional native run.

If the points will support a robot path check, whole-arm path geometry and collision remain separate questions. More even waypoint spacing does not establish that a tool or the rest of an arm clears an obstacle between samples.

Before treating a sample index as progress through a path, specify whether progress means parameter, endpoint displacement, or distance along a particular reference. Then carry that same meaning into timestamps and downstream interpolation. The 17-point list alone does not choose it for you.