How can I rig a cylinder so that when it's rotated its end cap will not penetrate the floor?

I have a default Blender cylinder positioned upright atop a ground plane:

How can I rig the cylinder so that can be animated to wiggle/jitter (perhaps procedurally with noise f-curve modifiers on two of its rotation axis) without penetrating the floor?

A simple parent-child relationship as in the image above is sufficient to rotate (and translate) the cylinder on 1 axis in either a positive OR negative direction.

I'm looking for a setup that would allow me easily to rotate the cylinder on two or even three axis in both a positive and negative direction.

I imagine this would be possible with some drivers applied to the XYZ coordinates using trigonometric equations but the math escapes me. It might also be possible using a particular bone setup with the correct constraints but my attempts to find something that works so far have not succeeded.

There is no need for complicated trigonometry or drivers. You can do this with constraints.
Constraints are faster than drivers and have no security problems (manual).

The Cylinder Rig

Create the following bones. (Side view. Note, that the orientations of the bones are all the same.)

CTRL_cylinder has no parent or child. The other bones are in the following hierarchy:
floorRotation > orientation > DEF_cylinder > Cylinder (object)

• Add a Copy Rotation constraint from CTRL_cylinder to DEF_cylinder.
• Add a Copy Rotation constraint from CTRL_cylinder to orientation.

• Add a Locked Track constraint to orientation. The Target is CTRL_cylinder, with Head/Tail at 1. Set the To field to -Z and Lock Y.

• Add a Copy Location constraint from CTRL_cylinder to floorRotation.
• Add a Locked Track constraint from CTRL_cylinder to floorRotation.
Yet again, Head/Tail is at 1. Set the To field to Y and Lock Z.

I suggest the rotation order YZX for CTRL_cylinder. Add a crazy control shape for CTRL_cylinder and set it to be displayed At DEF_cylinder. The animator can move, rotate and roll the cylinder. Optionally, the Y axis can be locked, if no twisting is desired.

BONUS CONTENT

Two Axis (Rocker) Setup

This is useful for objects with two or more pivots, like feet, chairs, ... or a cube.

1. Create the following setup. The CTRL_cube is not in the hierarchy. The other bones are parented as such:
y_copy > frontPivot > backPivot > DEF_cube > Cube (object)
2. The rotation order of CTRL_cube is XYZ with Z locked.
• Add a Copy Location constraint from CTRL_cube to y_copy.
• Add a Copy Rotation constraint from CTRL_cube to y_copy with only Y checked and Local Space for Target and Owner.
• Add a Copy Rotation constraint from the CTRL_cube to frontPivot.
• Add a Copy Rotation constraint from the CTRL_cube to backPivot.
3. Finally, limit the rotation of the frontPivot, to prevent the cube from going under the floor.
• Add a Limit Rotation constraint to the frontPivot in Local Space, For Transform and with the X Limits 0 and 90.

The result can be moved rotated and rocked from front to back. With additional pivots and their respective Limit Rotation constraints any number of edges can be available for rocking.

Added a stylisch Bone Shape for CTRL_cube At DEF_Cube.

• This is fantastic, exactly what I need! Thank you! I cannot award the bounty for another 13 hours but will do so then. Oct 5, 2018 at 10:42
• @BlenderBro Leave the bounty open, someone could come up with an even more fitting answer. Oct 5, 2018 at 11:25

It doesn't seem you would need complex trigonometric equations here. You only wish to keep the object above ground(or on the ground), let's do just that.

Using a Driver

You can set up a driver for z location with a scripted function that finds the lowest vertex and then adjusts the location of the object accordingly:

import bpy

def aboveGround(object_name,floor_z):
obj = bpy.data.objects[object_name]
mesh = obj.data
z = 7 # because it's a lucky number...
# ...and 0 seems to be buggy inside the driver for some reason
for verts in mesh.vertices:
world_space_co = obj.matrix_world * verts.co
if world_space_co[2] < z :
z = world_space_co[2]

loc_z = obj.location[2]

return loc_z - z + floor_z
#    # or this if we don't want the object to stick to the ground when it's above :
#    if z<floor_z:
#        return loc_z - z + floor_z
#    else:
#        return loc_z

bpy.app.driver_namespace['aboveGround'] = aboveGround


You would need to have this in a text block and check Register (Autorun Python Script will need to be enabled in the user preferences as well) so it runs when you open the file:

You can just run it manually as well.

Then you can create a driver for z location of your object and put the function as the expression:

Using a Scene Update App Handler

It would also be possible to have only a function that would correct the position while working so you could set keyframes. Sort of like a tool. You could use a scene update app handler for that. You could just register it when you need it and then remove it once you don't need it any more:

import bpy

def AboveGroundHandler(scene):
obj = bpy.data.objects['Plane']
mesh = obj.data
floor_z = 0
z = 7
for verts in mesh.vertices:
world_space_co = obj.matrix_world * verts.co
if world_space_co[2] < z :
z = world_space_co[2]

obj.location[2] = obj.location[2] - z + floor_z

bpy.app.handlers.scene_update_pre.append(AboveGroundHandler)
#bpy.app.handlers.scene_update_pre.clear() # to remove it(can be run from Python console)

• Thanks for great script! I want to say, if driver expression produces error after opening a file, just add ".py" after script name in text editor (w/o quotes) Oct 5, 2018 at 9:44
• (I wrote a trig. version of your driver, but for cylinders only, so it's not as general as yours, and not worth posting.).. What triggers a scene_update_pre handler? Would it work during interpolation between key frames? Oct 5, 2018 at 9:49
• I believe drivers are intended for use in animation, I think it would be better to use them. I just thought, that if there is a cylinder wiggling it might bounce up and down so if one wanted to animate it manually, that could be simplified using this handler method as a simple tool for just placing the object in correct positions while the keyframes are set - that was the thought process. I think it might lead to unexpected results if used in animation. Oct 5, 2018 at 9:58
• A really interesting, general purpose solution, thank you! I gotta go with Leander's constraint setup though due to its comparative ease of use, and for the reasons stated at the top of their answer. Oct 5, 2018 at 10:48
• Leander's solution is really great. That would probably be my choice as well. I don't do much animation, so I did not think of it. Oct 5, 2018 at 11:07