[Tex/LaTex] 3D Vector Fields in Asymptote

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?

Best Answer

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

path3 gradient1(triple z){
  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);
   path3 gradient (pair r)
   {
   triple tmp=(r.x,r.y,z);
   return vector(tmp);
   }
   triple F(pair r) { return(r.x,r.y,z);}
   add(vectorfield(gradient,F,(a.x,a.y),(b.x,b.y),nx,ny,truesize,p,arrow,margin,name,render));
   }

   }
   VectorAPlot3D(gradient1,A,B,5,5,5);
*/
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);
              add(pic,opic,z);
            } else
              draw(pic,shift(z)*g,p,arrow,margin,name,render);
          }
        }
    }}
  if(group)
    endgroup3(pic);
  return pic;

}
add(VectorPlot3D(gradient1,A,B,5,5,5));
xaxis3(XY()*"$x$",OutTicks(XY()*Label));
yaxis3(XY()*"$y$",InTicks(YX()*Label));
zaxis3("$z$",OutTicks);

and the result enter image description here

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.