# Zig-Zag bone folding animation sways

## Context

Imagine a space radiator panel, where it could fold / unfold in zig-zag manner.

I naively implemented it using 9-boned armature with each 2nd ones being rolled 180d in order to make it fold without overlapping, while using single mesh and linked meshes of it so I only have to edit one section.

So when simplified it looks like this:

bone idx(from root) bone length bone roll mesh
0 0.5 180d A
1 1 0d B
2 1 180d Link of B
3 1 0d Link of B
4 1 180d Link of B
5 1 0d Link of B
6 1 180d Link of B
7 1 0d Link of B
8 1 180d Link of B
9 1 0d Link of B

## Issue with Manual rotation

Thinking this will be simple job, I only set two frame for rotation only. Rest pose (straight) as first, and all-folded one as end frame.

Since root only have to rotate half of the rest of those, I tested this idea by setting end frame to have root rotation to 90d, everything else as either 180d or -180d, double of root's rotation (with some sign diff).

(Recreation of such process. Not shown, but typed exact values every rotation)

However to my surprise, it kept swaying left and right(in sideway view perspective), not folding in straight - regardless of interpolations and such.

On the other hand, one I did with constraints with double copy modifier to follow the same 'calculation'(if you can call as one) with duplicated Copy rotation bone constraints do work fine, so my logic probably isn't issue.

## Question

What could've went wrong on when animating by typing rotation manually?

## Edit

After setting keyframe to vector & interpolation to linear (rotated in object mode for easier view)

Sample file:

• Probably, your interpolation curve. An auto-curve may create a different interpolation for the rotation of theta compared to the rotations of 2 * theta-- between keyframes, they won't match. Look on the graph editor and try changing your interpolation curves to linear. Commented Apr 28 at 13:32
• @Nathan for that reason I searched up and disabled interpolation, and set each frames(only start end tho) to linear manually to doublely make sure - I'll check graph editor too, hold up! Commented Apr 28 at 16:13
• @Xana Jupiterbjy isn't having troubles with the constrained version: "bone constraints do work fine"; as well as the video demonstrating proper action with constraints. As far as I can tell, the question is why it's happening on keyframed rotations (dope sheet screenshot.) Commented Apr 28 at 16:26
• @Nathan Finally found where the graph editor is, indeed there was still interpolation ongoing, selected all and replaced as vector & linear again, and still some sway exists; lemme post new gif and blend file in 5 mins Commented Apr 28 at 16:48
• @Nathan up now, sorry for delay - first time using blend-exchange if I am allowed to excuse! Commented Apr 28 at 16:59

You have a fan made up of quaternion bones, where one bone is rotating from 0 to 90 degrees, and the other bones are rotating from 0 to 180 degrees. All, with linear interpolation. It bends off to the side. Not at the exact middle of the interpolation, but at the first and third quarter of the interpolation.

Why? Because of how Blender handles quaternions.

I'm at frame 25. I'm comparing your quaternion fan to an Euler fan behind it-- notice that the Euler fan works. I'm also copying rotation from the first two bones to Euler bones off to the side, so that I can apply visual transforms and read off the angles. We see, despite linear interpolation, that the second bone in the fan has 35.5 degrees rotation, whereas with linear interpolation we'd expect 45 degrees rotation here at frame 25. Not shown, the first bone has 20.9 degrees rotation. We'd expect 22.5 degrees rotation.

Importantly, 35.5 degrees is not twice 20.9 degrees, so the whole structure bends off to the side.

Quaternions are 4D vectors that represent 3D rotations. The "right" way to interpolate between two quaternions is with a function called slerp. If we interpolated using slerp, we'd see that linear interpolation of the quaternion would give us linear rotation like we expect.

Blender does not use slerp. Blender instead interpolates between quaternion components. XYZW components are all treated independently, and then the final vector is normalized behind the scenes.

Blender's way isn't a great way to do things. It's just the way that most easily fit into the way Blender was designed (for Eulers) at the time that quaternions were implemented. It is slightly faster, which is why you will sometimes see component interpolation used more intentionally, when you expect frequent keyframes at 30hz rather than your 0.2hz, and when performance is very tight-- like in video games.

It's hard to understand intuitively what component interpolation means, because the whole 4D thing, but we can consider a 2D analogy. What if instead of using trig functions to locate the tail of a bone rotating through an arc, you just linearly interpolated between your starting and ending points that lay on that circle, and then renormalized your vector? At halfway points, it would be correct-- just like your structure is correct at frame 50. But at other points, it would not be correct. And how incorrect it would be would depend on the size of the rotation.

The selected bone, tracking a bone 3/4 of the way from 0 to 90 degrees rotation, has a rotation of 71.5 degrees (not the 67.5 degees we would expect 90 * 0.75.) Another bone, tracking 3/4 of the way to 135 degrees, has a rotation of 117.9 degrees.

And what if we were to track 3/4 of 180 degrees? Interpolated components would tell us that 3/4 of 180 degrees is still 180 degrees-- that anything less than half is 0 degrees, anything more than half is 180 degrees, and half exactly is undefined (the direction of the zero vector.)

We can see by doing 2D rotation via interpolated components not only that linear interpolation of components is not linear rotation, but that in addition, how far it differs from linear rotation depends on the angle described; and that relationship isn't linear either. Blender's quaternions are the same way.

That's what's going on with your structure. At frames 1, 50.5, and 100, normalized component interpolation gives the right answer, but at frames 25 and 75, it gives the wrong answer. The wrong answer doesn't preserve the ratio of rotation needed to keep your structure straight, because how wrong it is depends on the amount of rotation described, and not all of your bones are describing the same rotation.

• Thank you. Your first screenshot showed everything very well.
– user185939
Commented Apr 29 at 15:52
• Wow. Great answer. Is there really no way of forcing quaternion slerp() ? That seems to nearly defeat the purpose of using quaternions in the first place. I'm amazed. Commented Apr 29 at 16:27
• @RobinBetts The structure of Blender makes it really hard. You could implement slerp in a script or in geometry nodes. To do slerp, you need to know starting quaternion, ending quaternion, and slerp factor, which is your only interpolation value. So to implement it, you need to know the next keyframe and the last keyframe and also keep track of an interpolation variable. And none of the existing useful tools will work with that. The difficulty is interface, not math. Slerp vs components doesn't end up being that huge of an issue in practice, but it is confusing. Commented Apr 29 at 16:30
• Thanks, @Nathan! That gives me a starting-point to have a closer look and work it out for myself.. I need to go back and revise slerp(). Commented Apr 29 at 16:35
• This. This is the perfect scratch on my back I was dying for my question, god I wish I could visit you and kneel personally. I'll still need to re-read multiple times to fully digest this beauty - but to validate my understanding so far: Blender interpolate Quarternion's 4 basis (w, x, y, z) by simply doing Basis-Wise interplation such as (w1 * (1 - time) + w2 * time, ..., ..., ...), not converting it into Euler before interpolating - causing similar issues as if we interpolated Transform matrix - am I on right track? Commented Apr 30 at 0:49

Why don't you put the inversion on Y through one bone?