TubePlot[curve_List, {var_, min_, max_}, radius_,
crossVector_List:{1, 1, 1}, opts___] :=
Module[{tangent, unitTangent, normal,
unitNormal, biNormal},
tangent = D[curve, t];
unitTangent = tangent/Sqrt[tangent.tangent];
normal = Cross[tangent, crossVector];
unitNormal = normal/Sqrt[normal.normal];
biNormal = Cross[unitTangent, unitNormal];
ParametricPlot3D[
curve + radius Cos[s] unitNormal + radius Sin[s] biNormal //
Evaluate,
{var, min, max}, {s, 0, 2Pi}, opts]
]
Another approach is to take the derivative of the unit tangent vector, normailize it, and then take the cross product of these two. This is called the Frenet reference frame of the path and is used in general relativity. While the previous approach is usually faster and more reliable, the Frenet approach sometimes leads to prettier results. Here is some code implementing the Frenet approach.
TubePlotFrenet[curve_List, {var_, min_, max_}, radius_, opts___] :=
Module[{tangent, unitTangent, normal,
unitNormal, biNormal},
tangent = D[curve, t];
unitTangent = tangent/Sqrt[tangent.tangent];
normal = D[unitTangent, t];
unitNormal = normal/Sqrt[normal.normal];
biNormal = Cross[unitTangent, unitNormal];
ParametricPlot3D[
curve + radius Cos[s] unitNormal + radius Sin[s] biNormal // Evaluate,
{var, min, max}, {s, 0, 2Pi}, opts]
]