5

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.

enter image description here

2
  • 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

1 Answer 1

6

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);

enter image description here


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);

enter image description here

This is what I was looking for. Here is what I wanted to do, a stereographic duoprism:

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .