ticklocate ticklocate(real a, real b, autoscaleT S=defaultS,
real tickmin=-infinity, real tickmax=infinity,
real time(real)=null, direction3 dir)
{
if((valuetime) time == null) time=linear(S.T(),a,b);
ticklocate locate;
locate.a=a;
locate.b=b;
locate.S=S.copy();
if(finite(tickmin)) locate.S.tickMin=tickmin;
if(finite(tickmax)) locate.S.tickMax=tickmax;
locate.time=time;
locate.dir=zero;
locate.dir3=dir;
return locate;
}
private struct locateT {
real t; // tick location time
triple V; // tick location in frame coordinates
triple pathdir; // path direction in frame coordinates
triple dir; // tick direction in frame coordinates
void dir(transform3 T, path3 g, ticklocate locate, real t) {
pathdir=unit(shiftless(T)*dir(g,t));
triple Dir=locate.dir3(t);
dir=unit(Dir);
}
// Locate the desired position of a tick along a path.
void calc(transform3 T, path3 g, ticklocate locate, real val) {
t=locate.time(val);
V=T*point(g,t);
dir(T,g,locate,t);
}
}
for(int i=(begin ? 0 : 1); i < (end ? Ticks.length : Ticks.length-1); ++i) {
real val=T(Ticks[i]);
if(val >= a && val <= b)
drawtick(pic,t,g,g2,locate,val,Size,sign,pTick,extend);
}
for(int i=0; i < ticks.length; ++i) {
real val=T(ticks[i]);
if(val >= a && val <= b)
drawtick(pic,t,g,g2,locate,val,size,sign,ptick,extend);
}
if(N == 0) N=1;
if(Size > 0 && primary) {
for(int i=(beginlabel ? 0 : 1);
i < (endlabel ? Ticks.length : Ticks.length-1); i += N) {
real val=T(Ticks[i]);
if(val >= a && val <= b) {
ticklabels=true;
labeltick(pic,t,g,locate,val,Sign,Size,ticklabel,F,norm);
}
}
}
if(labelaxis)
labelaxis(pic,t,L,G,locate,Sign,ticklabels);
endgroup3(pic);
};
}
// Automatic tick construction routine.
ticks3 Ticks3(int sign, Label F="", ticklabel ticklabel=null,
bool beginlabel=true, bool endlabel=true,
int N, int n=0, real Step=0, real step=0,
bool begin=true, bool end=true, tickmodifier modify=None,
real Size=0, real size=0, bool extend=false,
pen pTick=nullpen, pen ptick=nullpen)
{
return new void(picture pic, transform3 T, Label L,
path3 g, path3 g2, pen p,
arrowbar3 arrow, margin3 margin=NoMargin3, ticklocate locate,
int[] divisor, bool opposite, bool primary,
projection P=currentprojection) {
path3 G=T*g;
real limit=Step == 0 ? axiscoverage*arclength(G) : 0;
tickvalues values=modify(generateticks(sign,F,ticklabel,N,n,Step,step,
Size,size,identity(),1,
project(G,P),
limit,p,locate,divisor,
opposite));
Ticks3(sign,F,ticklabel,beginlabel,endlabel,values.major,values.minor,
values.N,begin,end,Size,size,extend,pTick,ptick)
(pic,T,L,g,g2,p,arrow,margin,locate,divisor,opposite,primary,P);
};
}
// Process any queued y and z axes bound calculation requests.
for(int i=0; i < pic.scale.y.bound.length; ++i)
pic.scale.y.bound[i]();
for(int i=0; i < pic.scale.z.bound.length; ++i)
pic.scale.z.bound[i]();
// Process any queued x and z axis bound calculation requests.
for(int i=0; i < pic.scale.x.bound.length; ++i)
pic.scale.x.bound[i]();
for(int i=0; i < pic.scale.z.bound.length; ++i)
pic.scale.z.bound[i]();
// Process any queued x and y axes bound calculation requests.
for(int i=0; i < pic.scale.x.bound.length; ++i)
pic.scale.x.bound[i]();
for(int i=0; i < pic.scale.y.bound.length; ++i)
pic.scale.y.bound[i]();
// Request another z bounds calculation before final picture scaling.
pic.scale.z.bound.push(bounds);
}
// Internal routine to autoscale the user limits of a picture.
void autoscale3(picture pic=currentpicture, axis axis)
{
bool set=pic.scale.set;
autoscale(pic,axis);
// Set the z limits of a picture.
void zlimits(picture pic=currentpicture, real min=-infinity, real max=infinity,
bool crop=NoCrop)
{
if(min > max) return;
// Restrict the x, y, and z limits to box(min,max).
void limits(picture pic=currentpicture, triple min, triple max)
{
xlimits(pic,min.x,max.x);
ylimits(pic,min.y,max.y);
zlimits(pic,min.z,max.z);
}
real[] uniform(real T(real x), real Tinv(real x), real a, real b, int n)
{
return map(Tinv,uniform(T(a),T(b),n));
}
// Draw a tick of length size at triple v in direction dir using pen p.
void tick(picture pic=currentpicture, triple v, triple dir, real size=Ticksize,
pen p=currentpen)
{
triple v=Scale(pic,v);
pic.add(new void (picture f, transform3 t) {
triple tv=t*v;
draw(f,tv--tv+unit(dir)*size,p);
});
pic.addPoint(v,p);
pic.addPoint(v,unit(dir)*size,p);
}
graph graph(interpolate3 join)
{
return new guide3(triple f(real), real a, real b, int n) {
real width=b-a;
return n == 0 ? join(f(a)) :
join(...sequence(new guide3(int i) {return f(a+(i/n)*width);},n+1));
};
}
multigraph graph(interpolate3 join, bool3 cond(real))
{
return new guide3[](triple f(real), real a, real b, int n) {
real width=b-a;
if(n == 0) return new guide3[] {join(cond(a) ? f(a) : nullpath3)};
guide3[] G;
guide3[] g;
for(int i=0; i < n+1; ++i) {
real t=a+(i/n)*width;
bool3 b=cond(t);
if(b)
g.push(f(t));
else {
if(g.length > 0) {
G.push(join(...g));
g=new guide3[] {};
}
if(b == default)
g.push(f(t));
}
}
if(g.length > 0)
G.push(join(...g));
return G;
};
}
guide3 graph(picture pic=currentpicture, real x(real), real y(real),
real z(real), real a, real b, int n=ngraph,
interpolate3 join=operator --)
{
return graph(join)(new triple(real t) {return Scale(pic,(x(t),y(t),z(t)));},
a,b,n);
}
guide3[] graph(picture pic=currentpicture, real x(real), real y(real),
real z(real), real a, real b, int n=ngraph,
bool3 cond(real), interpolate3 join=operator --)
{
return graph(join,cond)(new triple(real t) {
return Scale(pic,(x(t),y(t),z(t)));
},a,b,n);
}
guide3 graph(picture pic=currentpicture, triple v(real), real a, real b,
int n=ngraph, interpolate3 join=operator --)
{
return graph(join)(new triple(real t) {return Scale(pic,v(t));},a,b,n);
}
guide3[] graph(picture pic=currentpicture, triple v(real), real a, real b,
int n=ngraph, bool3 cond(real), interpolate3 join=operator --)
{
return graph(join,cond)(new triple(real t) {
return Scale(pic,v(t));
},a,b,n);
}
// Connect points in v into segments corresponding to consecutive true elements
// of b using interpolation operator join.
path3[] segment(triple[] v, bool[] cond, interpolate3 join=operator --)
{
checkconditionlength(cond.length,v.length);
int[][] segment=segment(cond);
return sequence(new path3(int i) {return join(...v[segment[i]]);},
segment.length);
}
bool uperiodic(real[][] a) {
int n=a.length;
if(n == 0) return false;
int m=a[0].length;
real[] a0=a[0];
real[] a1=a[n-1];
for(int j=0; j < m; ++j) {
real norm=0;
for(int i=0; i < n; ++i)
norm=max(norm,abs(a[i][j]));
real epsilon=sqrtEpsilon*norm;
if(abs(a0[j]-a1[j]) > epsilon) return false;
}
return true;
}
bool vperiodic(real[][] a) {
int n=a.length;
if(n == 0) return false;
int m=a[0].length-1;
for(int i=0; i < n; ++i) {
real[] ai=a[i];
real epsilon=sqrtEpsilon*norm(ai);
if(abs(ai[0]-ai[m]) > epsilon) return false;
}
return true;
}
bool uperiodic(triple[][] a) {
int n=a.length;
if(n == 0) return false;
int m=a[0].length;
triple[] a0=a[0];
triple[] a1=a[n-1];
real epsilon=sqrtEpsilon*norm(a);
for(int j=0; j < m; ++j)
if(abs(a0[j]-a1[j]) > epsilon) return false;
return true;
}
bool vperiodic(triple[][] a) {
int n=a.length;
if(n == 0) return false;
int m=a[0].length-1;
real epsilon=sqrtEpsilon*norm(a);
for(int i=0; i < n; ++i)
if(abs(a[i][0]-a[i][m]) > epsilon) return false;
return true;
}
// return the surface described by a matrix f
surface surface(picture pic=currentpicture, triple[][] f, bool[][] cond={})
{
if(!rectangular(f)) abort("matrix is not rectangular");
int nx=f.length-1;
int ny=nx > 0 ? f[0].length-1 : 0;
surface bispline(real[][] z, real[][] p, real[][] q, real[][] r,
real[] x, real[] y, bool[][] cond={})
{ // z[i][j] is the value at (x[i],y[j])
// p and q are the first derivatives with respect to x and y, respectively
// r is the second derivative ddu/dxdy
int n=x.length-1;
int m=y.length-1;
bool all=cond.length == 0;
int count;
if(all)
count=n*m;
else {
count=0;
for(int i=0; i < n; ++i) {
bool[] condi=cond[i];
for(int j=0; j < m; ++j)
if(condi[j]) ++count;
}
}
surface s=surface(count);
s.index=new int[n][m];
int k=0;
for(int i=0; i < n; ++i) {
int ip=i+1;
real xi=x[i];
real xp=x[ip];
real x1=interp(xi,xp,1/3);
real x2=interp(xi,xp,2/3);
real hx=x1-xi;
real[] zi=z[i];
real[] zp=z[ip];
real[] ri=r[i];
real[] rp=r[ip];
real[] pi=p[i];
real[] pp=p[ip];
real[] qi=q[i];
real[] qp=q[ip];
int[] indexi=s.index[i];
bool[] condi=all ? null : cond[i];
for(int j=0; j < m; ++j) {
if(all || condi[j]) {
real yj=y[j];
int jp=j+1;
real yp=y[jp];
real y1=interp(yj,yp,1/3);
real y2=interp(yj,yp,2/3);
real hy=y1-yj;
real hxy=hx*hy;
real zij=zi[j];
real zip=zi[jp];
real zpj=zp[j];
real zpp=zp[jp];
real pij=hx*pi[j];
real ppj=hx*pp[j];
real qip=hy*qi[jp];
real qpp=hy*qp[jp];
real zippip=zip+hx*pi[jp];
real zppmppp=zpp-hx*pp[jp];
real zijqij=zij+hy*qi[j];
real zpjqpj=zpj+hy*qp[j];
private real[][][] bispline0(real[][] z, real[][] p, real[][] q, real[][] r,
real[] x, real[] y, bool[][] cond={})
{ // z[i][j] is the value at (x[i],y[j])
// p and q are the first derivatives with respect to x and y, respectively
// r is the second derivative ddu/dxdy
int n=x.length-1;
int m=y.length-1;
real[][][] s=new real[count][][];
int k=0;
for(int i=0; i < n; ++i) {
int ip=i+1;
real xi=x[i];
real xp=x[ip];
real hx=(xp-xi)/3;
real[] zi=z[i];
real[] zp=z[ip];
real[] ri=r[i];
real[] rp=r[ip];
real[] pi=p[i];
real[] pp=p[ip];
real[] qi=q[i];
real[] qp=q[ip];
bool[] condi=all ? null : cond[i];
bool[] condp=all ? null : cond[i+1];
for(int j=0; j < m; ++j) {
if(all || (condi[j] && condi[j+1] && condp[j] && condp[j+1])) {
real yj=y[j];
int jp=j+1;
real yp=y[jp];
real hy=(yp-yj)/3;
real hxy=hx*hy;
real zij=zi[j];
real zip=zi[jp];
real zpj=zp[j];
real zpp=zp[jp];
real pij=hx*pi[j];
real ppj=hx*pp[j];
real qip=hy*qi[jp];
real qpp=hy*qp[jp];
real zippip=zip+hx*pi[jp];
real zppmppp=zpp-hx*pp[jp];
real zijqij=zij+hy*qi[j];
real zpjqpj=zpj+hy*qp[j];
// return the surface values described by a real matrix f, interpolated with
// xsplinetype and ysplinetype.
real[][][] bispline(real[][] f, real[] x, real[] y,
splinetype xsplinetype=null,
splinetype ysplinetype=xsplinetype, bool[][] cond={})
{
real epsilon=sqrtEpsilon*norm(y);
if(xsplinetype == null)
xsplinetype=(abs(x[0]-x[x.length-1]) <= epsilon) ? periodic : notaknot;
if(ysplinetype == null)
ysplinetype=(abs(y[0]-y[y.length-1]) <= epsilon) ? periodic : notaknot;
int n=x.length; int m=y.length;
real[][] ft=transpose(f);
real[][] tp=new real[m][];
for(int j=0; j < m; ++j)
tp[j]=xsplinetype(x,ft[j]);
real[][] q=new real[n][];
for(int i=0; i < n; ++i)
q[i]=ysplinetype(y,f[i]);
real[][] qt=transpose(q);
real[] d1=xsplinetype(x,qt[0]);
real[] d2=xsplinetype(x,qt[m-1]);
real[][] r=new real[n][];
real[][] p=transpose(tp);
for(int i=0; i < n; ++i)
r[i]=clamped(d1[i],d2[i])(y,p[i]);
return bispline0(f,p,q,r,x,y,cond);
}
// return the surface described by a real matrix f, interpolated with
// xsplinetype and ysplinetype.
surface surface(picture pic=currentpicture, real[][] f, real[] x, real[] y,
splinetype xsplinetype=null,
splinetype ysplinetype=xsplinetype,
bool[][] cond={})
{
if(xsplinetype == linear && ysplinetype == linear) {
int nx=f.length-1;
int ny=nx > 0 ? f[0].length-1 : 0;
// return the surface described by a real matrix f, interpolated with
// xsplinetype and ysplinetype.
surface surface(picture pic=currentpicture, real[][] f, pair a, pair b,
splinetype xsplinetype, splinetype ysplinetype=xsplinetype,
bool[][] cond={})
{
if(!rectangular(f)) abort("matrix is not rectangular");
int nx=f.length-1;
int ny=nx > 0 ? f[0].length-1 : 0;
// return the surface described by a real matrix f, interpolated linearly.
surface surface(picture pic=currentpicture, real[][] f, pair a, pair b,
bool[][] cond={})
{
if(!rectangular(f)) abort("matrix is not rectangular");
int nx=f.length-1;
int ny=nx > 0 ? f[0].length-1 : 0;
// return the surface described by a parametric function f over box(a,b),
// interpolated linearly.
surface surface(picture pic=currentpicture, triple f(pair z), pair a, pair b,
int nu=nmesh, int nv=nu, bool cond(pair z)=null)
{
if(nu <= 0 || nv <= 0) return nullsurface;
// return the surface described by a parametric function f evaluated at u and v
// and interpolated with usplinetype and vsplinetype.
surface surface(picture pic=currentpicture, triple f(pair z),
real[] u, real[] v, splinetype[] usplinetype,
splinetype[] vsplinetype=Spline, bool cond(pair z)=null)
{
int nu=u.length-1;
int nv=v.length-1;
real[] ipt=sequence(u.length);
real[] jpt=sequence(v.length);
real[][] fx=new real[u.length][v.length];
real[][] fy=new real[u.length][v.length];
real[][] fz=new real[u.length][v.length];
// return the surface described by a parametric function f over box(a,b),
// interpolated with usplinetype and vsplinetype.
surface surface(picture pic=currentpicture, triple f(pair z), pair a, pair b,
int nu=nmesh, int nv=nu,
splinetype[] usplinetype, splinetype[] vsplinetype=Spline,
bool cond(pair z)=null)
{
real[] x=uniform(pic.scale.x.T,pic.scale.x.Tinv,a.x,b.x,nu);
real[] y=uniform(pic.scale.y.T,pic.scale.y.Tinv,a.y,b.y,nv);
return surface(pic,f,x,y,usplinetype,vsplinetype,cond);
}
// return the surface described by a real function f over box(a,b),
// interpolated linearly.
surface surface(picture pic=currentpicture, real f(pair z), pair a, pair b,
int nx=nmesh, int ny=nx, bool cond(pair z)=null)
{
return surface(pic,new triple(pair z) {return (z.x,z.y,f(z));},a,b,nx,ny,
cond);
}
// return the surface described by a real function f over box(a,b),
// interpolated with xsplinetype and ysplinetype.
surface surface(picture pic=currentpicture, real f(pair z), pair a, pair b,
int nx=nmesh, int ny=nx, splinetype xsplinetype,
splinetype ysplinetype=xsplinetype, bool cond(pair z)=null)
{
bool[][] active;
bool all=cond == null;
if(!all) active=new bool[nx+1][ny+1];
real dx=1/nx;
real dy=1/ny;
pair Idy=(0,dy);
pair dz=(dx,dy);
real maxlength(triple f(pair z), pair a, pair b, int nu, int nv)
{
return min(abs(f((b.x,a.y))-f(a))/nu,abs(f((a.x,b.y))-f(a))/nv);
}
// return a vector field on a parametric surface f over box(a,b).
picture vectorfield(path3 vector(pair v), triple f(pair z), pair a, pair b,
int nu=nmesh, int nv=nu, 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)
{
picture pic;
real du=(b.x-a.x)/(nu-1);
real dv=(b.y-a.y)/(nv-1);
bool all=cond == null;
real scale;
if(maxlength > 0) {
real size(pair z) {
path3 g=vector(z);
triple w=point(g,size(g)-1)-point(g,0);
return max(w.x,w.y,w.z);
}
real max=size(a);
for(int i=0; i <= nu; ++i) {
real u=a.x+i*du;
for(int j=0; j < nv; ++j) {
real v=a.y+j*dv;
max=max(max,size((u,v)));
}
}
scale=max > 0 ? maxlength/max : 1;
} else scale=1;
real fuzz=sqrtEpsilon*max(abs(v1),abs(v2));
if(abs(v1.z) > fuzz || abs(v2.z) > fuzz)
abort("invalid normal vector");
real phi1=radians(longitude(v1,warn=false));
real phi2=radians(longitude(v2,warn=false));
if(direction) {
if(phi1 >= phi2) phi1 -= 2pi;
} else if(phi2 >= phi1) phi2 -= 2pi;
static real piby2=pi/2;
return shift(c)*T*polargraph(new real(real theta, real phi) {return r;},
new real(real t) {return piby2;},
new real(real t) {return interp(phi1,phi2,t);},
n,operator ..);
}
path3 Arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
triple normal=O, bool direction, int n=nCircle)
{
return Arc(c,c+r*dir(theta1,phi1),c+r*dir(theta2,phi2),normal,direction,n);
}
path3 Arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
triple normal=O, int n=nCircle)
{
return Arc(c,r,theta1,phi1,theta2,phi2,normal,
theta2 > theta1 || (theta2 == theta1 && phi2 >= phi1) ? CCW : CW,
n);
}
// True circle
path3 Circle(triple c, real r, triple normal=Z, int n=nCircle)
{
static real piby2=pi/2;
return shift(c)*align(unit(normal))*
polargraph(new real(real theta, real phi) {return r;},
new real(real t) {return piby2;},
new real(real t) {return interp(0,2pi,t);},n,operator ..);