diff options
Diffstat (limited to 'Master/texmf/asymptote/plain_picture.asy')
-rw-r--r-- | Master/texmf/asymptote/plain_picture.asy | 1646 |
1 files changed, 0 insertions, 1646 deletions
diff --git a/Master/texmf/asymptote/plain_picture.asy b/Master/texmf/asymptote/plain_picture.asy deleted file mode 100644 index 5b06b7f737c..00000000000 --- a/Master/texmf/asymptote/plain_picture.asy +++ /dev/null @@ -1,1646 +0,0 @@ -// Pre picture <<<1 -import plain_scaling; -import plain_bounds; - -include plain_prethree; - -// This variable is required by asymptote.sty. -pair viewportsize=0; // Horizontal and vertical viewport limits. - -restricted bool Aspect=true; -restricted bool IgnoreAspect=false; - -struct coords3 { - coord[] x,y,z; - void erase() { - x.delete(); - y.delete(); - z.delete(); - } - // Only a shallow copy of the individual elements of x and y - // is needed since, once entered, they are never modified. - coords3 copy() { - coords3 c=new coords3; - c.x=copy(x); - c.y=copy(y); - c.z=copy(z); - return c; - } - void append(coords3 c) { - x.append(c.x); - y.append(c.y); - z.append(c.z); - } - void push(triple user, triple truesize) { - x.push(coord.build(user.x,truesize.x)); - y.push(coord.build(user.y,truesize.y)); - z.push(coord.build(user.z,truesize.z)); - } - void push(coord cx, coord cy, coord cz) { - x.push(cx); - y.push(cy); - z.push(cz); - } - void push(transform3 t, coords3 c1, coords3 c2, coords3 c3) { - for(int i=0; i < c1.x.length; ++i) { - coord cx=c1.x[i], cy=c2.y[i], cz=c3.z[i]; - triple tinf=shiftless(t)*(0,0,0); - triple z=t*(cx.user,cy.user,cz.user); - triple w=(cx.truesize,cy.truesize,cz.truesize); - w=length(w)*unit(shiftless(t)*w); - coord Cx,Cy,Cz; - Cx.user=z.x; - Cy.user=z.y; - Cz.user=z.z; - Cx.truesize=w.x; - Cy.truesize=w.y; - Cz.truesize=w.z; - push(Cx,Cy,Cz); - } - } -} - -// scaleT and Legend <<< -typedef real scalefcn(real x); - -struct scaleT { - scalefcn T,Tinv; - bool logarithmic; - bool automin,automax; - void operator init(scalefcn T, scalefcn Tinv, bool logarithmic=false, - bool automin=false, bool automax=false) { - this.T=T; - this.Tinv=Tinv; - this.logarithmic=logarithmic; - this.automin=automin; - this.automax=automax; - } - scaleT copy() { - scaleT dest=scaleT(T,Tinv,logarithmic,automin,automax); - return dest; - } -}; - -scaleT operator init() -{ - scaleT S=scaleT(identity,identity); - return S; -} - -typedef void boundRoutine(); - -struct autoscaleT { - scaleT scale; - scaleT postscale; - real tickMin=-infinity, tickMax=infinity; - boundRoutine[] bound; // Optional routines to recompute the bounding box. - bool automin=false, automax=false; - bool automin() {return automin && scale.automin;} - bool automax() {return automax && scale.automax;} - - real T(real x) {return postscale.T(scale.T(x));} - scalefcn T() {return scale.logarithmic ? postscale.T : T;} - real Tinv(real x) {return scale.Tinv(postscale.Tinv(x));} - - autoscaleT copy() { - autoscaleT dest=new autoscaleT; - dest.scale=scale.copy(); - dest.postscale=postscale.copy(); - dest.tickMin=tickMin; - dest.tickMax=tickMax; - dest.bound=copy(bound); - dest.automin=(bool) automin; - dest.automax=(bool) automax; - return dest; - } -} - -struct ScaleT { - bool set; - autoscaleT x; - autoscaleT y; - autoscaleT z; - - ScaleT copy() { - ScaleT dest=new ScaleT; - dest.set=set; - dest.x=x.copy(); - dest.y=y.copy(); - dest.z=z.copy(); - return dest; - } -}; - -struct Legend { - string label; - pen plabel; - pen p; - frame mark; - bool above; - void operator init(string label, pen plabel=currentpen, pen p=nullpen, - frame mark=newframe, bool above=true) { - this.label=label; - this.plabel=plabel; - this.p=(p == nullpen) ? plabel : p; - this.mark=mark; - this.above=above; - } -} - -// >>> - -// Frame Alignment was here - -triple min3(pen p) -{ - return linewidth(p)*(-0.5,-0.5,-0.5); -} - -triple max3(pen p) -{ - return linewidth(p)*(0.5,0.5,0.5); -} - -// A function that draws an object to frame pic, given that the transform -// from user coordinates to true-size coordinates is t. -typedef void drawer(frame f, transform t); - -// A generalization of drawer that includes the final frame's bounds. -// TODO: Add documentation as to what T is. -typedef void drawerBound(frame f, transform t, transform T, pair lb, pair rt); - -// PairOrTriple <<<1 -// This struct is used to represent a userMin/userMax which serves as both a -// pair and a triple depending on the context. -struct pairOrTriple { - real x,y,z; - void init() { x = y = z = 0; } -}; -void copyPairOrTriple(pairOrTriple dest, pairOrTriple src) -{ - dest.x = src.x; - dest.y = src.y; - dest.z = src.z; -} -pair operator cast (pairOrTriple a) { - return (a.x, a.y); -}; -triple operator cast (pairOrTriple a) { - return (a.x, a.y, a.z); -} -void write(pairOrTriple a) { - write((triple) a); -} - -struct picture { // <<<1 - // Nodes <<<2 - // Three-dimensional version of drawer and drawerBound: - typedef void drawer3(frame f, transform3 t, picture pic, projection P); - typedef void drawerBound3(frame f, transform3 t, transform3 T, - picture pic, projection P, triple lb, triple rt); - - // The functions to do the deferred drawing. - drawerBound[] nodes; - drawerBound3[] nodes3; - - bool uptodate=true; - - struct bounds3 { - coords3 point,min,max; - bool exact=true; // An accurate picture bounds is provided by the user. - void erase() { - point.erase(); - min.erase(); - max.erase(); - } - bounds3 copy() { - bounds3 b=new bounds3; - b.point=point.copy(); - b.min=min.copy(); - b.max=max.copy(); - b.exact=exact; - return b; - } - } - - bounds bounds; - bounds3 bounds3; - - // Other Fields <<<2 - // Transform to be applied to this picture. - transform T; - transform3 T3; - - // The internal representation of the 3D user bounds. - private pairOrTriple umin, umax; - private bool usetx, usety, usetz; - - ScaleT scale; // Needed by graph - Legend[] legend; - - pair[] clipmax; // Used by beginclip/endclip - pair[] clipmin; - - // The maximum sizes in the x, y, and z directions; zero means no restriction. - real xsize=0, ysize=0; - - real xsize3=0, ysize3=0, zsize3=0; - - // Fixed unitsizes in the x y, and z directions; zero means use - // xsize, ysize, and zsize. - real xunitsize=0, yunitsize=0, zunitsize=0; - - // If true, the x and y directions must be scaled by the same amount. - bool keepAspect=true; - - // A fixed scaling transform. - bool fixed; - transform fixedscaling; - - // Init and erase <<<2 - void init() { - umin.init(); - umax.init(); - usetx=usety=usetz=false; - T3=identity(4); - } - init(); - - // Erase the current picture, retaining any size specification. - void erase() { - nodes.delete(); - nodes3.delete(); - bounds.erase(); - bounds3.erase(); - T=identity(); - scale=new ScaleT; - legend.delete(); - init(); - } - - // Empty <<<2 - bool empty2() { - return nodes.length == 0; - } - - bool empty3() { - return nodes3.length == 0; - } - - bool empty() { - return empty2() && empty3(); - } - - // User min/max <<<2 - pair userMin2() {return bounds.userMin(); } - pair userMax2() {return bounds.userMax(); } - - bool userSetx2() { return bounds.userBoundsAreSet(); } - bool userSety2() { return bounds.userBoundsAreSet(); } - - triple userMin3() { return umin; } - triple userMax3() { return umax; } - - bool userSetx3() { return usetx; } - bool userSety3() { return usety; } - bool userSetz3() { return usetz; } - - private typedef real binop(real, real); - - // Helper functions for finding the minimum/maximum of two data, one of - // which may not be defined. - private static real merge(real x1, bool set1, real x2, bool set2, binop m) - { - return set1 ? (set2 ? m(x1,x2) : x1) : x2; - } - private pairOrTriple userExtreme(pair u2(), triple u3(), binop m) - { - bool setx2 = userSetx2(); - bool sety2 = userSety2(); - bool setx3 = userSetx3(); - bool sety3 = userSety3(); - - pair p; - if (setx2 || sety2) - p = u2(); - triple t = u3(); - - pairOrTriple r; - r.x = merge(p.x, setx2, t.x, setx3, m); - r.y = merge(p.y, sety2, t.y, sety3, m); - r.z = t.z; - - return r; - } - - // The combination of 2D and 3D data. - pairOrTriple userMin() { - return userExtreme(userMin2, userMin3, min); - } - pairOrTriple userMax() { - return userExtreme(userMax2, userMax3, max); - } - - bool userSetx() { return userSetx2() || userSetx3(); } - bool userSety() { return userSety2() || userSety3(); } - bool userSetz() = userSetz3; - - // Functions for setting the user bounds. - void userMinx3(real x) { - umin.x=x; - usetx=true; - } - - void userMiny3(real y) { - umin.y=y; - usety=true; - } - - void userMinz3(real z) { - umin.z=z; - usetz=true; - } - - void userMaxx3(real x) { - umax.x=x; - usetx=true; - } - - void userMaxy3(real y) { - umax.y=y; - usety=true; - } - - void userMaxz3(real z) { - umax.z=z; - usetz=true; - } - - void userMinx2(real x) { bounds.alterUserBound("minx", x); } - void userMinx(real x) { userMinx2(x); userMinx3(x); } - void userMiny2(real y) { bounds.alterUserBound("miny", y); } - void userMiny(real y) { userMiny2(y); userMiny3(y); } - void userMaxx2(real x) { bounds.alterUserBound("maxx", x); } - void userMaxx(real x) { userMaxx2(x); userMaxx3(x); } - void userMaxy2(real y) { bounds.alterUserBound("maxy", y); } - void userMaxy(real y) { userMaxy2(y); userMaxy3(y); } - void userMinz(real z) = userMinz3; - void userMaxz(real z) = userMaxz3; - - void userCorners3(triple c000, triple c001, triple c010, triple c011, - triple c100, triple c101, triple c110, triple c111) { - umin.x = min(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x); - umin.y = min(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y); - umin.z = min(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z); - umax.x = max(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x); - umax.y = max(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y); - umax.z = max(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z); - } - - // Cache the current user-space bounding box x coodinates - void userBoxX3(real min, real max, binop m=min, binop M=max) { - if (usetx) { - umin.x=m(umin.x,min); - umax.x=M(umax.x,max); - } else { - umin.x=min; - umax.x=max; - usetx=true; - } - } - - // Cache the current user-space bounding box y coodinates - void userBoxY3(real min, real max, binop m=min, binop M=max) { - if (usety) { - umin.y=m(umin.y,min); - umax.y=M(umax.y,max); - } else { - umin.y=min; - umax.y=max; - usety=true; - } - } - - // Cache the current user-space bounding box z coodinates - void userBoxZ3(real min, real max, binop m=min, binop M=max) { - if (usetz) { - umin.z=m(umin.z,min); - umax.z=M(umax.z,max); - } else { - umin.z=min; - umax.z=max; - usetz=true; - } - } - - // Cache the current user-space bounding box - void userBox3(triple min, triple max) { - userBoxX3(min.x,max.x); - userBoxY3(min.y,max.y); - userBoxZ3(min.z,max.z); - } - - // Add drawer <<<2 - void add(drawerBound d, bool exact=false, bool above=true) { - uptodate=false; - if(!exact) bounds.exact=false; - if(above) - nodes.push(d); - else - nodes.insert(0,d); - } - - // Faster implementation of most common case. - void addExactAbove(drawerBound d) { - uptodate=false; - nodes.push(d); - } - - void add(drawer d, bool exact=false, bool above=true) { - add(new void(frame f, transform t, transform T, pair, pair) { - d(f,t*T); - },exact,above); - } - - void add(drawerBound3 d, bool exact=false, bool above=true) { - uptodate=false; - if(!exact) bounds.exact=false; - if(above) - nodes3.push(d); - else - nodes3.insert(0,d); - } - - void add(drawer3 d, bool exact=false, bool above=true) { - add(new void(frame f, transform3 t, transform3 T, picture pic, - projection P, triple, triple) { - d(f,t*T,pic,P); - },exact,above); - } - - // Clip <<<2 - void clip(pair min, pair max, drawer d, bool exact=false) { - bounds.clip(min, max); - this.add(d,exact); - } - - void clip(pair min, pair max, drawerBound d, bool exact=false) { - bounds.clip(min, max); - this.add(d,exact); - } - - // Add sizing <<<2 - // Add a point to the sizing. - void addPoint(pair user, pair truesize=0) { - bounds.addPoint(user,truesize); - //userBox(user,user); - } - - // Add a point to the sizing, accounting also for the size of the pen. - void addPoint(pair user, pair truesize=0, pen p) { - addPoint(user,truesize+min(p)); - addPoint(user,truesize+max(p)); - } - - void addPoint(triple user, triple truesize=(0,0,0)) { - bounds3.point.push(user,truesize); - userBox3(user,user); - } - - void addPoint(triple user, triple truesize=(0,0,0), pen p) { - addPoint(user,truesize+min3(p)); - addPoint(user,truesize+max3(p)); - } - - // Add a box to the sizing. - void addBox(pair userMin, pair userMax, pair trueMin=0, pair trueMax=0) { - bounds.addBox(userMin, userMax, trueMin, trueMax); - } - - void addBox(triple userMin, triple userMax, triple trueMin=(0,0,0), - triple trueMax=(0,0,0)) { - bounds3.min.push(userMin,trueMin); - bounds3.max.push(userMax,trueMax); - userBox3(userMin,userMax); - } - - // For speed reason, we unravel the addPath routines from bounds. This - // avoids an extra function call. - from bounds unravel addPath; - - // Size commands <<<2 - void size(real x, real y=x, bool keepAspect=this.keepAspect) { - if(!empty()) uptodate=false; - xsize=x; - ysize=y; - this.keepAspect=keepAspect; - } - - void size3(real x, real y=x, real z=y, bool keepAspect=this.keepAspect) { - if(!empty3()) uptodate=false; - xsize3=x; - ysize3=y; - zsize3=z; - this.keepAspect=keepAspect; - } - - void unitsize(real x, real y=x, real z=y) { - uptodate=false; - xunitsize=x; - yunitsize=y; - zunitsize=z; - } - - // min/max of picture <<<2 - // Calculate the min for the final frame, given the coordinate transform. - pair min(transform t) { - return bounds.min(t); - } - - // Calculate the max for the final frame, given the coordinate transform. - pair max(transform t) { - return bounds.max(t); - } - - // Calculate the min for the final frame, given the coordinate transform. - triple min(transform3 t) { - if(bounds3.min.x.length == 0 && bounds3.point.x.length == 0 && - bounds3.max.x.length == 0) return (0,0,0); - triple a=t*(1,1,1)-t*(0,0,0), b=t*(0,0,0); - scaling xs=scaling.build(a.x,b.x); - scaling ys=scaling.build(a.y,b.y); - scaling zs=scaling.build(a.z,b.z); - return (min(min(min(infinity,xs,bounds3.point.x),xs,bounds3.min.x), - xs,bounds3.max.x), - min(min(min(infinity,ys,bounds3.point.y),ys,bounds3.min.y), - ys,bounds3.max.y), - min(min(min(infinity,zs,bounds3.point.z),zs,bounds3.min.z), - zs,bounds3.max.z)); - } - - // Calculate the max for the final frame, given the coordinate transform. - triple max(transform3 t) { - if(bounds3.min.x.length == 0 && bounds3.point.x.length == 0 && - bounds3.max.x.length == 0) return (0,0,0); - triple a=t*(1,1,1)-t*(0,0,0), b=t*(0,0,0); - scaling xs=scaling.build(a.x,b.x); - scaling ys=scaling.build(a.y,b.y); - scaling zs=scaling.build(a.z,b.z); - return (max(max(max(-infinity,xs,bounds3.point.x),xs,bounds3.min.x), - xs,bounds3.max.x), - max(max(max(-infinity,ys,bounds3.point.y),ys,bounds3.min.y), - ys,bounds3.max.y), - max(max(max(-infinity,zs,bounds3.point.z),zs,bounds3.min.z), - zs,bounds3.max.z)); - } - - void append(coords3 point, coords3 min, coords3 max, transform3 t, - bounds3 bounds) - { - // Add the coord info to this picture. - if(t == identity4) { - point.append(bounds.point); - min.append(bounds.min); - max.append(bounds.max); - } else { - point.push(t,bounds.point,bounds.point,bounds.point); - // Add in all 8 corner points, to properly size cuboid pictures. - point.push(t,bounds.min,bounds.min,bounds.min); - point.push(t,bounds.min,bounds.min,bounds.max); - point.push(t,bounds.min,bounds.max,bounds.min); - point.push(t,bounds.min,bounds.max,bounds.max); - point.push(t,bounds.max,bounds.min,bounds.min); - point.push(t,bounds.max,bounds.min,bounds.max); - point.push(t,bounds.max,bounds.max,bounds.min); - point.push(t,bounds.max,bounds.max,bounds.max); - } - } - - // Scaling and Fit <<<2 - // Returns the transform for turning user-space pairs into true-space pairs. - transform scaling(real xsize, real ysize, bool keepAspect=true, - bool warn=true) { - bounds b = (T == identity()) ? this.bounds : T * this.bounds; - - return b.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn); - } - - transform scaling(bool warn=true) { - return scaling(xsize,ysize,keepAspect,warn); - } - - // Returns the transform for turning user-space pairs into true-space triples. - transform3 scaling(real xsize, real ysize, real zsize, bool keepAspect=true, - bool warn=true) { - if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0 - && zsize == 0 && zunitsize == 0) - return identity(4); - - coords3 Coords; - - append(Coords,Coords,Coords,T3,bounds3); - - real sx; - if(xunitsize == 0) { - if(xsize != 0) sx=calculateScaling("x",Coords.x,xsize,warn); - } else sx=xunitsize; - - real sy; - if(yunitsize == 0) { - if(ysize != 0) sy=calculateScaling("y",Coords.y,ysize,warn); - } else sy=yunitsize; - - real sz; - if(zunitsize == 0) { - if(zsize != 0) sz=calculateScaling("z",Coords.z,zsize,warn); - } else sz=zunitsize; - - if(sx == 0) { - sx=max(sy,sz); - if(sx == 0) - return identity(4); - } - if(sy == 0) sy=max(sz,sx); - if(sz == 0) sz=max(sx,sy); - - if(keepAspect && (xunitsize == 0 || yunitsize == 0 || zunitsize == 0)) - return scale3(min(sx,sy,sz)); - else - return scale(sx,sy,sz); - } - - transform3 scaling3(bool warn=true) { - return scaling(xsize3,ysize3,zsize3,keepAspect,warn); - } - - frame fit(transform t, transform T0=T, pair m, pair M) { - frame f; - int n = nodes.length; - for(int i=0; i < n; ++i) - nodes[i](f,t,T0,m,M); - return f; - } - - frame fit3(transform3 t, transform3 T0=T3, picture pic, projection P, - triple m, triple M) { - frame f; - for(int i=0; i < nodes3.length; ++i) - nodes3[i](f,t,T0,pic,P,m,M); - return f; - } - - // Returns a rigid version of the picture using t to transform user coords - // into truesize coords. - frame fit(transform t) { - return fit(t,min(t),max(t)); - } - - frame fit3(transform3 t, picture pic, projection P) { - return fit3(t,pic,P,min(t),max(t)); - } - - // Add drawer wrappers <<<2 - void add(void d(picture, transform), bool exact=false) { - add(new void(frame f, transform t) { - picture opic=new picture; - d(opic,t); - add(f,opic.fit(identity())); - },exact); - } - - void add(void d(picture, transform3), bool exact=false, bool above=true) { - add(new void(frame f, transform3 t, picture pic2, projection P) { - picture opic=new picture; - d(opic,t); - add(f,opic.fit3(identity4,pic2,P)); - },exact,above); - } - - void add(void d(picture, transform3, transform3, triple, triple), - bool exact=false, bool above=true) { - add(new void(frame f, transform3 t, transform3 T, picture pic2, - projection P, triple lb, triple rt) { - picture opic=new picture; - d(opic,t,T,lb,rt); - add(f,opic.fit3(identity4,pic2,P)); - },exact,above); - } - - // More scaling <<<2 - frame scaled() { - frame f=fit(fixedscaling); - pair d=size(f); - static real epsilon=100*realEpsilon; - if(d.x > xsize*(1+epsilon)) - warning("xlimit","frame exceeds xlimit: "+(string) d.x+" > "+ - (string) xsize); - if(d.y > ysize*(1+epsilon)) - warning("ylimit","frame exceeds ylimit: "+(string) d.y+" > "+ - (string) ysize); - return f; - } - - // Calculate additional scaling required if only an approximate picture - // size estimate is available. - transform scale(frame f, real xsize=this.xsize, real ysize=this.ysize, - bool keepaspect=this.keepAspect) { - if(bounds.exact) return identity(); - pair m=min(f); - pair M=max(f); - real width=M.x-m.x; - real height=M.y-m.y; - real xgrow=xsize == 0 || width == 0 ? 1 : xsize/width; - real ygrow=ysize == 0 || height == 0 ? 1 : ysize/height; - if(keepAspect) { - real[] grow; - if(xsize > 0) grow.push(xgrow); - if(ysize > 0) grow.push(ygrow); - return scale(grow.length == 0 ? 1 : min(grow)); - } else return scale(xgrow,ygrow); - - } - - // Calculate additional scaling required if only an approximate picture - // size estimate is available. - transform3 scale3(frame f, real xsize3=this.xsize3, - real ysize3=this.ysize3, real zsize3=this.zsize3, - bool keepaspect=this.keepAspect) { - if(bounds3.exact) return identity(4); - triple m=min3(f); - triple M=max3(f); - real width=M.x-m.x; - real height=M.y-m.y; - real depth=M.z-m.z; - real xgrow=xsize3 == 0 || width == 0 ? 1 : xsize3/width; - real ygrow=ysize3 == 0 || height == 0 ? 1 : ysize3/height; - real zgrow=zsize3 == 0 || depth == 0 ? 1 : zsize3/depth; - if(keepAspect) { - real[] grow; - if(xsize3 > 0) grow.push(xgrow); - if(ysize3 > 0) grow.push(ygrow); - if(zsize3 > 0) grow.push(zgrow); - return scale3(grow.length == 0 ? 1 : min(grow)); - } else return scale(xgrow,ygrow,zgrow); - } - - // calculateTransform with scaling <<<2 - // Return the transform that would be used to fit the picture to a frame - transform calculateTransform(real xsize, real ysize, bool keepAspect=true, - bool warn=true) { - transform t=scaling(xsize,ysize,keepAspect,warn); - return scale(fit(t),xsize,ysize,keepAspect)*t; - } - - transform calculateTransform(bool warn=true) { - if(fixed) return fixedscaling; - return calculateTransform(xsize,ysize,keepAspect,warn); - } - - transform3 calculateTransform3(real xsize=xsize3, real ysize=ysize3, - real zsize=zsize3, - bool keepAspect=true, bool warn=true, - projection P=currentprojection) { - transform3 t=scaling(xsize,ysize,zsize,keepAspect,warn); - return scale3(fit3(t,null,P),keepAspect)*t; - } - - // min/max with xsize and ysize <<<2 - // NOTE: These are probably very slow as implemented. - pair min(real xsize=this.xsize, real ysize=this.ysize, - bool keepAspect=this.keepAspect, bool warn=true) { - return min(calculateTransform(xsize,ysize,keepAspect,warn)); - } - - pair max(real xsize=this.xsize, real ysize=this.ysize, - bool keepAspect=this.keepAspect, bool warn=true) { - return max(calculateTransform(xsize,ysize,keepAspect,warn)); - } - - triple min3(real xsize=this.xsize3, real ysize=this.ysize3, - real zsize=this.zsize3, bool keepAspect=this.keepAspect, - bool warn=true, projection P) { - return min(calculateTransform3(xsize,ysize,zsize,keepAspect,warn,P)); - } - - triple max3(real xsize=this.xsize3, real ysize=this.ysize3, - real zsize=this.zsize3, bool keepAspect=this.keepAspect, - bool warn=true, projection P) { - return max(calculateTransform3(xsize,ysize,zsize,keepAspect,warn,P)); - } - - // More Fitting <<<2 - // Returns the 2D picture fit to the requested size. - frame fit2(real xsize=this.xsize, real ysize=this.ysize, - bool keepAspect=this.keepAspect) { - if(fixed) return scaled(); - if(empty2()) - return newframe; - - transform t=scaling(xsize,ysize,keepAspect); - frame f=fit(t); - transform s=scale(f,xsize,ysize,keepAspect); - if(s == identity()) return f; - return fit(s*t); - } - - static frame fitter(string,picture,string,real,real,bool,bool,string,string, - light,projection); - frame fit(string prefix="", string format="", - real xsize=this.xsize, real ysize=this.ysize, - bool keepAspect=this.keepAspect, bool view=false, - string options="", string script="", light light=currentlight, - projection P=currentprojection) { - return fitter == null ? fit2(xsize,ysize,keepAspect) : - fitter(prefix,this,format,xsize,ysize,keepAspect,view,options,script, - light,P); - } - - // Fit a 3D picture. - frame fit3(projection P=currentprojection) { - if(settings.render == 0) return fit(P); - if(fixed) return scaled(); - if(empty3()) return newframe; - transform3 t=scaling(xsize3,ysize3,zsize3,keepAspect); - frame f=fit3(t,null,P); - transform3 s=scale3(f,xsize3,ysize3,zsize3,keepAspect); - if(s == identity4) return f; - return fit3(s*t,null,P); - } - - // In case only an approximate picture size estimate is available, return the - // fitted frame slightly scaled (including labels and true size distances) - // so that it precisely meets the given size specification. - frame scale(real xsize=this.xsize, real ysize=this.ysize, - bool keepAspect=this.keepAspect) { - frame f=fit(xsize,ysize,keepAspect); - transform s=scale(f,xsize,ysize,keepAspect); - if(s == identity()) return f; - return s*f; - } - - // Copying <<<2 - - // Copies enough information to yield the same userMin/userMax. - void userCopy2(picture pic) { - userMinx2(pic.userMin2().x); - userMiny2(pic.userMin2().y); - userMaxx2(pic.userMax2().x); - userMaxy2(pic.userMax2().y); - } - - void userCopy3(picture pic) { - copyPairOrTriple(umin, pic.umin); - copyPairOrTriple(umax, pic.umax); - usetx=pic.usetx; - usety=pic.usety; - usetz=pic.usetz; - } - - void userCopy(picture pic) { - userCopy2(pic); - userCopy3(pic); - } - - // Copies the drawing information, but not the sizing information into a new - // picture. Fitting this picture will not scale as the original picture would. - picture drawcopy() { - picture dest=new picture; - dest.nodes = copy(nodes); - dest.nodes3=copy(nodes3); - dest.T=T; - dest.T3=T3; - - // TODO: User bounds are sizing info, which probably shouldn't be part of - // a draw copy. Should we move this down to copy()? - dest.userCopy3(this); - - dest.scale=scale.copy(); - dest.legend=copy(legend); - - return dest; - } - - // A deep copy of this picture. Modifying the copied picture will not affect - // the original. - picture copy() { - picture dest=drawcopy(); - - dest.uptodate=uptodate; - dest.bounds=bounds.copy(); - dest.bounds3=bounds3.copy(); - - dest.xsize=xsize; dest.ysize=ysize; - dest.xsize3=xsize; dest.ysize3=ysize3; dest.zsize3=zsize3; - dest.keepAspect=keepAspect; - dest.xunitsize=xunitsize; dest.yunitsize=yunitsize; - dest.zunitsize=zunitsize; - dest.fixed=fixed; dest.fixedscaling=fixedscaling; - - return dest; - } - - // Helper function for defining transformed pictures. Do not call it - // directly. - picture transformed(transform t) { - picture dest=drawcopy(); - - // Replace nodes with a single drawer that realizes the transform. - drawerBound[] oldnodes = dest.nodes; - void drawAll(frame f, transform tt, transform T, pair lb, pair rt) { - transform Tt = T*t; - for (var node : oldnodes) - node(f, tt, Tt, lb, rt); - } - dest.nodes = new drawerBound[] { drawAll }; - - dest.uptodate=uptodate; - dest.bounds=bounds.transformed(t); - dest.bounds3=bounds3.copy(); - - dest.bounds.exact=false; - - dest.xsize=xsize; dest.ysize=ysize; - dest.xsize3=xsize; dest.ysize3=ysize3; dest.zsize3=zsize3; - dest.keepAspect=keepAspect; - dest.xunitsize=xunitsize; dest.yunitsize=yunitsize; - dest.zunitsize=zunitsize; - dest.fixed=fixed; dest.fixedscaling=fixedscaling; - - return dest; - } - - // Add Picture <<<2 - // Add a picture to this picture, such that the user coordinates will be - // scaled identically when fitted - void add(picture src, bool group=true, filltype filltype=NoFill, - bool above=true) { - // Copy the picture. Only the drawing function closures are needed, so we - // only copy them. This needs to be a deep copy, as src could later have - // objects added to it that should not be included in this picture. - - if(src == this) abort("cannot add picture to itself"); - - uptodate=false; - - picture srcCopy=src.drawcopy(); - // Draw by drawing the copied picture. - if(srcCopy.nodes.length > 0) - nodes.push(new void(frame f, transform t, transform T, pair m, pair M) { - add(f,srcCopy.fit(t,T*srcCopy.T,m,M),group,filltype,above); - }); - - if(srcCopy.nodes3.length > 0) { - nodes3.push(new void(frame f, transform3 t, transform3 T3, picture pic, - projection P, triple m, triple M) { - add(f,srcCopy.fit3(t,T3*srcCopy.T3,pic,P,m,M),group,above); - }); - } - - legend.append(src.legend); - - if(src.usetx) userBoxX3(src.umin.x,src.umax.x); - if(src.usety) userBoxY3(src.umin.y,src.umax.y); - if(src.usetz) userBoxZ3(src.umin.z,src.umax.z); - - bounds.append(srcCopy.T, src.bounds); - //append(bounds.point,bounds.min,bounds.max,srcCopy.T,src.bounds); - append(bounds3.point,bounds3.min,bounds3.max,srcCopy.T3,src.bounds3); - - //if(!src.bounds.exact) bounds.exact=false; - if(!src.bounds3.exact) bounds3.exact=false; - } -} - -// Post Struct <<<1 -picture operator * (transform t, picture orig) -{ - return orig.transformed(t); -} - -picture operator * (transform3 t, picture orig) -{ - picture pic=orig.copy(); - pic.T3=t*pic.T3; - triple umin=pic.userMin3(), umax=pic.userMax3(); - pic.userCorners3(t*umin, - t*(umin.x,umin.y,umax.z), - t*(umin.x,umax.y,umin.z), - t*(umin.x,umax.y,umax.z), - t*(umax.x,umin.y,umin.z), - t*(umax.x,umin.y,umax.z), - t*(umax.x,umax.y,umin.z), - t*umax); - pic.bounds3.exact=false; - return pic; -} - -picture currentpicture; - -void size(picture pic=currentpicture, real x, real y=x, - bool keepAspect=pic.keepAspect) -{ - pic.size(x,y,keepAspect); -} - -void size3(picture pic=currentpicture, real x, real y=x, real z=y, - bool keepAspect=pic.keepAspect) -{ - pic.size3(x,y,z,keepAspect); -} - -void unitsize(picture pic=currentpicture, real x, real y=x, real z=y) -{ - pic.unitsize(x,y,z); -} - -void size(picture pic=currentpicture, real xsize, real ysize, - pair min, pair max) -{ - pair size=max-min; - pic.unitsize(size.x != 0 ? xsize/size.x : 0, - size.y != 0 ? ysize/size.y : 0); -} - -void size(picture dest, picture src) -{ - dest.size(src.xsize,src.ysize,src.keepAspect); - dest.size3(src.xsize3,src.ysize3,src.zsize3,src.keepAspect); - dest.unitsize(src.xunitsize,src.yunitsize,src.zunitsize); -} - -pair min(picture pic, bool user=false) -{ - transform t=pic.calculateTransform(); - pair z=pic.min(t); - return user ? inverse(t)*z : z; -} - -pair max(picture pic, bool user=false) -{ - transform t=pic.calculateTransform(); - pair z=pic.max(t); - return user ? inverse(t)*z : z; -} - -pair size(picture pic, bool user=false) -{ - transform t=pic.calculateTransform(); - pair M=pic.max(t); - pair m=pic.min(t); - if(!user) return M-m; - t=inverse(t); - return t*M-t*m; -} - -// Frame Alignment <<< -pair rectify(pair dir) -{ - real scale=max(abs(dir.x),abs(dir.y)); - if(scale != 0) dir *= 0.5/scale; - dir += (0.5,0.5); - return dir; -} - -pair point(frame f, pair dir) -{ - pair m=min(f); - pair M=max(f); - return m+realmult(rectify(dir),M-m); -} - -path[] align(path[] g, transform t=identity(), pair position, - pair align, pen p=currentpen) -{ - if(g.length == 0) return g; - pair m=min(g); - pair M=max(g); - pair dir=rectify(inverse(t)*-align); - if(basealign(p) == 1) - dir -= (0,m.y/(M.y-m.y)); - pair a=m+realmult(dir,M-m); - return shift(position+align*labelmargin(p))*t*shift(-a)*g; -} - -// Returns a transform for aligning frame f in the direction align -transform shift(frame f, pair align) -{ - return shift(align-point(f,-align)); -} - -// Returns a copy of frame f aligned in the direction align -frame align(frame f, pair align) -{ - return shift(f,align)*f; -} -// >>> - -pair point(picture pic=currentpicture, pair dir, bool user=true) -{ - pair umin = pic.userMin2(); - pair umax = pic.userMax2(); - - pair z=umin+realmult(rectify(dir),umax-umin); - return user ? z : pic.calculateTransform()*z; -} - -pair truepoint(picture pic=currentpicture, pair dir, bool user=true) -{ - transform t=pic.calculateTransform(); - pair m=pic.min(t); - pair M=pic.max(t); - pair z=m+realmult(rectify(dir),M-m); - return user ? inverse(t)*z : z; -} - -// Transform coordinate in [0,1]x[0,1] to current user coordinates. -pair relative(picture pic=currentpicture, pair z) -{ - return pic.userMin2()+realmult(z,pic.userMax2()-pic.userMin2()); -} - -void add(picture pic=currentpicture, drawer d, bool exact=false) -{ - pic.add(d,exact); -} - -typedef void drawer3(frame f, transform3 t, picture pic, projection P); -void add(picture pic=currentpicture, drawer3 d, bool exact=false) -{ - pic.add(d,exact); -} - -void add(picture pic=currentpicture, void d(picture,transform), - bool exact=false) -{ - pic.add(d,exact); -} - -void add(picture pic=currentpicture, void d(picture,transform3), - bool exact=false) -{ - pic.add(d,exact); -} - -void begingroup(picture pic=currentpicture) -{ - pic.add(new void(frame f, transform) { - begingroup(f); - },true); -} - -void endgroup(picture pic=currentpicture) -{ - pic.add(new void(frame f, transform) { - endgroup(f); - },true); -} - -void Draw(picture pic=currentpicture, path g, pen p=currentpen) -{ - pic.add(new void(frame f, transform t) { - draw(f,t*g,p); - },true); - pic.addPath(g,p); -} - -// Default arguments have been removed to increase speed. -void _draw(picture pic, path g, pen p, margin margin) -{ - if (size(nib(p)) == 0 && margin==NoMargin) { - // Inline the drawerBound wrapper for speed. - pic.addExactAbove(new void(frame f, transform t, transform T, pair, pair) { - _draw(f,t*T*g,p); - }); - } else { - pic.add(new void(frame f, transform t) { - draw(f,margin(t*g,p).g,p); - },true); - } - pic.addPath(g,p); -} - -void Draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen) -{ - // Could optimize this by adding one drawer. - for(int i=0; i < g.length; ++i) Draw(pic,g[i],p); -} - -void fill(picture pic=currentpicture, path[] g, pen p=currentpen, - bool copy=true) -{ - if(copy) - g=copy(g); - pic.add(new void(frame f, transform t) { - fill(f,t*g,p,false); - },true); - pic.addPath(g); -} - -void latticeshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[][] p, bool copy=true) -{ - if(copy) { - g=copy(g); - p=copy(p); - } - pic.add(new void(frame f, transform t) { - latticeshade(f,t*g,stroke,fillrule,p,t,false); - },true); - pic.addPath(g); -} - -void axialshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen pena, pair a, pen penb, pair b, bool copy=true) -{ - if(copy) - g=copy(g); - pic.add(new void(frame f, transform t) { - axialshade(f,t*g,stroke,pena,t*a,penb,t*b,false); - },true); - pic.addPath(g); -} - -void radialshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen pena, pair a, real ra, pen penb, pair b, real rb, - bool copy=true) -{ - if(copy) - g=copy(g); - pic.add(new void(frame f, transform t) { - pair A=t*a, B=t*b; - real RA=abs(t*(a+ra)-A); - real RB=abs(t*(b+rb)-B); - radialshade(f,t*g,stroke,pena,A,RA,penb,B,RB,false); - },true); - pic.addPath(g); -} - -void gouraudshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[] p, pair[] z, int[] edges, - bool copy=true) -{ - if(copy) { - g=copy(g); - p=copy(p); - z=copy(z); - edges=copy(edges); - } - pic.add(new void(frame f, transform t) { - gouraudshade(f,t*g,stroke,fillrule,p,t*z,edges,false); - },true); - pic.addPath(g); -} - -void gouraudshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[] p, int[] edges, bool copy=true) -{ - if(copy) { - g=copy(g); - p=copy(p); - edges=copy(edges); - } - pic.add(new void(frame f, transform t) { - gouraudshade(f,t*g,stroke,fillrule,p,edges,false); - },true); - pic.addPath(g); -} - -void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[][] p, path[] b=g, - pair[][] z=new pair[][], bool copy=true) -{ - if(copy) { - g=copy(g); - p=copy(p); - b=copy(b); - z=copy(z); - } - pic.add(new void(frame f, transform t) { - pair[][] Z=new pair[z.length][]; - for(int i=0; i < z.length; ++i) - Z[i]=t*z[i]; - tensorshade(f,t*g,stroke,fillrule,p,t*b,Z,false); - },true); - pic.addPath(g); -} - -void tensorshade(frame f, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[] p, - path b=g.length > 0 ? g[0] : nullpath) -{ - tensorshade(f,g,stroke,fillrule,new pen[][] {p},b); -} - -void tensorshade(frame f, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[] p, - path b=g.length > 0 ? g[0] : nullpath, pair[] z) -{ - tensorshade(f,g,stroke,fillrule,new pen[][] {p},b,new pair[][] {z}); -} - -void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[] p, - path b=g.length > 0 ? g[0] : nullpath) -{ - tensorshade(pic,g,stroke,fillrule,new pen[][] {p},b); -} - -void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, pen[] p, - path b=g.length > 0 ? g[0] : nullpath, pair[] z) -{ - tensorshade(pic,g,stroke,fillrule,new pen[][] {p},b,new pair[][] {z}); -} - -// Smoothly shade the regions between consecutive paths of a sequence using a -// given array of pens: -void draw(picture pic=currentpicture, path[] g, pen fillrule=currentpen, - pen[] p) -{ - path[] G; - pen[][] P; - string differentlengths="arrays have different lengths"; - if(g.length != p.length) abort(differentlengths); - for(int i=0; i < g.length-1; ++i) { - path g0=g[i]; - path g1=g[i+1]; - if(length(g0) != length(g1)) abort(differentlengths); - for(int j=0; j < length(g0); ++j) { - G.push(subpath(g0,j,j+1)--reverse(subpath(g1,j,j+1))--cycle); - P.push(new pen[] {p[i],p[i],p[i+1],p[i+1]}); - } - } - tensorshade(pic,G,fillrule,P); -} - -void functionshade(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, string shader, bool copy=true) -{ - if(copy) - g=copy(g); - pic.add(new void(frame f, transform t) { - functionshade(f,t*g,stroke,fillrule,shader); - },true); - pic.addPath(g); -} - -void filldraw(picture pic=currentpicture, path[] g, pen fillpen=currentpen, - pen drawpen=currentpen) -{ - begingroup(pic); - fill(pic,g,fillpen); - Draw(pic,g,drawpen); - endgroup(pic); -} - -void clip(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, bool copy=true) -{ - if(copy) - g=copy(g); - //pic.userClip(min(g),max(g)); - pic.clip(min(g), max(g), - new void(frame f, transform t) { - clip(f,t*g,stroke,fillrule,false); - }, - true); -} - -void beginclip(picture pic=currentpicture, path[] g, bool stroke=false, - pen fillrule=currentpen, bool copy=true) -{ - if(copy) - g=copy(g); - - pic.clipmin.push(min(g)); - pic.clipmax.push(max(g)); - - pic.add(new void(frame f, transform t) { - beginclip(f,t*g,stroke,fillrule,false); - },true); -} - -void endclip(picture pic=currentpicture) -{ - pair min,max; - if (pic.clipmin.length > 0 && pic.clipmax.length > 0) - { - min = pic.clipmin.pop(); - max = pic.clipmax.pop(); - } - else - { - // We should probably abort here, since the PostScript output will be - // garbage. - warning("endclip", "endclip without beginclip"); - min = pic.userMin2(); - max = pic.userMax2(); - } - - pic.clip(min, max, - new void(frame f, transform) { - endclip(f); - }, - true); -} - -void unfill(picture pic=currentpicture, path[] g, bool copy=true) -{ - if(copy) - g=copy(g); - pic.add(new void(frame f, transform t) { - unfill(f,t*g,false); - },true); -} - -void filloutside(picture pic=currentpicture, path[] g, pen p=currentpen, - bool copy=true) -{ - if(copy) - g=copy(g); - pic.add(new void(frame f, transform t) { - filloutside(f,t*g,p,false); - },true); - pic.addPath(g); -} - -// Use a fixed scaling to map user coordinates in box(min,max) to the -// desired picture size. -transform fixedscaling(picture pic=currentpicture, pair min, pair max, - pen p=nullpen, bool warn=false) -{ - Draw(pic,min,p+invisible); - Draw(pic,max,p+invisible); - pic.fixed=true; - return pic.fixedscaling=pic.calculateTransform(pic.xsize,pic.ysize, - pic.keepAspect); -} - -// Add frame src to frame dest about position with optional grouping. -void add(frame dest, frame src, pair position, bool group=false, - filltype filltype=NoFill, bool above=true) -{ - add(dest,shift(position)*src,group,filltype,above); -} - -// Add frame src to picture dest about position with optional grouping. -void add(picture dest=currentpicture, frame src, pair position=0, - bool group=true, filltype filltype=NoFill, bool above=true) -{ - if(is3D(src)) { - dest.add(new void(frame f, transform3, picture, projection) { - add(f,src); // always add about 3D origin (ignore position) - },true); - dest.addBox((0,0,0),(0,0,0),min3(src),max3(src)); - } else { - dest.add(new void(frame f, transform t) { - add(f,shift(t*position)*src,group,filltype,above); - },true); - dest.addBox(position,position,min(src),max(src)); - } -} - -// Like add(picture,frame,pair) but extend picture to accommodate frame. -void attach(picture dest=currentpicture, frame src, pair position=0, - bool group=true, filltype filltype=NoFill, bool above=true) -{ - transform t=dest.calculateTransform(); - add(dest,src,position,group,filltype,above); - pair s=size(dest.fit(t)); - size(dest,dest.xsize != 0 ? s.x : 0,dest.ysize != 0 ? s.y : 0); -} - -// Like add(picture,frame,pair) but align frame in direction align. -void add(picture dest=currentpicture, frame src, pair position, pair align, - bool group=true, filltype filltype=NoFill, bool above=true) -{ - add(dest,align(src,align),position,group,filltype,above); -} - -// Like add(frame,frame,pair) but align frame in direction align. -void add(frame dest, frame src, pair position, pair align, - bool group=true, filltype filltype=NoFill, bool above=true) -{ - add(dest,align(src,align),position,group,filltype,above); -} - -// Like add(picture,frame,pair,pair) but extend picture to accommodate frame; -void attach(picture dest=currentpicture, frame src, pair position, - pair align, bool group=true, filltype filltype=NoFill, - bool above=true) -{ - attach(dest,align(src,align),position,group,filltype,above); -} - -// Add a picture to another such that user coordinates in both will be scaled -// identically in the shipout. -void add(picture dest, picture src, bool group=true, filltype filltype=NoFill, - bool above=true) -{ - dest.add(src,group,filltype,above); -} - -void add(picture src, bool group=true, filltype filltype=NoFill, - bool above=true) -{ - currentpicture.add(src,group,filltype,above); -} - -// Fit the picture src using the identity transformation (so user -// coordinates and truesize coordinates agree) and add it about the point -// position to picture dest. -void add(picture dest, picture src, pair position, bool group=true, - filltype filltype=NoFill, bool above=true) -{ - add(dest,src.fit(identity()),position,group,filltype,above); -} - -void add(picture src, pair position, bool group=true, filltype filltype=NoFill, - bool above=true) -{ - add(currentpicture,src,position,group,filltype,above); -} - -// Fill a region about the user-coordinate 'origin'. -void fill(pair origin, picture pic=currentpicture, path[] g, pen p=currentpen) -{ - picture opic; - fill(opic,g,p); - add(pic,opic,origin); -} - -void postscript(picture pic=currentpicture, string s) -{ - pic.add(new void(frame f, transform) { - postscript(f,s); - },true); -} - -void postscript(picture pic=currentpicture, string s, pair min, pair max) -{ - pic.add(new void(frame f, transform t) { - postscript(f,s,t*min,t*max); - },true); -} - -void tex(picture pic=currentpicture, string s) -{ - // Force TeX string s to be evaluated immediately (in case it is a macro). - frame g; - tex(g,s); - size(g); - pic.add(new void(frame f, transform) { - tex(f,s); - },true); -} - -void tex(picture pic=currentpicture, string s, pair min, pair max) -{ - frame g; - tex(g,s); - size(g); - pic.add(new void(frame f, transform t) { - tex(f,s,t*min,t*max); - },true); -} - -void layer(picture pic=currentpicture) -{ - pic.add(new void(frame f, transform) { - layer(f); - },true); -} - -void erase(picture pic=currentpicture) -{ - pic.uptodate=false; - pic.erase(); -} - -void begin(picture pic=currentpicture, string name, string id="", - bool visible=true) -{ - if(!latex() || !pdf()) return; - settings.twice=true; - if(id == "") id=string(++ocgindex); - tex(pic,"\begin{ocg}{"+name+"}{"+id+"}{"+(visible ? "1" : "0")+"}"); - layer(pic); -} - -void end(picture pic=currentpicture) -{ - if(!latex() || !pdf()) return; - tex(pic,"\end{ocg}"); - layer(pic); -} - -// For users of the LaTeX babel package. -void deactivatequote(picture pic=currentpicture) -{ - tex(pic,"\catcode`\"=12"); -} - -void activatequote(picture pic=currentpicture) -{ - tex(pic,"\catcode`\"=13"); -} |