# Tube with variable radius using Asymptote

In Asymptote, given a (curved) path between two points \$A\$ and \$B\$, I would like to draw a tube joining these two points, with a variable radius, as in the picture below. That is, if \$M\$ is a point of the path, I want a radius at \$M\$ that is a function of \$M\$ (for example the distance between \$M\$ and the origin).

I know the tube package, but it allows only a constant section as far as I know. • Please add an example of what you have tried. I think you can always use `revolution` to produce this plot, e.g. `import graph3; import solids; size(0,150); currentprojection=perspective(1.5,0,10,Y); pen color=yellow+opacity(0.75); real f(real x){return sqrt(x);} pair F(real x){return (x,f(x));} triple F3(real x){return (x,f(x),0);} path p=graph(F,1,3,n=20,operator ..); path3 p3=path3(p); revolution a=revolution(p3,X,0,360); draw(surface(a),color,render(compression=Low,merge=true));`
– user194703
Dec 12, 2019 at 21:23
• Thanks @Schrödinger'scat. But this does not give what I want (I will edit my answer later). I think the way to go is here (see the second figure, this is not a revolution surface). I wrote "I know the tube package, but it allows only a constant section as far as I know." but this is not true, there is the `transform` argument to transform the section. Dec 13, 2019 at 15:55

I think I get it (EDIT: nope, see the edit below), using revolution, as suggested by @Schrodinger's cat (thanks!).

The idea consists in constructing the surface of revolution from `(0,0,0)` to `(1,0,0)`, then scaling this surface in the `X` direction by `length(B-A)`, then mapping this surface with a rotation that sends the unit vector `(1,0,0)` to the unit vector `(B-A)/length(B-A)`, and a translation by `A`.

``````settings.render = 4;
settings.outformat = "pdf";
import solids;
size(5cm,0);
currentprojection = orthographic(1,2,4);

path3 pathAB(triple A, triple B, int n){
path3 out;
for(int i = 0; i <= n; ++i){
real t = i/n;
triple M = A + t*(B-A);
real r = length(M);
out = out -- (t, r, 0);
}
return out;
}

triple A = (2,1,0);
triple B = (-10,2,1);

path3 p3 = pathAB(A, B, 100);
revolution a = scale(length(B-A),1,1)*revolution(p3,X,0,360);

struct quaternion {
real w;
real x;
real y;
real z;
}

// unit quaternion to rotation matrix
transform3 quat2rot(quaternion q){
transform3 T = identity4;
real a = q.w;
real b = q.x;
real c = q.y;
real d = q.z;
T = a*a+b*b-c*c-d*d; T = 2*b*c-2*a*d; T = 2*a*c+2*b*d;
T = 2*a*d+2*b*c; T = a*a-b*b+c*c-d*d; T = 2*c*d-2*a*b;
T = 2*b*d-2*a*c; T = 2*a*b+2*c*d; T = a*a-b*b-c*c+d*d;
return T;
}

// quaternion sending u to v
quaternion from2vectors(triple u, triple v){
real norm_u_norm_v = length(u) * length(v);
real cos_theta = dot(u, v) / norm_u_norm_v;
real half_cos = sqrt(0.5 * (1 + cos_theta));
triple w = cross(u, v) / (norm_u_norm_v * 2 * half_cos);
quaternion q;
q.w = half_cos; q.x = w.x; q.y = w.y; q.z = w.z;
return q;
}

//
quaternion q = from2vectors((1,0,0), (B-A)/length(B-A));
transform3 T = quat2rot(q);
draw(shift(A)*T*surface(a), red+opacity(0.5), render(compression=Low,merge=true));
draw(shift(A)*scale3(0.1)*unitsphere, black);
draw(shift(B)*scale3(0.1)*unitsphere, black);
`````` # EDIT

The above is not what I want (I don't delete it because it has its own interest).

I was wrong when I wrote "I know the tube package, but it allows only a constant section as far as I know.". Indeed, `tube` has a transform parameter allowing to apply a transformation to the section of the tube at each point of the path. Here is an example:

``````settings.render = 4;
settings.outformat = "pdf";
import tube;
import graph3;
size(5cm,0);
currentprojection = orthographic(0,0,4);

// define a path
triple f(real x){
return (x, x*x*x, 1);
}
int n = 200;
path3 p = graph(f, -1, 1, n=n, operator ..);

// transformation to apply to the section of the tube
// t varies from 0 to n/4
transform T(real t){
triple M = relpoint(p, t/(n/4));
return scale(length(M)/10);
}

// draw tube
draw(tube(p, unitcircle, T), purple);

draw(shift(relpoint(p,0))*scale3(0.1)*unitsphere, green);
draw(shift(relpoint(p,1))*scale3(0.1)*unitsphere, red);
`````` This is what I was looking for. Here is what I wanted to do, a stereographic duoprism: 