// grid3.asy
// Author: Philippe Ivaldi (Grids in 3D)
// http://www.piprime.fr/
// Created: 10 janvier 2007

import graph3;

struct grid3 {
 path3 axea,axeb;
 bounds bds;
 triple dir;
 valuetime vt;
 ticklocate locate;
 void create(picture pic, path3 axea, path3 axeb, path3 axelevel,
             real min, real max, position pos, autoscaleT t) {
   real position=pos.position.x;
   triple level;
   if(pos.relative) {
     position=reltime(axelevel,position);
     level=point(axelevel,position)-point(axelevel,0);
   } else {
     triple v=unit(point(axelevel,1)-point(axelevel,0));
     triple zerolevel=dot(-point(axelevel,0),v)*v;
     level=zerolevel+position*v;
   }
   this.axea=shift(level)*axea;
   this.axeb=shift(level)*axeb;
   bds=autoscale(min,max,t.scale);
   locate=ticklocate(min,max,t,bds.min,bds.max,
                     Dir(point(axeb,0)-point(axea,0)));
 }
};

typedef grid3 grid3routine(picture pic);

triple X(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMin().z);}
triple XY(picture pic) {return (pic.userMax().x,pic.userMax().y,pic.userMin().z);}
triple Y(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMin().z);}
triple YZ(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMax().z);}
triple Z(picture pic) {return (pic.userMin().x,pic.userMin().y,pic.userMax().z);}
triple ZX(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMax().z);}

grid3routine XYgrid(position pos=Relative(0)) {
 return new grid3(picture pic) {
   grid3 og;
   triple m=pic.userMin();
   triple M=pic.userMax();
   og.create(pic,m--X(pic),Y(pic)--XY(pic),m--Z(pic),
             m.x,M.x,pos,pic.scale.x);
   return og;
 };
};
grid3routine XYgrid=XYgrid();

grid3routine YXgrid(position pos=Relative(0)) {
 return new grid3(picture pic) {
   grid3 og;
   triple m=pic.userMin();
   triple M=pic.userMax();
   og.create(pic,m--Y(pic),X(pic)--XY(pic),m--Z(pic),
             m.y,M.y,pos,pic.scale.y);
   return og;
 };
};
grid3routine YXgrid=YXgrid();


grid3routine XZgrid(position pos=Relative(0)) {
 return new grid3(picture pic) {
   grid3 og;
   triple m=pic.userMin();
   triple M=pic.userMax();
   og.create(pic,m--X(pic),Z(pic)--ZX(pic),m--Y(pic),
             m.x,M.x,pos,pic.scale.x);
   return og;
 };
};
grid3routine XZgrid=XZgrid();

grid3routine ZXgrid(position pos=Relative(0)) {
 return new grid3(picture pic) {
   grid3 og;
   triple m=pic.userMin();
   triple M=pic.userMax();
   og.create(pic,m--Z(pic),X(pic)--ZX(pic),m--Y(pic),
             m.z,M.z,pos,pic.scale.z);
   return og;
 };
};
grid3routine ZXgrid=ZXgrid();

grid3routine YZgrid(position pos=Relative(0)) {
 return new grid3(picture pic) {
   grid3 og;
   triple m=pic.userMin();
   triple M=pic.userMax();
   og.create(pic,m--Y(pic),Z(pic)--YZ(pic),m--X(pic),
             m.y,M.y,pos,pic.scale.y);
   return og;
 };
};
grid3routine YZgrid=YZgrid();

grid3routine ZYgrid(position pos=Relative(0)) {
 return new grid3(picture pic) {
   grid3 og;
   triple m=pic.userMin();
   triple M=pic.userMax();
   og.create(pic,m--Z(pic),Y(pic)--YZ(pic),m--X(pic),
             m.z,M.z,pos,pic.scale.z);
   return og;
 };
};
grid3routine ZYgrid=ZYgrid();

typedef grid3routine grid3routines[] ;

grid3routines XYXgrid(position pos=Relative(0)) {
 grid3routines ogs=new grid3routine[] {XYgrid(pos),YXgrid(pos)};
 return ogs;
};
grid3routines XYXgrid=XYXgrid();
grid3routines YXYgrid(position pos=Relative(0)) {return XYXgrid(pos);};
grid3routines YXYgrid=XYXgrid();

grid3routines ZXZgrid(position pos=Relative(0)) {
 grid3routines ogs=new grid3routine[] {ZXgrid(pos),XZgrid(pos)};
 return ogs;
};
grid3routines ZXZgrid=ZXZgrid();
grid3routines XZXgrid(position pos=Relative(0)) {return ZXZgrid(pos);};
grid3routines XZXgrid=XZXgrid();

grid3routines ZYZgrid(position pos=Relative(0)) {
 grid3routines ogs=new grid3routine[] {ZYgrid(pos),YZgrid(pos)};
 return ogs;
};
grid3routines ZYZgrid=ZYZgrid();
grid3routines YZYgrid(position pos=Relative(0)) {return ZYZgrid(pos);};
grid3routines YZYgrid=YZYgrid();

