# 3D Vector Fields in Asymptote

I'd like to use Asymptote to draw a 3D vector field, such as `F(x,y,z) = <y+z,x+z,x+y>`, wherein a rectangular solid of vectors is returned (say, 5 x 5 x 5). The result will certainly be messy, but it is what I want. I can only see in the Asymptote manual how to plot a vector field along a surface, and I can't figure how to adapt the example files to fit my needs. In Mathematica, there is a simple `VectorPlot3D` command that does precisely this.

How can it be done?

Hy

To my knowledge you have to make by yourself such a function (there was also a question to draw a vector field on a line). So I adapted the `vectorfield` defined in `graph3.asy` (I remove the `bool cond(z)` possibility). As James it is some loops. A scale is also computed. Please consider the code

``````import graph3;

size(12cm,0);
currentprojection=perspective((45,135,30));

return O--(z.y+z.z,z.x+z.z,z.x+z.y);
}

/* First solution : a loop on z

void VectorAPlot3D(path3 vector(triple v), triple a, triple b,
int nx=nmesh, int ny=nx, int nz=nx,bool truesize=false,
//real maxlength=truesize ? 0 : maxlength(f,a,b,nu,nv),
//  bool cond(pair z)=null,
pen p=currentpen,
arrowbar3 arrow=Arrow3, margin3 margin=PenMargin3,
string name="", render render=defaultrender)
{
real dz=1/nz;
for(int k=0; k <= nz; ++k)
{
real z=interp(a.z,b.z,k*dz);
{
triple tmp=(r.x,r.y,z);
return vector(tmp);
}
triple F(pair r) { return(r.x,r.y,z);}
}

}
*/
triple A=(0,0,0);
triple B=(5,5,5);

picture VectorPlot3D(path3 vector(triple t), triple a, triple b,
int nx=nmesh, int ny=nx, int nz=nx,bool truesize=false,
real maxlength=truesize ? 0 : min(abs(b.x-a.x)/nx,abs(b.y-a.y)/ny,abs(b.z-a.z)/nz),
//  bool cond(pair z)=null,
pen p=currentpen,
arrowbar3 arrow=Arrow3, margin3 margin=PenMargin3,
string name="", render render=defaultrender)
{
picture pic;
real dx=1/nx;
real dy=1/ny;
real dz=1/nz;
real scale;
if(maxlength > 0) {
real size(triple t) {
path3 g=vector(t);
return abs(point(g,size(g)-1)-point(g,0));
}
real max=size((0,0,0));

for(int i=0; i <= nx; ++i) {
real x=interp(a.x,b.x,i*dx);
for(int j=0; j <= ny; ++j)
{
real y=interp(a.y,b.y,j*dy);
for(int k=0; k <= nz; ++k)
max=max(max,size((x,y,interp(a.z,b.z,k*dz))));
}}
scale=max > 0 ? maxlength/max : 1;
} else scale=1;
bool group=name != "" || render.defaultnames;
if(group)
begingroup3(pic,name == "" ? "vectorfield" : name,render);
for(int i=0; i <= nx; ++i) {
real x=interp(a.x,b.x,i*dx);
for(int j=0; j <= ny; ++j) {
real y=interp(a.y,b.y,j*dy);
for(int k=0; k <= nz; ++k)
{      triple z=(x,y,interp(a.z,b.z,k*dz));
{
path3 g=scale3(scale)*vector(z);
string name="vector";
if(truesize) {
picture opic;
draw(opic,g,p,arrow,margin,name,render);
} else
draw(pic,shift(z)*g,p,arrow,margin,name,render);
}
}
}}
if(group)
endgroup3(pic);
return pic;

}
xaxis3(XY()*"\$x\$",OutTicks(XY()*Label));
yaxis3(XY()*"\$y\$",InTicks(YX()*Label));
zaxis3("\$z\$",OutTicks);
``````

and the result

Notice that in the code, there is also a previous version, a loop in `z` and `vectorfield` on the rectangle at height `z`, in this case the scale could be different for different `z`.

I didn't look for a canned function, but you could always do it manually with some `for` loops as follows.

``````import three;
size(1inch);
currentprojection=perspective((45,135,30));

for (int x = 1; x <= 5; ++x) {
for (int y = 1; y <= 5; ++y) {
for (int z = 1; z <= 5; ++z) {
triple start = (x,y,z);
triple end = start + scale(0.1,0.1,0.1)*(y+z,x+z,x+y);
draw(start--end, Arrow3(2));
}
}
}
``````

• What is nice about the 2D canned function is that the arrows get auto-sized: instead of drawing really long vectors, the vector get fatter. This avoids having vectors cross over each other. The 3D canned functions do something similar (pg 153 of the manual points to some example .asy files), except they are only drawn on surfaces. I figure I can do what you did, and nest loops, and also come up with my own auto-sizing routine, but this seems unnecessarily complicated. There's got to be a built-in way of doing it... Aug 23, 2016 at 13:50
• @GregH I modified the above code a bit. I guess `scale3(.5)*unit(f(x,y,z))` is what you meant. `import three; size(2inch); triple f(real x,real y,real z){return (y+z,x+z,x+y);} real h=.8,a=3; for (real x = -a; x <= a; x=x+h) { for (real y = -a; y <= a; y=y+h) { for (real z = -a; z <= a; z=z+h) { triple start = (x,y,z); triple end = start + scale3(.5)*unit(f(x,y,z)); draw(start--end, Arrow3(5)); }}}` Jan 4, 2023 at 16:33