3

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?

4

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.

|improve this answer|||||
4

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

enter image description here

|improve this answer|||||
  • 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... – GregH Aug 23 '16 at 13:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.