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[0][0] = a*a+b*b-c*c-d*d; T[0][1] = 2*b*c-2*a*d; T[0][2] = 2*a*c+2*b*d;
T[1][0] = 2*a*d+2*b*c; T[1][1] = a*a-b*b+c*c-d*d; T[1][2] = 2*c*d-2*a*b;
T[2][0] = 2*b*d-2*a*c; T[2][1] = 2*a*b+2*c*d; T[2][2] = 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:

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));
transform
argument to transform the section.