grid3routines XY_XZgrid(position posa=Relative(0), position posb=Relative(0)) {
 grid3routines ogs=new grid3routine[] {XYgrid(posa),XZgrid(posb)};
 return ogs;
};
grid3routines XY_XZgrid=XY_XZgrid();

grid3routines YX_YZgrid(position posa=Relative(0), position posb=Relative(0)) {
 grid3routines ogs=new grid3routine[] {YXgrid(posa),YZgrid(posb)};
 return ogs;
};
grid3routines YX_YZgrid=YX_YZgrid();

grid3routines ZX_ZYgrid(position posa=Relative(0), position posb=Relative(0)) {
 grid3routines ogs=new grid3routine[] {ZXgrid(posa),ZYgrid(posb)};
 return ogs;
};
grid3routines ZX_ZYgrid=ZX_ZYgrid();

typedef grid3routines[] grid3routinetype;

grid3routinetype XYZgrid(position pos=Relative(0))
{
 grid3routinetype ogs=new grid3routines[] {YZYgrid(pos),XYXgrid(pos),
                                           XZXgrid(pos)};
 return ogs;
}
grid3routinetype XYZgrid=XYZgrid();

grid3routines operator cast(grid3routine gridroutine) {
 grid3routines og=new grid3routine[] {gridroutine};
 return og;
}

grid3routinetype operator cast(grid3routines gridroutine) {
 grid3routinetype og=new grid3routines[] {gridroutine};
 return og;
}

grid3routinetype operator cast(grid3routine gridroutine) {
 grid3routinetype og=(grid3routinetype)(grid3routines) gridroutine;
 return og;
}

void grid3(picture pic=currentpicture,
          grid3routinetype gridroutine=XYZgrid,
          int N=0, int n=0, real Step=0, real step=0,
          bool begin=true, bool end=true,
          pen pGrid=grey, pen pgrid=lightgrey,
          bool above=false)
{
 for(int j=0; j < gridroutine.length; ++j) {
   grid3routines gridroutinej=gridroutine[j];
   for(int i=0; i < gridroutinej.length; ++i) {
     grid3 gt=gridroutinej[i](pic);
     pic.add(new void(picture f, transform3 t, transform3 T,
                      projection P, triple, triple) {
         picture d;
         ticks3 ticks=Ticks3(1,F="%",ticklabel=null,
                             beginlabel=false,endlabel=false,
                             N=N,n=n,Step=Step,step=step,
                             begin=begin,end=end,
                             Size=0,size=0,extend=true,
                             pTick=pGrid,ptick=pgrid);
         ticks(d,t,"",gt.axea,gt.axeb,nullpen,None,NoMargin3,gt.locate,
               gt.bds.divisor,opposite=true,primary=false,P);
         add(f,t*T*inverse(t)*d);
       },above=above);
     addPath(pic,gt.axea,pGrid);
     addPath(pic,gt.axeb,pGrid);
   }
 }
}

void grid3(picture pic=currentpicture,
          grid3routinetype gridroutine,
          int N=0, int n=0, real Step=0, real step=0,
          bool begin=true, bool end=true,
          pen[] pGrid, pen[] pgrid,
          bool above=false)
{
 if(pGrid.length != gridroutine.length || pgrid.length != gridroutine.length)
   abort("pen array has different length than grid");
 for(int i=0; i < gridroutine.length; ++i) {
   grid3(pic=pic,gridroutine=gridroutine[i],
         N=N,n=n,Step=Step,step=step,
         begin=begin,end=end,
         pGrid=pGrid[i],pgrid=pgrid[i],
         above=above);
 }
}

position top=Relative(1);
position bottom=Relative(0);
position middle=Relative(0.5);

// Structure used to communicate ticks and axis settings to grid3 routines.
struct ticksgridT {
 ticks3 ticks;
 // Other arguments of grid3 are define by ticks and axis settings
 void grid3(picture, bool);
};

typedef ticksgridT ticksgrid();


ticksgrid InOutTicks(Label F="", ticklabel ticklabel=null,
                    bool beginlabel=true, bool endlabel=true,
                    int N=0, int n=0, real Step=0, real step=0,
                    bool begin=true, bool end=true,
                    real Size=0, real size=0,
                    pen pTick=nullpen, pen ptick=nullpen,
                    grid3routinetype gridroutine,
                    pen pGrid=grey, pen pgrid=lightgrey)
{
 return new ticksgridT()
   {
     ticksgridT otg;
     otg.ticks=Ticks3(0,F,ticklabel,beginlabel,endlabel,
                      N,n,Step,step,begin,end,
                      Size,size,false,pTick,ptick);
     otg.grid3=new void(picture pic, bool above) {
                                                  grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above);
     };
     return otg;
   };
}

