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