summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/base/plain_picture.asy
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/base/plain_picture.asy')
-rw-r--r--Build/source/utils/asymptote/base/plain_picture.asy1118
1 files changed, 371 insertions, 747 deletions
diff --git a/Build/source/utils/asymptote/base/plain_picture.asy b/Build/source/utils/asymptote/base/plain_picture.asy
index 6764d885380..5b06b7f737c 100644
--- a/Build/source/utils/asymptote/base/plain_picture.asy
+++ b/Build/source/utils/asymptote/base/plain_picture.asy
@@ -1,114 +1,15 @@
-real camerafactor=2; // Factor used for camera adjustment.
+// 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;
-typedef real[][] transform3;
-restricted transform3 identity4=identity(4);
-
-// A uniform 3D scaling.
-transform3 scale3(real s)
-{
- transform3 t=identity(4);
- t[0][0]=t[1][1]=t[2][2]=s;
- return t;
-}
-
-// Simultaneous 3D scalings in the x, y, and z directions.
-transform3 scale(real x, real y, real z)
-{
- transform3 t=identity(4);
- t[0][0]=x;
- t[1][1]=y;
- t[2][2]=z;
- return t;
-}
-
-transform3 shiftless(transform3 t)
-{
- transform3 T=copy(t);
- T[0][3]=T[1][3]=T[2][3]=0;
- return T;
-}
-
-// A coordinate in "flex space." A linear combination of user and true-size
-// coordinates.
-struct coord {
- real user,truesize;
-
- // Build a coord.
- static coord build(real user, real truesize) {
- coord c=new coord;
- c.user=user;
- c.truesize=truesize;
- return c;
- }
-
- // Deep copy of coordinate. Users may add coords to the picture, but then
- // modify the struct. To prevent this from yielding unexpected results, deep
- // copying is used.
- coord copy() {
- return build(user, truesize);
- }
-
- void clip(real min, real max) {
- user=min(max(user,min),max);
- truesize=0;
- }
-}
-
-struct coords2 {
- coord[] x,y;
- void erase() {
- x.delete();
- y.delete();
- }
- // Only a shallow copy of the individual elements of x and y
- // is needed since, once entered, they are never modified.
- coords2 copy() {
- coords2 c=new coords2;
- c.x=copy(x);
- c.y=copy(y);
- return c;
- }
- void append(coords2 c) {
- x.append(c.x);
- y.append(c.y);
- }
- void push(pair user, pair truesize) {
- x.push(coord.build(user.x,truesize.x));
- y.push(coord.build(user.y,truesize.y));
- }
- void push(coord cx, coord cy) {
- x.push(cx);
- y.push(cy);
- }
- void push(transform t, coords2 c1, coords2 c2) {
- for(int i=0; i < c1.x.length; ++i) {
- coord cx=c1.x[i], cy=c2.y[i];
- pair tinf=shiftless(t)*(0,0);
- pair z=t*(cx.user,cy.user);
- pair w=(cx.truesize,cy.truesize);
- w=length(w)*unit(shiftless(t)*w);
- coord Cx,Cy;
- Cx.user=z.x;
- Cy.user=z.y;
- Cx.truesize=w.x;
- Cy.truesize=w.y;
- push(Cx,Cy);
- }
- }
- void xclip(real min, real max) {
- for(int i=0; i < x.length; ++i)
- x[i].clip(min,max);
- }
- void yclip(real min, real max) {
- for(int i=0; i < y.length; ++i)
- y[i].clip(min,max);
- }
-}
-
struct coords3 {
coord[] x,y,z;
void erase() {
@@ -159,89 +60,7 @@ struct coords3 {
}
}
-bool operator <= (coord a, coord b)
-{
- return a.user <= b.user && a.truesize <= b.truesize;
-}
-
-bool operator >= (coord a, coord b)
-{
- return a.user >= b.user && a.truesize >= b.truesize;
-}
-
-// Find the maximal elements of the input array, using the partial ordering
-// given.
-coord[] maxcoords(coord[] in, bool operator <= (coord,coord))
-{
- // As operator <= is defined in the parameter list, it has a special
- // meaning in the body of the function.
-
- coord best;
- coord[] c;
-
- int n=in.length;
-
- if(n == 0)
- return c;
-
- int first=0;
- // Add the first coord without checking restrictions (as there are none).
- best=in[first];
- c.push(best);
-
- static int NONE=-1;
-
- int dominator(coord x)
- {
- // This assumes it has already been checked against the best.
- for(int i=1; i < c.length; ++i)
- if(x <= c[i])
- return i;
- return NONE;
- }
-
- void promote(int i)
- {
- // Swap with the top
- coord x=c[i];
- c[i]=best;
- best=c[0]=x;
- }
-
- void addmaximal(coord x)
- {
- coord[] newc;
-
- // Check if it beats any others.
- for(int i=0; i < c.length; ++i) {
- coord y=c[i];
- if(!(y <= x))
- newc.push(y);
- }
- newc.push(x);
- c=newc;
- best=c[0];
- }
-
- void add(coord x)
- {
- if(x <= best)
- return;
- else {
- int i=dominator(x);
- if(i == NONE)
- addmaximal(x);
- else
- promote(i);
- }
- }
-
- for(int i=1; i < n; ++i)
- add(in[i]);
-
- return c;
-}
-
+// scaleT and Legend <<<
typedef real scalefcn(real x);
struct scaleT {
@@ -328,233 +147,9 @@ struct Legend {
}
}
-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;
-}
-
-struct transformation {
- transform3 modelview; // For orientation and positioning
- transform3 projection; // For 3D to 2D projection
- bool infinity;
- void operator init(transform3 modelview) {
- this.modelview=modelview;
- this.projection=identity4;
- infinity=true;
- }
- void operator init(transform3 modelview, transform3 projection) {
- this.modelview=modelview;
- this.projection=projection;
- infinity=false;
- }
- transform3 compute() {
- return infinity ? modelview : projection*modelview;
- }
- transformation copy() {
- transformation T=new transformation;
- T.modelview=copy(modelview);
- T.projection=copy(projection);
- T.infinity=infinity;
- return T;
- }
-}
-
-struct projection {
- transform3 t; // projection*modelview (cached)
- bool infinity;
- bool absolute=false;
- triple camera; // Position of camera.
- triple up; // A vector that should be projected to direction (0,1).
- triple target; // Point where camera is looking at.
- triple normal; // Normal vector from target to projection plane.
- pair viewportshift; // Fractional viewport shift.
- real zoom=1; // Zoom factor.
- real angle; // Lens angle (for perspective projection).
- bool showtarget=true; // Expand bounding volume to include target?
- typedef transformation projector(triple camera, triple up, triple target);
- projector projector;
- bool autoadjust=true; // Adjust camera to lie outside bounding volume?
- bool center=false; // Center target within bounding volume?
- int ninterpolate; // Used for projecting nurbs to 2D Bezier curves.
- bool bboxonly=true; // Typeset label bounding box only.
-
- transformation T;
-
- void calculate() {
- T=projector(camera,up,target);
- t=T.compute();
- infinity=T.infinity;
- ninterpolate=infinity ? 1 : 16;
- }
-
- triple vector() {
- return camera-target;
- }
-
- void operator init(triple camera, triple up=(0,0,1), triple target=(0,0,0),
- triple normal=camera-target,
- real zoom=1, real angle=0, pair viewportshift=0,
- bool showtarget=true, bool autoadjust=true,
- bool center=false, projector projector) {
- this.camera=camera;
- this.up=up;
- this.target=target;
- this.normal=normal;
- this.zoom=zoom;
- this.angle=angle;
- this.viewportshift=viewportshift;
- this.showtarget=showtarget;
- this.autoadjust=autoadjust;
- this.center=center;
- this.projector=projector;
- calculate();
- }
-
- projection copy() {
- projection P=new projection;
- P.t=t;
- P.infinity=infinity;
- P.absolute=absolute;
- P.camera=camera;
- P.up=up;
- P.target=target;
- P.normal=normal;
- P.zoom=zoom;
- P.angle=angle;
- P.viewportshift=viewportshift;
- P.showtarget=showtarget;
- P.autoadjust=autoadjust;
- P.center=center;
- P.projector=projector;
- P.ninterpolate=ninterpolate;
- P.bboxonly=bboxonly;
- P.T=T.copy();
- return P;
- }
-
- // Return the maximum distance of box(m,M) from target.
- real distance(triple m, triple M) {
- triple[] c={m,(m.x,m.y,M.z),(m.x,M.y,m.z),(m.x,M.y,M.z),
- (M.x,m.y,m.z),(M.x,m.y,M.z),(M.x,M.y,m.z),M};
- return max(abs(c-target));
- }
-
- // Move the camera so that the box(m,M) rotated about target will always
- // lie in front of the clipping plane.
- bool adjust(triple m, triple M) {
- triple v=camera-target;
- real d=distance(m,M);
- static real lambda=camerafactor*(1-sqrtEpsilon);
- if(lambda*d >= abs(v)) {
- camera=target+camerafactor*d*unit(v);
- calculate();
- return true;
- }
- return false;
- }
-}
-
-projection currentprojection;
-
-struct light {
- real[][] diffuse;
- real[][] ambient;
- real[][] specular;
- pen background=nullpen; // Background color of the 3D canvas.
- real specularfactor;
- bool viewport; // Are the lights specified (and fixed) in the viewport frame?
- triple[] position; // Only directional lights are currently implemented.
-
- transform3 T=identity(4); // Transform to apply to normal vectors.
+// >>>
- bool on() {return position.length > 0;}
-
- void operator init(pen[] diffuse,
- pen[] ambient=array(diffuse.length,black),
- pen[] specular=diffuse, pen background=nullpen,
- real specularfactor=1,
- bool viewport=false, triple[] position) {
- int n=diffuse.length;
- assert(ambient.length == n && specular.length == n && position.length == n);
-
- this.diffuse=new real[n][];
- this.ambient=new real[n][];
- this.specular=new real[n][];
- this.background=background;
- this.position=new triple[n];
- for(int i=0; i < position.length; ++i) {
- this.diffuse[i]=rgba(diffuse[i]);
- this.ambient[i]=rgba(ambient[i]);
- this.specular[i]=rgba(specular[i]);
- this.position[i]=unit(position[i]);
- }
- this.specularfactor=specularfactor;
- this.viewport=viewport;
- }
-
- void operator init(pen diffuse=white, pen ambient=black, pen specular=diffuse,
- pen background=nullpen, real specularfactor=1,
- bool viewport=false...triple[] position) {
- int n=position.length;
- operator init(array(n,diffuse),array(n,ambient),array(n,specular),
- background,specularfactor,viewport,position);
- }
-
- void operator init(pen diffuse=white, pen ambient=black, pen specular=diffuse,
- pen background=nullpen, bool viewport=false,
- real x, real y, real z) {
- operator init(diffuse,ambient,specular,background,viewport,(x,y,z));
- }
-
- void operator init(explicit light light) {
- diffuse=copy(light.diffuse);
- ambient=copy(light.ambient);
- specular=copy(light.specular);
- background=light.background;
- specularfactor=light.specularfactor;
- viewport=light.viewport;
- position=copy(light.position);
- }
-
- real[] background() {return rgba(background == nullpen ? white : background);}
-}
-
-light currentlight;
+// Frame Alignment was here
triple min3(pen p)
{
@@ -571,9 +166,34 @@ triple max3(pen p)
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);
-struct picture {
+// 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,
@@ -585,39 +205,6 @@ struct picture {
bool uptodate=true;
- // The coordinates in flex space to be used in sizing the picture.
- struct bounds {
- coords2 point,min,max;
- bool exact=true; // An accurate picture bounds is provided by the user.
- void erase() {
- point.erase();
- min.erase();
- max.erase();
- }
- bounds copy() {
- bounds b=new bounds;
- b.point=point.copy();
- b.min=min.copy();
- b.max=max.copy();
- b.exact=exact;
- return b;
- }
- void xclip(real Min, real Max) {
- point.xclip(Min,Max);
- min.xclip(Min,Max);
- max.xclip(Min,Max);
- }
- void yclip(real Min, real Max) {
- point.yclip(Min,Max);
- min.yclip(Min,Max);
- max.yclip(Min,Max);
- }
- void clip(triple Min, triple Max) {
- xclip(Min.x,Max.x);
- yclip(Min.y,Max.y);
- }
- }
-
struct bounds3 {
coords3 point,min,max;
bool exact=true; // An accurate picture bounds is provided by the user.
@@ -639,14 +226,15 @@ struct picture {
bounds bounds;
bounds3 bounds3;
+ // Other Fields <<<2
// Transform to be applied to this picture.
transform T;
transform3 T3;
- // Cached user-space bounding box
- triple userMin,userMax;
- bool userSetx,userSety,userSetz;
-
+ // The internal representation of the 3D user bounds.
+ private pairOrTriple umin, umax;
+ private bool usetx, usety, usetz;
+
ScaleT scale; // Needed by graph
Legend[] legend;
@@ -669,9 +257,11 @@ struct picture {
bool fixed;
transform fixedscaling;
+ // Init and erase <<<2
void init() {
- userMin=userMax=(0,0,0);
- userSetx=userSety=userSetz=false;
+ umin.init();
+ umax.init();
+ usetx=usety=usetz=false;
T3=identity(4);
}
init();
@@ -688,6 +278,7 @@ struct picture {
init();
}
+ // Empty <<<2
bool empty2() {
return nodes.length == 0;
}
@@ -700,133 +291,175 @@ struct picture {
return empty2() && empty3();
}
- pair userMin() {return (userMin.x,userMin.y);}
- pair userMax() {return (userMax.x,userMax.y);}
+ // User min/max <<<2
+ pair userMin2() {return bounds.userMin(); }
+ pair userMax2() {return bounds.userMax(); }
- void userMinx(real x) {
- userMin=(x,userMin.y,userMin.z);
- userSetx=true;
+ 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;
}
-
- void userMiny(real y) {
- userMin=(userMin.x,y,userMin.z);
- userSety=true;
+ 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;
}
-
- void userMinz(real z) {
- userMin=(userMin.x,userMin.y,z);
- userSetz=true;
+
+ // The combination of 2D and 3D data.
+ pairOrTriple userMin() {
+ return userExtreme(userMin2, userMin3, min);
}
-
- void userMaxx(real x) {
- userMax=(x,userMax.y,userMax.z);
- userSetx=true;
+ 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 userMaxy(real y) {
- userMax=(userMax.x,y,userMax.z);
- userSety=true;
+ void userMiny3(real y) {
+ umin.y=y;
+ usety=true;
}
- void userMaxz(real z) {
- userMax=(userMax.x,userMax.y,z);
- userSetz=true;
+ void userMinz3(real z) {
+ umin.z=z;
+ usetz=true;
}
- void userCorners(pair c00, pair c01, pair c10, pair c11) {
- userMin=(min(c00.x,c01.x,c10.x,c11.x),min(c00.y,c01.y,c10.y,c11.y),
- userMin.z);
- userMax=(max(c00.x,c01.x,c10.x,c11.x),max(c00.y,c01.y,c10.y,c11.y),
- userMax.z);
+ void userMaxx3(real x) {
+ umax.x=x;
+ usetx=true;
}
- void userCorners(triple c000, triple c001, triple c010, triple c011,
- triple c100, triple c101, triple c110, triple c111) {
- userMin=(min(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x),
- min(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y),
- min(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z));
- userMax=(max(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x),
- max(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y),
- max(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z));
+ void userMaxy3(real y) {
+ umax.y=y;
+ usety=true;
}
- void userCopy(picture pic) {
- userMin=(triple) pic.userMin;
- userMax=(triple) pic.userMax;
- userSetx=pic.userSetx;
- userSety=pic.userSety;
- userSetz=pic.userSetz;
+ 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);
}
- typedef real binop(real, real);
-
// Cache the current user-space bounding box x coodinates
- void userBoxX(real min, real max, binop m=min, binop M=max) {
- if(userSetx) {
- userMin=(m(userMin.x,min),userMin.y,userMin.z);
- userMax=(M(userMax.x,max),userMax.y,userMax.z);
+ 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 {
- userMin=(min,userMin.y,userMin.z);
- userMax=(max,userMax.y,userMax.z);
- userSetx=true;
+ umin.x=min;
+ umax.x=max;
+ usetx=true;
}
}
-
+
// Cache the current user-space bounding box y coodinates
- void userBoxY(real min, real max, binop m=min, binop M=max) {
- if(userSety) {
- userMin=(userMin.x,m(userMin.y,min),userMin.z);
- userMax=(userMax.x,M(userMax.y,max),userMax.z);
+ 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 {
- userMin=(userMin.x,min,userMin.z);
- userMax=(userMax.x,max,userMax.z);
- userSety=true;
+ umin.y=min;
+ umax.y=max;
+ usety=true;
}
}
-
+
// Cache the current user-space bounding box z coodinates
- void userBoxZ(real min, real max, binop m=min, binop M=max) {
- if(userSetz) {
- userMin=(userMin.x,userMin.y,m(userMin.z,min));
- userMax=(userMax.x,userMax.y,M(userMax.z,max));
+ 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 {
- userMin=(userMin.x,userMin.y,min);
- userMax=(userMax.x,userMax.y,max);
- userSetz=true;
+ umin.z=min;
+ umax.z=max;
+ usetz=true;
}
}
-
- // Cache the current user-space bounding box
- void userBox(pair min, pair max) {
- userBoxX(min.x,max.x);
- userBoxY(min.y,max.y);
- }
-
+
// Cache the current user-space bounding box
- void userBox(triple min, triple max) {
- userBoxX(min.x,max.x);
- userBoxY(min.y,max.y);
- userBoxZ(min.z,max.z);
+ void userBox3(triple min, triple max) {
+ userBoxX3(min.x,max.x);
+ userBoxY3(min.y,max.y);
+ userBoxZ3(min.z,max.z);
}
- // Clip the current user-space bounding box
- void userClip(pair min, pair max) {
- userBoxX(min.x,max.x,max,min);
- userBoxY(min.y,max.y,max,min);
+ // 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);
}
- void add(drawerBound d, bool exact=false) {
+ // Faster implementation of most common case.
+ void addExactAbove(drawerBound d) {
uptodate=false;
- if(!exact) bounds.exact=false;
nodes.push(d);
}
void add(drawer d, bool exact=false, bool above=true) {
- uptodate=false;
- if(!exact) bounds.exact=false;
- nodes.push(new void(frame f, transform t, transform T, pair, pair) {
+ 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) {
@@ -845,20 +478,22 @@ struct picture {
},exact,above);
}
- void clip(drawer d, bool exact=false) {
- bounds.clip(userMin,userMax);
+ // Clip <<<2
+ void clip(pair min, pair max, drawer d, bool exact=false) {
+ bounds.clip(min, max);
this.add(d,exact);
}
- void clip(drawerBound d, bool exact=false) {
- bounds.clip(userMin,userMax);
+ 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.point.push(user,truesize);
- userBox(user,user);
+ bounds.addPoint(user,truesize);
+ //userBox(user,user);
}
// Add a point to the sizing, accounting also for the size of the pen.
@@ -869,7 +504,7 @@ struct picture {
void addPoint(triple user, triple truesize=(0,0,0)) {
bounds3.point.push(user,truesize);
- userBox(user,user);
+ userBox3(user,user);
}
void addPoint(triple user, triple truesize=(0,0,0), pen p) {
@@ -879,34 +514,21 @@ struct picture {
// Add a box to the sizing.
void addBox(pair userMin, pair userMax, pair trueMin=0, pair trueMax=0) {
- bounds.min.push(userMin,trueMin);
- bounds.max.push(userMax,trueMax);
- userBox(userMin,userMax);
+ 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);
- userBox(userMin,userMax);
- }
-
- // Add a (user space) path to the sizing.
- void addPath(path g) {
- if(size(g) > 0)
- addBox(min(g),max(g));
- }
- void addPath(path[] g) {
- for(int i=0; i < g.length; ++i)
- addPath(g[i]);
+ userBox3(userMin,userMax);
}
- // Add a path to the sizing with the additional padding of a pen.
- void addPath(path g, pen p) {
- if(size(g) > 0)
- addBox(min(g),max(g),min(p),max(p));
- }
+ // 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;
@@ -929,62 +551,15 @@ struct picture {
zunitsize=z;
}
- // The scaling in one dimension: x --> a*x + b
- struct scaling {
- real a,b;
- static scaling build(real a, real b) {
- scaling s=new scaling;
- s.a=a; s.b=b;
- return s;
- }
- real scale(real x) {
- return a*x+b;
- }
- real scale(coord c) {
- return scale(c.user) + c.truesize;
- }
- }
-
- // Calculate the minimum point in scaling the coords.
- real min(real m, scaling s, coord[] c) {
- for(int i=0; i < c.length; ++i)
- if(s.scale(c[i]) < m)
- m=s.scale(c[i]);
- return m;
- }
-
- // Calculate the maximum point in scaling the coords.
- real max(real M, scaling s, coord[] c) {
- for(int i=0; i < c.length; ++i)
- if(s.scale(c[i]) > M)
- M=s.scale(c[i]);
- return M;
- }
-
+ // min/max of picture <<<2
// Calculate the min for the final frame, given the coordinate transform.
pair min(transform t) {
- if(bounds.min.x.length == 0 && bounds.point.x.length == 0 &&
- bounds.max.x.length == 0) return 0;
- pair a=t*(1,1)-t*(0,0), b=t*(0,0);
- scaling xs=scaling.build(a.x,b.x);
- scaling ys=scaling.build(a.y,b.y);
- return (min(min(min(infinity,xs,bounds.point.x),xs,bounds.min.x),
- xs,bounds.max.x),
- min(min(min(infinity,ys,bounds.point.y),ys,bounds.min.y),
- ys,bounds.max.y));
+ return bounds.min(t);
}
// Calculate the max for the final frame, given the coordinate transform.
pair max(transform t) {
- if(bounds.min.x.length == 0 && bounds.point.x.length == 0 &&
- bounds.max.x.length == 0) return 0;
- pair a=t*(1,1)-t*(0,0), b=t*(0,0);
- scaling xs=scaling.build(a.x,b.x);
- scaling ys=scaling.build(a.y,b.y);
- return (max(max(max(-infinity,xs,bounds.point.x),xs,bounds.min.x),
- xs,bounds.max.x),
- max(max(max(-infinity,ys,bounds.point.y),ys,bounds.min.y),
- ys,bounds.max.y));
+ return bounds.max(t);
}
// Calculate the min for the final frame, given the coordinate transform.
@@ -1019,66 +594,6 @@ struct picture {
zs,bounds3.max.z));
}
- // Calculate the sizing constants for the given array and maximum size.
- real calculateScaling(string dir, coord[] coords, real size,
- bool warn=true) {
- access simplex;
- simplex.problem p=new simplex.problem;
-
- void addMinCoord(coord c) {
- // (a*user + b) + truesize >= 0:
- p.addRestriction(c.user,1,c.truesize);
- }
- void addMaxCoord(coord c) {
- // (a*user + b) + truesize <= size:
- p.addRestriction(-c.user,-1,size-c.truesize);
- }
-
- coord[] m=maxcoords(coords,operator >=);
- coord[] M=maxcoords(coords,operator <=);
-
- for(int i=0; i < m.length; ++i)
- addMinCoord(m[i]);
- for(int i=0; i < M.length; ++i)
- addMaxCoord(M[i]);
-
- int status=p.optimize();
- if(status == simplex.problem.OPTIMAL) {
- return scaling.build(p.a(),p.b()).a;
- } else if(status == simplex.problem.UNBOUNDED) {
- if(warn) warning("unbounded",dir+" scaling in picture unbounded");
- return 0;
- } else {
- if(!warn) return 1;
- bool userzero=true;
- for(int i=0; i < coords.length; ++i) {
- if(coords[i].user != 0) userzero=false;
- }
- if(userzero) return 1;
- warning("cannotfit","cannot fit picture to "+dir+"size "+(string) size
- +"...enlarging...");
- return calculateScaling(dir,coords,sqrt(2)*size,warn);
- }
- }
-
- void append(coords2 point, coords2 min, coords2 max, transform t,
- bounds bounds)
- {
- // Add the coord info to this picture.
- if(t == identity()) {
- point.append(bounds.point);
- min.append(bounds.min);
- max.append(bounds.max);
- } else {
- point.push(t,bounds.point,bounds.point);
- // Add in all 4 corner points, to properly size rectangular pictures.
- point.push(t,bounds.min,bounds.min);
- point.push(t,bounds.min,bounds.max);
- point.push(t,bounds.max,bounds.min);
- point.push(t,bounds.max,bounds.max);
- }
- }
-
void append(coords3 point, coords3 min, coords3 max, transform3 t,
bounds3 bounds)
{
@@ -1101,37 +616,13 @@ struct picture {
}
}
+ // 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) {
- if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0)
- return identity();
-
- coords2 Coords;
-
- append(Coords,Coords,Coords,T,bounds);
-
- 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;
-
- if(sx == 0) {
- sx=sy;
- if(sx == 0)
- return identity();
- } else if(sy == 0) sy=sx;
+ bounds b = (T == identity()) ? this.bounds : T * this.bounds;
-
- if(keepAspect && (xunitsize == 0 || yunitsize == 0))
- return scale(min(sx,sy));
- else
- return scale(sx,sy);
+ return b.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn);
}
transform scaling(bool warn=true) {
@@ -1184,7 +675,8 @@ struct picture {
frame fit(transform t, transform T0=T, pair m, pair M) {
frame f;
- for(int i=0; i < nodes.length; ++i)
+ int n = nodes.length;
+ for(int i=0; i < n; ++i)
nodes[i](f,t,T0,m,M);
return f;
}
@@ -1207,6 +699,7 @@ struct picture {
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;
@@ -1233,6 +726,7 @@ struct picture {
},exact,above);
}
+ // More scaling <<<2
frame scaled() {
frame f=fit(fixedscaling);
pair d=size(f);
@@ -1289,6 +783,7 @@ struct picture {
} 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) {
@@ -1309,6 +804,8 @@ struct picture {
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));
@@ -1331,11 +828,14 @@ struct picture {
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;
+ if(empty2())
+ return newframe;
+
transform t=scaling(xsize,ysize,keepAspect);
frame f=fit(t);
transform s=scale(f,xsize,ysize,keepAspect);
@@ -1378,15 +878,42 @@ struct picture {
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.nodes = copy(nodes);
dest.nodes3=copy(nodes3);
dest.T=T;
dest.T3=T3;
- dest.userCopy(this);
+
+ // 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);
@@ -1412,6 +939,37 @@ struct picture {
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,
@@ -1440,42 +998,38 @@ struct picture {
legend.append(src.legend);
- if(src.userSetx) userBoxX(src.userMin.x,src.userMax.x);
- if(src.userSety) userBoxY(src.userMin.y,src.userMax.y);
- if(src.userSetz) userBoxZ(src.userMin.z,src.userMax.z);
+ 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);
- append(bounds.point,bounds.min,bounds.max,srcCopy.T,src.bounds);
+ 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.bounds.exact) bounds.exact=false;
if(!src.bounds3.exact) bounds3.exact=false;
}
}
+// Post Struct <<<1
picture operator * (transform t, picture orig)
{
- picture pic=orig.copy();
- pic.T=t*pic.T;
- pic.userCorners(t*(pic.userMin.x,pic.userMin.y),
- t*(pic.userMin.x,pic.userMax.y),
- t*(pic.userMax.x,pic.userMin.y),
- t*(pic.userMax.x,pic.userMax.y));
- pic.bounds.exact=false;
- return pic;
+ return orig.transformed(t);
}
picture operator * (transform3 t, picture orig)
{
picture pic=orig.copy();
pic.T3=t*pic.T3;
- pic.userCorners(t*pic.userMin,
- t*(pic.userMin.x,pic.userMin.y,pic.userMax.z),
- t*(pic.userMin.x,pic.userMax.y,pic.userMin.z),
- t*(pic.userMin.x,pic.userMax.y,pic.userMax.z),
- t*(pic.userMax.x,pic.userMin.y,pic.userMin.z),
- t*(pic.userMax.x,pic.userMin.y,pic.userMax.z),
- t*(pic.userMax.x,pic.userMax.y,pic.userMin.z),
- t*pic.userMax);
+ 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;
}
@@ -1538,9 +1092,54 @@ pair size(picture pic, bool user=false)
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 z=pic.userMin()+realmult(rectify(dir),pic.userMax()-pic.userMin());
+ pair umin = pic.userMin2();
+ pair umax = pic.userMax2();
+
+ pair z=umin+realmult(rectify(dir),umax-umin);
return user ? z : pic.calculateTransform()*z;
}
@@ -1556,7 +1155,7 @@ pair truepoint(picture pic=currentpicture, pair dir, bool user=true)
// Transform coordinate in [0,1]x[0,1] to current user coordinates.
pair relative(picture pic=currentpicture, pair z)
{
- return pic.userMin()+realmult(z,pic.userMax()-pic.userMin());
+ return pic.userMin2()+realmult(z,pic.userMax2()-pic.userMin2());
}
void add(picture pic=currentpicture, drawer d, bool exact=false)
@@ -1604,17 +1203,25 @@ void Draw(picture pic=currentpicture, path g, pen p=currentpen)
pic.addPath(g,p);
}
-void _draw(picture pic=currentpicture, path g, pen p=currentpen,
- margin margin=NoMargin)
+// Default arguments have been removed to increase speed.
+void _draw(picture pic, path g, pen p, margin margin)
{
- pic.add(new void(frame f, transform t) {
- draw(f,margin(t*g,p).g,p);
- },true);
+ 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);
}
@@ -1791,10 +1398,12 @@ void clip(picture pic=currentpicture, path[] g, bool stroke=false,
{
if(copy)
g=copy(g);
- pic.userClip(min(g),max(g));
- pic.clip(new void(frame f, transform t) {
- clip(f,t*g,stroke,fillrule,false);
- },true);
+ //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,
@@ -1813,11 +1422,26 @@ void beginclip(picture pic=currentpicture, path[] g, bool stroke=false,
void endclip(picture pic=currentpicture)
{
- if(pic.clipmin.length > 0 && pic.clipmax.length > 0)
- pic.userClip(pic.clipmin.pop(),pic.clipmax.pop());
- pic.clip(new void(frame f, transform) {
- endclip(f);
- },true);
+ 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)