ticksgrid InTicks(Label F="", ticklabel ticklabel=null,
                 bool beginlabel=true, bool endlabel=true,
                 int N=0, int n=0, real Step=0, real step=0,
                 bool begin=true, bool end=true,
                 real Size=0, real size=0,
                 pen pTick=nullpen, pen ptick=nullpen,
                 grid3routinetype gridroutine,
                 pen pGrid=grey, pen pgrid=lightgrey)
{
 return new ticksgridT()
   {
     ticksgridT otg;
     otg.ticks=Ticks3(-1,F,ticklabel,beginlabel,endlabel,N,n,Step,step,
                      begin,end,Size,size,false,pTick,ptick);
     otg.grid3=new void(picture pic, bool above) {
                                                  grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above);
     };
     return otg;
   };
}

ticksgrid OutTicks(Label F="", ticklabel ticklabel=null,
                  bool beginlabel=true, bool endlabel=true,
                  int N=0, int n=0, real Step=0, real step=0,
                  bool begin=true, bool end=true,
                  real Size=0, real size=0,
                  pen pTick=nullpen, pen ptick=nullpen,
                  grid3routinetype gridroutine,
                  pen pGrid=grey, pen pgrid=lightgrey)
{
 return new ticksgridT()
   {
     ticksgridT otg;
     otg.ticks=Ticks3(1,F,ticklabel,beginlabel,endlabel,N,n,Step,step,
                      begin,end,Size,size,false,pTick,ptick);
     otg.grid3=new void(picture pic, bool above) {
                                                  grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above);
     };
     return otg;
   };
}

void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero,
           pen p=currentpen, ticksgrid ticks,
           arrowbar3 arrow=None, bool above=false)
{
 xaxis3(pic,L,axis,p,ticks().ticks,arrow,above);
 ticks().grid3(pic,above);
}

void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero,
           pen p=currentpen, ticksgrid ticks,
           arrowbar3 arrow=None, bool above=false)
{
 yaxis3(pic,L,axis,p,ticks().ticks,arrow,above);
 ticks().grid3(pic,above);
}

void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero,
           pen p=currentpen, ticksgrid ticks,
           arrowbar3 arrow=None, bool above=false)
{
 zaxis3(pic,L,axis,p,ticks().ticks,arrow,above);
 ticks().grid3(pic,above);
}

/* Example:

  import grid3;

  size(8cm,0);
  currentprojection=orthographic(0.5,1,0.5);

  defaultpen(overwrite(SuppressQuiet));

  scale(Linear, Linear, Log);

  grid3(pic=currentpicture, // picture
  gridroutine=XYZgrid(// grid3routine
  // or grid3routine[] (alias grid3routines)
  // or grid3routines[]:
  // The routine(s) to draw the grid(s):
  // *XYgrid: draw grid from X in direction of Y
  // *YXgrid: draw grid from Y in direction of X, ...
  // *An array of previous values XYgrid, YXgrid, ...
  // *XYXgrid: draw XYgrid and YXgrid grids
  // *YXYgrid: draw XYgrid and YXgrid grids
  // *ZXZgrid: draw ZXgrid and XZgrid grids
  // *YX_YZgrid: draw YXgrid and YZgrid grids
  // *XY_XZgrid: draw XYgrid and XZgrid grids
  // *YX_YZgrid: draw YXgrid and YZgrid grids
  // *An array of previous values XYXgrid,...
  // *XYZgrid: draw XYXgrid, ZYZgrid, XZXgrid grids.
  pos=Relative(0)),
  // the position of the grid relative to the axis
  // perpendicular to the grid; a real number
  // specifies a coordinate relative  to this axis.
  // Aliases: top=Relative(1), middle=Relative(0.5)
  // and bottom=Relative(0).

  // These arguments are similar to those of InOutTicks():
  N=0,                // int
  n=0,                // int
  Step=0,             // real
  step=0,             // real
  begin=true,         // bool
  end=true,           // bool
  pGrid=grey,         // pen
  pgrid=lightgrey,    // pen
  above=false         // bool
  );

  xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
  yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks());
  zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
*/

/* Other examples:

  int N=10, n=2;
  xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
  yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks(N=N,n=n));
  zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
  grid3(N=N,n=n);

  xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
  yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks());
  zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
  grid3(new grid3routines[] {XYXgrid(top),XZXgrid(0)});

  xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
  yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks());
  zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
  grid3(new grid3routines[] {XYXgrid(-0.5),XYXgrid(1.5)},
  pGrid=new pen[] {red,blue},
  pgrid=new pen[] {0.5red,0.5blue});

  // Axes with grids:

  xaxis3(Label("$x$",position=EndPoint,align=S),
  OutTicks(Step=0.5,gridroutine=XYgrid));
  yaxis3(Label("$y$",position=EndPoint,align=S),
  InOutTicks(Label("",align=0.5X),N=8,n=2,gridroutine=YX_YZgrid));
  zaxis3("$z$",OutTicks(ZYgrid));
*/