# Blender array modifier "fit curve" when using scaled "object offset"

I have a curve and I'm repeating an object along that curve. Setting the array modifier on the object to "fit curve" works great until I also use "object offset" with a scale to get a taper. If I avoid scaling in the curve direction, it still works, but if the offset object is scaled in the curve direction (getting shorter in the curve direction along the curve) then the curve is too short.

All scales are applied, origins are the same, and everything works with relative, fixed, or object-based offsets without a scale. It also "works" if I just use a manual array count. But the "fit curve" array count does not seem to take scale into account. The array is too short whether I use relative, fixed, or object-based offsets to get the displacement. If the "object offset" is scaled in the "deform axis" of the curve modifier it ends up too short.

Am I missing something else? • Hello, it would help if you show some pictures of your problem Jul 28, 2021 at 9:04

I've found a workaround with drivers:

# Math

First, an array of $$m$$ segments with base segment length $$l_s$$ and an incremental per-segment scale of $$s$$ is $$\sum_{n=0}^{m-1}l_ss^n$$ We can move the $$l_s$$ to the outside and ask Wolfram Alpha to simplify the sum: $$l_s\left(\frac{s^m-1}{s-1} \right)$$ Then we can find $$m$$ for a target length $$l_t$$ as $$\log_s(\frac{l_t}{l_s}(s-1)+1)$$ And we can simplify the $$\log_s$$ by dividing it out: $$\frac{\log(\frac{l_t}{l_s}(s-1)+1)}{\log s}$$

So written in Python form, log(target_len/segment_len*(scale - 1) + 1)/log(scale) and since that could be a fractional count, we can decide if we want to round up or down with ceil() or floor().

# Implementation

Now the tricky part: How to get the inputs to this equation with drivers:

## Target Length

Finding target_len aka $$l_t$$: This must be computed as the sum of the calculated lengths of the splines in the curve. It has to be split into two parts, one part is the "input variable" which accesses the data path of the curve object: then your expression needs sum([s.calc_length() for s in trunk.splines]) for target_len

(of course this assumes all scales applied)

## Segment Length

This is ugly for multiple reasons:

1. It depends on how you configured your curve modifier (not taken into account here) deform axis. This example assumes Z.
2. It assumes your base object's origin is at the base of the mesh.
3. It assumes all of your scales are applied.
4. It can't use dimensions.z on the object because the dimensions will change with the application of array and cancel out all of the math, it must look into the mesh directly.
5. Meshes don't know their own bounds, so we have to compute them from the vertices!
6. Again we must split into two parts, accessing the vertices property of the mesh (note the type changed to mesh and the mesh selected, not the object): Then the expression needs (max([v.co for v in base_vertices])-min([v.co for v in base_vertices])) to find the Z extents of the mesh.

## Scale

This one is easy! It's just the Z scale of the empty used in the "object offset" of the array modifier. (Again, Z because it's the deform axis of the curve modifier on the array object): ## Expression

Finally your expression just combines the above as ceil(log(sum([s.calc_length() for s in trunk.splines])/(max([v.co for v in base_vertices])-min([v.co for v in base_vertices]))*(scale - 1) + 1)/log(scale)) (here with ceil() because I want at least enough to cover).

Note that this equation can explode, because if $$\sum s^n$$ converges, then its limit sets the maximum possible length of the array. This can happen if scale is too small, the segments are too short, or the curve gets too long. If any of those happen, the array will quit applying (stuck at 1) until you edit the driver and edit the expression field (just clicking on it and clicking out will fix it). It should be possible to fix this with even more math in the expression box, but that is left as an exercise for the reader.

# Action Shot 