diff options
Diffstat (limited to 'Build/source/utils/asymptote/base')
25 files changed, 2224 insertions, 1353 deletions
diff --git a/Build/source/utils/asymptote/base/bsp.asy b/Build/source/utils/asymptote/base/bsp.asy index a744da522cc..2a4d93daa81 100644 --- a/Build/source/utils/asymptote/base/bsp.asy +++ b/Build/source/utils/asymptote/base/bsp.asy @@ -201,7 +201,9 @@ void add(picture pic=currentpicture, face[] faces, for(int i=0; i < n; ++i) { picture F=Faces[i].pic; - pic.userBox(F.userMin,F.userMax); - pic.append(pic.bounds.point,pic.bounds.min,pic.bounds.max,F.T,F.bounds); + pic.userBox3(F.userMin3(), F.userMax3()); + pic.bounds.append(F.T, F.bounds); + // The above 2 lines should be replaced with a routine in picture which + // copies only sizing data from another picture. } } diff --git a/Build/source/utils/asymptote/base/embed.asy b/Build/source/utils/asymptote/base/embed.asy index dc6c7d4101a..165de0b7534 100644 --- a/Build/source/utils/asymptote/base/embed.asy +++ b/Build/source/utils/asymptote/base/embed.asy @@ -6,7 +6,9 @@ if(latex()) { \usepackage[3D,dvipdfmx]{movie15} \else% \usepackage[3D]{movie15} -\fi%"); +\fi% +\FPmessagesfalse% +"); } // See http://www.ctan.org/tex-archive/macros/latex/contrib/movie15/README diff --git a/Build/source/utils/asymptote/base/flowchart.asy b/Build/source/utils/asymptote/base/flowchart.asy index bb8fe21a4d6..3db66173755 100644 --- a/Build/source/utils/asymptote/base/flowchart.asy +++ b/Build/source/utils/asymptote/base/flowchart.asy @@ -424,20 +424,20 @@ blockconnector blockconnector(picture pic, transform t, pen p=currentpen, if(abs(b1.center.y-b2.center.y) < sqrtEpsilon) { // horizontally aligned b1.dirs[0]=b1.center.x < b2.center.x ? right : left; - blockconnector(pic,t)(b1,b2); + blockconnector(pic,t,p,margin)(b1,b2); } else if(abs(b1.center.x-b2.center.x) < sqrtEpsilon) { // vertically aligned b1.dirs[0]=b1.center.y < b2.center.y ? up : down; - blockconnector(pic,t)(b1,b2); + blockconnector(pic,t,p,margin)(b1,b2); } else { if(abs(b1.center.y-b2.center.y) < abs(b1.center.x-b2.center.x)) { b1.dirs[0]=b1.center.x < b2.center.x ? right : left; b1.dirs[1]=b1.center.y < b2.center.y ? up : down; - blockconnector(pic,t)(b1,b2); + blockconnector(pic,t,p,margin)(b1,b2); } else { b1.dirs[0]=b1.center.y < b2.center.y ? up : down; b1.dirs[1]=b1.center.x < b2.center.x ? right : left; - blockconnector(pic,t)(b1,b2); + blockconnector(pic,t,p,margin)(b1,b2); } } return b2; diff --git a/Build/source/utils/asymptote/base/geometry.asy b/Build/source/utils/asymptote/base/geometry.asy index 41a6397de64..61ff1ba8e8f 100644 --- a/Build/source/utils/asymptote/base/geometry.asy +++ b/Build/source/utils/asymptote/base/geometry.asy @@ -433,10 +433,6 @@ point point(explicit pair p, real m) op.init(currentcoordsys, p, m); return op; } -point point(explicit pair p, int m) -{// Handle ambiguity - return point(p,(real)m); -} /*<asyxml><function type="point" signature="point(coordsys,explicit point,real)"><code></asyxml>*/ point point(coordsys R, explicit point M, real m=M.m) @@ -1150,17 +1146,18 @@ private void Drawline(picture pic=currentpicture, Label L="",pair P, bool dirP=t Label legend="", marker marker=nomarker, pathModifier pathModifier=NoModifier) {/* Add the two parameters 'dirP' and 'dirQ' to the native routine - 'drawline' of the module 'maths'. + 'drawline' of the module 'math'. Segment [PQ] will be prolonged in direction of P if 'dirP=true', in direction of Q if 'dirQ=true'. If 'dirP=dirQ=true', the behavior is that of the native 'drawline'. Add all the other parameters of 'Draw'.*/ - pic.add(new void (frame f, transform t, transform, pair m, pair M) { + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { picture opic; // Reduce the bounds by the size of the pen. m -= min(p)-(linemargin(),linemargin()); M -= max(p)+(linemargin(),linemargin()); // Calculate the points and direction vector in the transformed space. + t=t*T; pair z=t*P; pair q=t*Q; pair v=q-z; @@ -1175,16 +1172,16 @@ private void Drawline(picture pic=currentpicture, Label L="",pair P, bool dirP=t ptp=(z.x,z.y+cp*(m.y-z.y)); ptq=(z.x,q.y+cq*(M.y-q.y)); } else { - ptp=(z.x,q.y+cq*(m.y-q.y)); - ptq=(z.x,z.y+cp*(M.y-z.y)); + ptq=(z.x,q.y+cq*(m.y-q.y)); + ptp=(z.x,z.y+cp*(M.y-z.y)); } } else if(v.y == 0) { if (dot(v,m-z) < 0) { ptp=(z.x+cp*(m.x-z.x),z.y); ptq=(q.x+cq*(M.x-q.x),z.y); } else { - ptp=(q.x+cq*(m.x-q.x),z.y); - ptq=(z.x+cp*(M.x-z.x),z.y); + ptq=(q.x+cq*(m.x-q.x),z.y); + ptp=(z.x+cp*(M.x-z.x),z.y); } } else { // Calculate the maximum and minimum t values allowed for the @@ -1239,12 +1236,12 @@ void clipdraw(picture pic=currentpicture, Label L="", path g, label(tmp,L,g,p); add(pic,tmp); } - pic.add(new void (frame f, transform t, transform, pair m, pair M) { + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { // Reduce the bounds by the size of the pen and the margins. m += min(p)+(xmargin,ymargin); M -= max(p)+(xmargin,ymargin); path bound=box(m,M); picture tmp; - draw(tmp,"",t*g,align,p,arrow,bar,NoMargin,legend,marker); + draw(tmp,"",t*T*g,align,p,arrow,bar,NoMargin,legend,marker); clip(tmp,bound); add(f,tmp.fit()); }); @@ -2963,13 +2960,6 @@ ellipse ellipse(point C, real a, real b, real angle=0) return oe; } -/*<asyxml><function type="ellipse" signature="ellipse(explicit pair,real,real)"><code></asyxml>*/ -ellipse ellipse(explicit pair C, real a, real b)= - new ellipse(explicit pair C, real a, real b) -{/*<asyxml></code><documentation>Overwrite the default routine.</documentation></function></asyxml>*/ - return ellipse((point)C,a,b,0); -}; - /*<asyxml><function type="ellipse" signature="ellipse(bqe)"><code></asyxml>*/ ellipse ellipse(bqe bqe) {/*<asyxml></code><documentation>Return the ellipse a[0]*x^2 + a[1]*xy + a[2]*y^2 + a[3]*x + a[4]*y + a[5]=0 @@ -3204,16 +3194,6 @@ circle circle(explicit point C, real r) if(!finite(r)) oc.l=line(C,C+vector(C.coordsys,(1,0))); return oc; } -circle circle(explicit point C, int r) -{ - return circle(C,(real) r); -} - -/*<asyxml><function type="circle" signature="circle(pair,real)"><code></asyxml>*/ -circle circle(pair c, real r)=new circle(pair c, real r) -{/*<asyxml></code><documentation>Overwrite 'circle(pair,real)'</documentation></function></asyxml>*/ - return circle(locate(c),r); -}; /*<asyxml><function type="circle" signature="circle(point,point)"><code></asyxml>*/ circle circle(point A, point B) @@ -3526,18 +3506,30 @@ path arcfromcenter(ellipse el, real angle1, real angle2, The angles are mesured relatively to the axis (C,x-axis) where C is the center of the ellipse.</documentation></function></asyxml>*/ if(degenerate(el)) abort("arcfromcenter: can not convert degenerated ellipse to path."); - guide op; - coordsys Rp=coordsys(el); - if (n < 1) return op; if (angle1 > angle2) return reverse(arcfromcenter(el,angle2,angle1,!direction,n)); + path op; + coordsys Rp=coordsys(el); + if (n < 1) return op; + interpolate join = operator ..; + real stretch = max(el.a/el.b, el.b/el.a); + if (stretch > 10) { + n *= floor(stretch/5); + join=operator --; + } real a1=direction ? radians(angle1) : radians(angle2); real a2=direction ? radians(angle2) : radians(angle1)+2*pi; real step=(a2-a1)/(n != 1 ? n-1 : 1); real a,r; real da=radians(el.angle); + real a3=angle((cos(a1)/el.a,sin(a1)/el.b)); + real a3=(a3>=0) ? a3 : a3+2pi; + real a4=angle((cos(a2)/el.a,sin(a2)/el.b)); + real a4=(a4>=0) ? a4 : a4+2pi; + real step=(a4-a3)/(n != 1 ? n-1 : 1); for (int i=0; i < n; ++i) { - a=a1+i*step; + a=a3+i*step; + a=angle((el.a*cos(a),el.b*sin(a))); r=el.b/sqrt(1-(el.e*cos(a))^2); op=op..Rp*Rp.polar(r,a+da); } @@ -3782,16 +3774,17 @@ void draw(picture pic=currentpicture, Label L="",parabola parabola, margin margin=NoMargin, Label legend="", marker marker=nomarker) {/*<asyxml></code><documentation>Draw the parabola 'p' on 'pic' without (if possible) altering the size of picture pic.</documentation></function></asyxml>*/ - pic.add(new void (frame f, transform t, transform, pair m, pair M) { + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { // Reduce the bounds by the size of the pen and the margins. m -= min(p); M -= max(p); parabola.bmin=inverse(t)*m; parabola.bmax=inverse(t)*M; picture tmp; - draw(tmp,L,t*(path) parabola,align,p,arrow,bar,NoMargin,legend,marker); + draw(tmp,L,t*T*(path) parabola,align,p,arrow,bar,NoMargin,legend,marker); add(f,tmp.fit()); }); - if(pic.userMin.x != pic.userMax.x & pic.userMin.y != pic.userMax.y & - !finite(abs(pic.userMin)) & !finite(abs(pic.userMax))) + pair m=pic.userMin(); + pair M=pic.userMax(); + if(m != M) pic.addBox(truepoint(SW), truepoint(NE)); } @@ -3814,19 +3807,21 @@ void draw(picture pic=currentpicture, Label L="", hyperbola h, margin margin=NoMargin, Label legend="", marker marker=nomarker) {/*<asyxml></code><documentation>Draw the hyperbola 'h' on 'pic' without (if possible) altering the size of the picture pic.</documentation></function></asyxml>*/ - pic.add(new void (frame f, transform t, transform, pair m, pair M) { + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { // Reduce the bounds by the size of the pen and the margins. m -= min(p); M -= max(p); h.bmin=inverse(t)*m; h.bmax=inverse(t)*M; picture tmp; - draw(tmp,L,t*(path) h,align,p,arrow,bar,NoMargin,legend,marker); + transform tT=t*T; + draw(tmp,L,tT*(path) h,align,p,arrow,bar,NoMargin,legend,marker); hyperbola ht=hyperbola(h.F2,h.F1,h.a); ht.bmin=inverse(t)*m; ht.bmax=inverse(t)*M; - draw(tmp,"",t*(path) ht,align,p,arrow,bar,NoMargin,marker); + draw(tmp,"",tT*(path) ht,align,p,arrow,bar,NoMargin,marker); add(f,tmp.fit()); }); - if(pic.userMin.x != pic.userMax.x & pic.userMin.y != pic.userMax.y & - !finite(abs(pic.userMin)) & !finite(abs(pic.userMax))) + pair m=pic.userMin(); + pair M=pic.userMax(); + if(m != M) pic.addBox(truepoint(SW), truepoint(NE)); } @@ -4158,10 +4153,6 @@ abscissa operator +(int x, explicit abscissa a) { return ((real)x)+a; } -abscissa operator +(explicit abscissa a, int x) -{ - return ((real)x)+a; -} /*<asyxml><operator type="abscissa" signature="-(explicit abscissa a)"><code></asyxml>*/ abscissa operator -(explicit abscissa a) @@ -4190,10 +4181,6 @@ abscissa operator -(int x, explicit abscissa a) { return ((real)x)-a; } -abscissa operator -(explicit abscissa a, int x) -{ - return a-((real)x); -} /*<asyxml><operator type="abscissa" signature="*(real,abscissa)"><code></asyxml>*/ abscissa operator *(real x, explicit abscissa a) @@ -4210,15 +4197,6 @@ abscissa operator *(explicit abscissa a, real x) return x*a; } -abscissa operator *(int x, explicit abscissa a) -{ - return ((real)x)*a; -} -abscissa operator *(explicit abscissa a, int x) -{ - return ((real)x)*a; -} - abscissa operator /(real x, explicit abscissa a) { abscissa oa; @@ -4238,10 +4216,6 @@ abscissa operator /(int x, explicit abscissa a) { return ((real)x)/a; } -abscissa operator /(explicit abscissa a, int x) -{ - return a/((real)x); -} /*<asyxml><function type="abscissa" signature="relabscissa(real)"><code></asyxml>*/ abscissa relabscissa(real x) @@ -4832,6 +4806,10 @@ struct arc { /*<asyxml><method type="void" signature="setangles(real,real,real)"><code></asyxml>*/ void setangles(real a0, real a1, real a2) {/*<asyxml></code><documentation>Set the angles.</documentation></method></asyxml>*/ + if (a1 < 0 && a2 < 0) { + a1 += 360; + a2 += 360; + } this.angle0=a0%(sgnd(a0)*360); this.angle1=a1%(sgnd(a1)*360); this.angle2=a2%(sgnd(2)*360); diff --git a/Build/source/utils/asymptote/base/graph.asy b/Build/source/utils/asymptote/base/graph.asy index 9ac9983ddef..a7ffabc6220 100644 --- a/Build/source/utils/asymptote/base/graph.asy +++ b/Build/source/utils/asymptote/base/graph.asy @@ -1203,17 +1203,17 @@ void xaxisAt(picture pic=currentpicture, Label L="", axis axis, void bounds() { if(type == Min) - y=pic.scale.y.automin() ? tickMin(pic).y : pic.userMin.y; + y=pic.scale.y.automin() ? tickMin(pic).y : pic.userMin().y; else if(type == Max) - y=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax.y; + y=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax().y; else if(type == Both) { - y2=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax.y; + y2=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax().y; y=opposite ? y2 : - (pic.scale.y.automin() ? tickMin(pic).y : pic.userMin.y); + (pic.scale.y.automin() ? tickMin(pic).y : pic.userMin().y); } - real Xmin=finite(xmin) ? xmin : pic.userMin.x; - real Xmax=finite(xmax) ? xmax : pic.userMax.x; + real Xmin=finite(xmin) ? xmin : pic.userMin().x; + real Xmax=finite(xmax) ? xmax : pic.userMax().x; pair a=(Xmin,y); pair b=(Xmax,y); @@ -1297,17 +1297,17 @@ void yaxisAt(picture pic=currentpicture, Label L="", axis axis, void bounds() { if(type == Min) - x=pic.scale.x.automin() ? tickMin(pic).x : pic.userMin.x; + x=pic.scale.x.automin() ? tickMin(pic).x : pic.userMin().x; else if(type == Max) - x=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax.x; + x=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax().x; else if(type == Both) { - x2=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax.x; + x2=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax().x; x=opposite ? x2 : - (pic.scale.x.automin() ? tickMin(pic).x : pic.userMin.x); + (pic.scale.x.automin() ? tickMin(pic).x : pic.userMin().x); } - real Ymin=finite(ymin) ? ymin : pic.userMin.y; - real Ymax=finite(ymax) ? ymax : pic.userMax.y; + real Ymin=finite(ymin) ? ymin : pic.userMin().y; + real Ymax=finite(ymax) ? ymax : pic.userMax().y; pair a=(x,Ymin); pair b=(x,Ymax); @@ -1363,21 +1363,22 @@ void xlimits(picture pic=currentpicture, real min=-infinity, real max=infinity, bounds mx; if(pic.scale.x.automin() || pic.scale.x.automax()) - mx=autoscale(pic.userMin.x,pic.userMax.x,pic.scale.x.scale); + mx=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale); if(pic.scale.x.automin) { if(pic.scale.x.automin()) pic.userMinx(mx.min); - } else pic.userMinx(pic.scale.x.T(min)); + } else pic.userMinx(min(pic.scale.x.T(min),pic.scale.x.T(max))); if(pic.scale.x.automax) { if(pic.scale.x.automax()) pic.userMaxx(mx.max); - } else pic.userMaxx(pic.scale.x.T(max)); + } else pic.userMaxx(max(pic.scale.x.T(min),pic.scale.x.T(max))); if(crop) { pair userMin=pic.userMin(); pair userMax=pic.userMax(); pic.bounds.xclip(userMin.x,userMax.x); - pic.clip(new void (frame f, transform t, transform T, pair, pair) { + pic.clip(userMin, userMax, + new void (frame f, transform t, transform T, pair, pair) { frame Tinvf=T == identity() ? f : t*inverse(T)*inverse(t)*f; clip(f,T*box(((t*userMin).x,(min(Tinvf)).y), ((t*userMax).x,(max(Tinvf)).y))); @@ -1396,21 +1397,22 @@ void ylimits(picture pic=currentpicture, real min=-infinity, real max=infinity, bounds my; if(pic.scale.y.automin() || pic.scale.y.automax()) - my=autoscale(pic.userMin.y,pic.userMax.y,pic.scale.y.scale); + my=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale); if(pic.scale.y.automin) { if(pic.scale.y.automin()) pic.userMiny(my.min); - } else pic.userMiny(pic.scale.y.T(min)); + } else pic.userMiny(min(pic.scale.y.T(min),pic.scale.y.T(max))); if(pic.scale.y.automax) { if(pic.scale.y.automax()) pic.userMaxy(my.max); - } else pic.userMaxy(pic.scale.y.T(max)); + } else pic.userMaxy(max(pic.scale.y.T(min),pic.scale.y.T(max))); if(crop) { pair userMin=pic.userMin(); pair userMax=pic.userMax(); pic.bounds.yclip(userMin.y,userMax.y); - pic.clip(new void (frame f, transform t, transform T, pair, pair) { + pic.clip(userMin, userMax, + new void (frame f, transform t, transform T, pair, pair) { frame Tinvf=T == identity() ? f : t*inverse(T)*inverse(t)*f; clip(f,T*box(((min(Tinvf)).x,(t*userMin).y), ((max(Tinvf)).x,(t*userMax).y))); @@ -1423,7 +1425,7 @@ void crop(picture pic=currentpicture) { xlimits(pic,false); ylimits(pic,false); - if(pic.userSetx && pic.userSety) + if(pic.userSetx() && pic.userSety()) clip(pic,box(pic.userMin(),pic.userMax())); } @@ -1432,7 +1434,7 @@ void limits(picture pic=currentpicture, pair min, pair max, bool crop=NoCrop) { xlimits(pic,min.x,max.x); ylimits(pic,min.y,max.y); - if(crop && pic.userSetx && pic.userSety) + if(crop && pic.userSetx() && pic.userSety()) clip(pic,box(pic.userMin(),pic.userMax())); } @@ -1443,25 +1445,25 @@ void autoscale(picture pic=currentpicture, axis axis) bounds mx,my; pic.scale.set=true; - if(pic.userSetx) { - mx=autoscale(pic.userMin.x,pic.userMax.x,pic.scale.x.scale); + if(pic.userSetx()) { + mx=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale); if(pic.scale.x.scale.logarithmic && - floor(pic.userMin.x) == floor(pic.userMax.x)) { + floor(pic.userMin().x) == floor(pic.userMax().x)) { if(pic.scale.x.automin()) - pic.userMinx(floor(pic.userMin.x)); + pic.userMinx(floor(pic.userMin().x)); if(pic.scale.x.automax()) - pic.userMaxx(ceil(pic.userMax.x)); + pic.userMaxx(ceil(pic.userMax().x)); } } else {mx.min=mx.max=0; pic.scale.set=false;} - if(pic.userSety) { - my=autoscale(pic.userMin.y,pic.userMax.y,pic.scale.y.scale); + if(pic.userSety()) { + my=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale); if(pic.scale.y.scale.logarithmic && - floor(pic.userMin.y) == floor(pic.userMax.y)) { + floor(pic.userMin().y) == floor(pic.userMax().y)) { if(pic.scale.y.automin()) - pic.userMiny(floor(pic.userMin.y)); + pic.userMiny(floor(pic.userMin().y)); if(pic.scale.y.automax()) - pic.userMaxy(ceil(pic.userMax.y)); + pic.userMaxy(ceil(pic.userMax().y)); } } else {my.min=my.max=0; pic.scale.set=false;} @@ -1503,9 +1505,9 @@ void xaxis(picture pic=currentpicture, Label L="", axis axis=YZero, newticks=true; } - if(newticks && pic.userSetx && ticks != NoTicks) { - if(xmin == -infinity) xmin=pic.userMin.x; - if(xmax == infinity) xmax=pic.userMax.x; + if(newticks && pic.userSetx() && ticks != NoTicks) { + if(xmin == -infinity) xmin=pic.userMin().x; + if(xmax == infinity) xmax=pic.userMax().x; bounds mx=autoscale(xmin,xmax,pic.scale.x.scale); pic.scale.x.tickMin=mx.min; pic.scale.x.tickMax=mx.max; @@ -1517,15 +1519,15 @@ void xaxis(picture pic=currentpicture, Label L="", axis axis=YZero, if(xmin == -infinity && !axis.extend) { if(pic.scale.set) xmin=pic.scale.x.automin() ? pic.scale.x.tickMin : - max(pic.scale.x.tickMin,pic.userMin.x); - else xmin=pic.userMin.x; + max(pic.scale.x.tickMin,pic.userMin().x); + else xmin=pic.userMin().x; } if(xmax == infinity && !axis.extend) { if(pic.scale.set) xmax=pic.scale.x.automax() ? pic.scale.x.tickMax : - min(pic.scale.x.tickMax,pic.userMax.x); - else xmax=pic.userMax.x; + min(pic.scale.x.tickMax,pic.userMax().x); + else xmax=pic.userMax().x; } if(L.defaultposition) L.position(axis.position); @@ -1565,9 +1567,9 @@ void yaxis(picture pic=currentpicture, Label L="", axis axis=XZero, newticks=true; } - if(newticks && pic.userSety && ticks != NoTicks) { - if(ymin == -infinity) ymin=pic.userMin.y; - if(ymax == infinity) ymax=pic.userMax.y; + if(newticks && pic.userSety() && ticks != NoTicks) { + if(ymin == -infinity) ymin=pic.userMin().y; + if(ymax == infinity) ymax=pic.userMax().y; bounds my=autoscale(ymin,ymax,pic.scale.y.scale); pic.scale.y.tickMin=my.min; pic.scale.y.tickMax=my.max; @@ -1579,16 +1581,16 @@ void yaxis(picture pic=currentpicture, Label L="", axis axis=XZero, if(ymin == -infinity && !axis.extend) { if(pic.scale.set) ymin=pic.scale.y.automin() ? pic.scale.y.tickMin : - max(pic.scale.y.tickMin,pic.userMin.y); - else ymin=pic.userMin.y; + max(pic.scale.y.tickMin,pic.userMin().y); + else ymin=pic.userMin().y; } if(ymax == infinity && !axis.extend) { if(pic.scale.set) ymax=pic.scale.y.automax() ? pic.scale.y.tickMax : - min(pic.scale.y.tickMax,pic.userMax.y); - else ymax=pic.userMax.y; + min(pic.scale.y.tickMax,pic.userMax().y); + else ymax=pic.userMax().y; } if(L.defaultposition) L.position(axis.position); @@ -1784,23 +1786,23 @@ picture secondaryX(picture primary=currentpicture, void f(picture)) { if(!primary.scale.set) abort(noprimary); picture pic; - if(primary.userMax.x == primary.userMin.x) return pic; + if(primary.userMax().x == primary.userMin().x) return pic; f(pic); - if(!pic.userSetx) return pic; - bounds a=autoscale(pic.userMin.x,pic.userMax.x,pic.scale.x.scale); - real bmin=pic.scale.x.automin() ? a.min : pic.userMin.x; - real bmax=pic.scale.x.automax() ? a.max : pic.userMax.x; + if(!pic.userSetx()) return pic; + bounds a=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale); + real bmin=pic.scale.x.automin() ? a.min : pic.userMin().x; + real bmax=pic.scale.x.automax() ? a.max : pic.userMax().x; real denom=bmax-bmin; if(denom != 0) { pic.erase(); - real m=(primary.userMax.x-primary.userMin.x)/denom; - pic.scale.x.postscale=Linear(m,bmin-primary.userMin.x/m); + real m=(primary.userMax().x-primary.userMin().x)/denom; + pic.scale.x.postscale=Linear(m,bmin-primary.userMin().x/m); pic.scale.set=true; pic.scale.x.tickMin=pic.scale.x.postscale.T(a.min); pic.scale.x.tickMax=pic.scale.x.postscale.T(a.max); - pic.scale.y.tickMin=primary.userMin.y; - pic.scale.y.tickMax=primary.userMax.y; + pic.scale.y.tickMin=primary.userMin().y; + pic.scale.y.tickMax=primary.userMax().y; axis.xdivisor=a.divisor; f(pic); } @@ -1813,21 +1815,21 @@ picture secondaryY(picture primary=currentpicture, void f(picture)) { if(!primary.scale.set) abort(noprimary); picture pic; - if(primary.userMax.y == primary.userMin.y) return pic; + if(primary.userMax().y == primary.userMin().y) return pic; f(pic); - if(!pic.userSety) return pic; - bounds a=autoscale(pic.userMin.y,pic.userMax.y,pic.scale.y.scale); - real bmin=pic.scale.y.automin() ? a.min : pic.userMin.y; - real bmax=pic.scale.y.automax() ? a.max : pic.userMax.y; + if(!pic.userSety()) return pic; + bounds a=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale); + real bmin=pic.scale.y.automin() ? a.min : pic.userMin().y; + real bmax=pic.scale.y.automax() ? a.max : pic.userMax().y; real denom=bmax-bmin; if(denom != 0) { pic.erase(); - real m=(primary.userMax.y-primary.userMin.y)/denom; - pic.scale.y.postscale=Linear(m,bmin-primary.userMin.y/m); + real m=(primary.userMax().y-primary.userMin().y)/denom; + pic.scale.y.postscale=Linear(m,bmin-primary.userMin().y/m); pic.scale.set=true; - pic.scale.x.tickMin=primary.userMin.x; - pic.scale.x.tickMax=primary.userMax.x; + pic.scale.x.tickMin=primary.userMin().x; + pic.scale.x.tickMax=primary.userMax().x; pic.scale.y.tickMin=pic.scale.y.postscale.T(a.min); pic.scale.y.tickMax=pic.scale.y.postscale.T(a.max); axis.ydivisor=a.divisor; diff --git a/Build/source/utils/asymptote/base/graph3.asy b/Build/source/utils/asymptote/base/graph3.asy index e588c8aa4d6..391b72ca87a 100644 --- a/Build/source/utils/asymptote/base/graph3.asy +++ b/Build/source/utils/asymptote/base/graph3.asy @@ -363,14 +363,14 @@ InOutTicks=InOutTicks(); triple tickMin3(picture pic) { - return minbound(pic.userMin,(pic.scale.x.tickMin,pic.scale.y.tickMin, - pic.scale.z.tickMin)); + return minbound(pic.userMin(),(pic.scale.x.tickMin,pic.scale.y.tickMin, + pic.scale.z.tickMin)); } triple tickMax3(picture pic) { - return maxbound(pic.userMax,(pic.scale.x.tickMax,pic.scale.y.tickMax, - pic.scale.z.tickMax)); + return maxbound(pic.userMax(),(pic.scale.x.tickMax,pic.scale.y.tickMax, + pic.scale.z.tickMax)); } axis Bounds(int type=Both, int type2=Both, triple align=O, bool extend=false) @@ -570,27 +570,27 @@ void xaxis3At(picture pic=currentpicture, Label L="", axis axis, void bounds() { if(type == Min) - y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin.y; + y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y; else if(type == Max) - y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax.y; + y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; else if(type == Both) { - y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax.y; + y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; y=opposite ? y2 : - (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin.y); + (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y); } if(type2 == Min) - z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin.z; + z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z; else if(type2 == Max) - z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax.z; + z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; else if(type2 == Both) { - z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax.z; + z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; z=opposite2 ? z2 : - (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin.z); + (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z); } - real Xmin=finite(xmin) ? xmin : pic.userMin.x; - real Xmax=finite(xmax) ? xmax : pic.userMax.x; + real Xmin=finite(xmin) ? xmin : pic.userMin().x; + real Xmax=finite(xmax) ? xmax : pic.userMax().x; triple a=(Xmin,y,z); triple b=(Xmax,y,z); @@ -692,27 +692,27 @@ void yaxis3At(picture pic=currentpicture, Label L="", axis axis, void bounds() { if(type == Min) - x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin.x; + x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x; else if(type == Max) - x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax.x; + x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; else if(type == Both) { - x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax.x; + x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; x=opposite ? x2 : - (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin.x); + (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x); } if(type2 == Min) - z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin.z; + z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z; else if(type2 == Max) - z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax.z; + z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; else if(type2 == Both) { - z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax.z; + z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; z=opposite2 ? z2 : - (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin.z); + (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z); } - real Ymin=finite(ymin) ? ymin : pic.userMin.y; - real Ymax=finite(ymax) ? ymax : pic.userMax.y; + real Ymin=finite(ymin) ? ymin : pic.userMin().y; + real Ymax=finite(ymax) ? ymax : pic.userMax().y; triple a=(x,Ymin,z); triple b=(x,Ymax,z); @@ -814,27 +814,27 @@ void zaxis3At(picture pic=currentpicture, Label L="", axis axis, void bounds() { if(type == Min) - x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin.x; + x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x; else if(type == Max) - x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax.x; + x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; else if(type == Both) { - x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax.x; + x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; x=opposite ? x2 : - (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin.x); + (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x); } if(type2 == Min) - y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin.y; + y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y; else if(type2 == Max) - y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax.y; + y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; else if(type2 == Both) { - y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax.y; + y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; y=opposite2 ? y2 : - (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin.y); + (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y); } - real Zmin=finite(zmin) ? zmin : pic.userMin.z; - real Zmax=finite(zmax) ? zmax : pic.userMax.z; + real Zmin=finite(zmin) ? zmin : pic.userMin().z; + real Zmax=finite(zmax) ? zmax : pic.userMax().z; triple a=(x,y,Zmin); triple b=(x,y,Zmax); @@ -893,14 +893,14 @@ void autoscale3(picture pic=currentpicture, axis axis) if(!set) { bounds mz; - if(pic.userSetz) { - mz=autoscale(pic.userMin.z,pic.userMax.z,pic.scale.z.scale); + if(pic.userSetz()) { + mz=autoscale(pic.userMin().z,pic.userMax().z,pic.scale.z.scale); if(pic.scale.z.scale.logarithmic && - floor(pic.userMin.z) == floor(pic.userMax.z)) { + floor(pic.userMin().z) == floor(pic.userMax().z)) { if(pic.scale.z.automin()) - pic.userMinz(floor(pic.userMin.z)); + pic.userMinz(floor(pic.userMin().z)); if(pic.scale.z.automax()) - pic.userMaxz(ceil(pic.userMax.z)); + pic.userMaxz(ceil(pic.userMax().z)); } } else {mz.min=mz.max=0; pic.scale.set=false;} @@ -938,9 +938,9 @@ void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero, newticks=true; } - if(newticks && pic.userSetx && ticks != NoTicks3) { - if(xmin == -infinity) xmin=pic.userMin.x; - if(xmax == infinity) xmax=pic.userMax.x; + if(newticks && pic.userSetx() && ticks != NoTicks3) { + if(xmin == -infinity) xmin=pic.userMin().x; + if(xmax == infinity) xmax=pic.userMax().x; bounds mx=autoscale(xmin,xmax,pic.scale.x.scale); pic.scale.x.tickMin=mx.min; pic.scale.x.tickMax=mx.max; @@ -952,15 +952,15 @@ void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero, if(xmin == -infinity && !axis.extend) { if(pic.scale.set) xmin=pic.scale.x.automin() ? pic.scale.x.tickMin : - max(pic.scale.x.tickMin,pic.userMin.x); - else xmin=pic.userMin.x; + max(pic.scale.x.tickMin,pic.userMin().x); + else xmin=pic.userMin().x; } if(xmax == infinity && !axis.extend) { if(pic.scale.set) xmax=pic.scale.x.automax() ? pic.scale.x.tickMax : - min(pic.scale.x.tickMax,pic.userMax.x); - else xmax=pic.userMax.x; + min(pic.scale.x.tickMax,pic.userMax().x); + else xmax=pic.userMax().x; } if(L.defaultposition) { @@ -971,7 +971,7 @@ void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero, bool back=false; if(axis.type == Both) { triple v=currentprojection.normal; - back=dot((0,pic.userMax.y-pic.userMin.y,0),v)*sgn(v.z) > 0; + back=dot((0,pic.userMax().y-pic.userMin().y,0),v)*sgn(v.z) > 0; } xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,false,false,!back); @@ -1012,9 +1012,9 @@ void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero, newticks=true; } - if(newticks && pic.userSety && ticks != NoTicks3) { - if(ymin == -infinity) ymin=pic.userMin.y; - if(ymax == infinity) ymax=pic.userMax.y; + if(newticks && pic.userSety() && ticks != NoTicks3) { + if(ymin == -infinity) ymin=pic.userMin().y; + if(ymax == infinity) ymax=pic.userMax().y; bounds my=autoscale(ymin,ymax,pic.scale.y.scale); pic.scale.y.tickMin=my.min; pic.scale.y.tickMax=my.max; @@ -1026,16 +1026,16 @@ void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero, if(ymin == -infinity && !axis.extend) { if(pic.scale.set) ymin=pic.scale.y.automin() ? pic.scale.y.tickMin : - max(pic.scale.y.tickMin,pic.userMin.y); - else ymin=pic.userMin.y; + max(pic.scale.y.tickMin,pic.userMin().y); + else ymin=pic.userMin().y; } if(ymax == infinity && !axis.extend) { if(pic.scale.set) ymax=pic.scale.y.automax() ? pic.scale.y.tickMax : - min(pic.scale.y.tickMax,pic.userMax.y); - else ymax=pic.userMax.y; + min(pic.scale.y.tickMax,pic.userMax().y); + else ymax=pic.userMax().y; } if(L.defaultposition) { @@ -1046,7 +1046,7 @@ void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero, bool back=false; if(axis.type == Both) { triple v=currentprojection.normal; - back=dot((pic.userMax.x-pic.userMin.x,0,0),v)*sgn(v.z) > 0; + back=dot((pic.userMax().x-pic.userMin().x,0,0),v)*sgn(v.z) > 0; } yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,false,false,!back); @@ -1087,9 +1087,9 @@ void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero, newticks=true; } - if(newticks && pic.userSetz && ticks != NoTicks3) { - if(zmin == -infinity) zmin=pic.userMin.z; - if(zmax == infinity) zmax=pic.userMax.z; + if(newticks && pic.userSetz() && ticks != NoTicks3) { + if(zmin == -infinity) zmin=pic.userMin().z; + if(zmax == infinity) zmax=pic.userMax().z; bounds mz=autoscale(zmin,zmax,pic.scale.z.scale); pic.scale.z.tickMin=mz.min; pic.scale.z.tickMax=mz.max; @@ -1101,15 +1101,15 @@ void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero, if(zmin == -infinity && !axis.extend) { if(pic.scale.set) zmin=pic.scale.z.automin() ? pic.scale.z.tickMin : - max(pic.scale.z.tickMin,pic.userMin.z); - else zmin=pic.userMin.z; + max(pic.scale.z.tickMin,pic.userMin().z); + else zmin=pic.userMin().z; } if(zmax == infinity && !axis.extend) { if(pic.scale.set) zmax=pic.scale.z.automax() ? pic.scale.z.tickMax : - min(pic.scale.z.tickMax,pic.userMax.z); - else zmax=pic.userMax.z; + min(pic.scale.z.tickMax,pic.userMax().z); + else zmax=pic.userMax().z; } if(L.defaultposition) { @@ -1120,7 +1120,7 @@ void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero, bool back=false; if(axis.type == Both) { triple v=currentprojection.vector(); - back=dot((pic.userMax.x-pic.userMin.x,0,0),v)*sgn(v.y) > 0; + back=dot((pic.userMax().x-pic.userMin().x,0,0),v)*sgn(v.y) > 0; } zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,false,false,!back); @@ -1144,15 +1144,15 @@ void zlimits(picture pic=currentpicture, real min=-infinity, real max=infinity, bounds mz; if(pic.scale.z.automin() || pic.scale.z.automax()) - mz=autoscale(pic.userMin.z,pic.userMax.z,pic.scale.z.scale); + mz=autoscale(pic.userMin().z,pic.userMax().z,pic.scale.z.scale); if(pic.scale.z.automin) { if(pic.scale.z.automin()) pic.userMinz(mz.min); - } else pic.userMinz(pic.scale.z.T(min)); + } else pic.userMinz(min(pic.scale.z.T(min),pic.scale.z.T(max))); if(pic.scale.z.automax) { if(pic.scale.z.automax()) pic.userMaxz(mz.max); - } else pic.userMaxz(pic.scale.z.T(max)); + } else pic.userMaxz(max(pic.scale.z.T(min),pic.scale.z.T(max))); } // Restrict the x, y, and z limits to box(min,max). diff --git a/Build/source/utils/asymptote/base/grid3.asy b/Build/source/utils/asymptote/base/grid3.asy index afd621eb741..98e043a71d7 100644 --- a/Build/source/utils/asymptote/base/grid3.asy +++ b/Build/source/utils/asymptote/base/grid3.asy @@ -33,18 +33,20 @@ struct grid3 { typedef grid3 grid3routine(picture pic); -triple X(picture pic) {return (pic.userMax.x,pic.userMin.y,pic.userMin.z);} -triple XY(picture pic) {return (pic.userMax.x,pic.userMax.y,pic.userMin.z);} -triple Y(picture pic) {return (pic.userMin.x,pic.userMax.y,pic.userMin.z);} -triple YZ(picture pic) {return (pic.userMin.x,pic.userMax.y,pic.userMax.z);} -triple Z(picture pic) {return (pic.userMin.x,pic.userMin.y,pic.userMax.z);} -triple ZX(picture pic) {return (pic.userMax.x,pic.userMin.y,pic.userMax.z);} +triple X(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMin().z);} +triple XY(picture pic) {return (pic.userMax().x,pic.userMax().y,pic.userMin().z);} +triple Y(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMin().z);} +triple YZ(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMax().z);} +triple Z(picture pic) {return (pic.userMin().x,pic.userMin().y,pic.userMax().z);} +triple ZX(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMax().z);} grid3routine XYgrid(position pos=Relative(0)) { return new grid3(picture pic) { grid3 og; - og.create(pic,pic.userMin--X(pic),Y(pic)--XY(pic),pic.userMin--Z(pic), - pic.userMin.x,pic.userMax.x,pos,pic.scale.x); + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--X(pic),Y(pic)--XY(pic),m--Z(pic), + m.x,M.x,pos,pic.scale.x); return og; }; }; @@ -53,8 +55,10 @@ grid3routine XYgrid=XYgrid(); grid3routine YXgrid(position pos=Relative(0)) { return new grid3(picture pic) { grid3 og; - og.create(pic,pic.userMin--Y(pic),X(pic)--XY(pic),pic.userMin--Z(pic), - pic.userMin.y,pic.userMax.y,pos,pic.scale.y); + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Y(pic),X(pic)--XY(pic),m--Z(pic), + m.y,M.y,pos,pic.scale.y); return og; }; }; @@ -64,8 +68,10 @@ grid3routine YXgrid=YXgrid(); grid3routine XZgrid(position pos=Relative(0)) { return new grid3(picture pic) { grid3 og; - og.create(pic,pic.userMin--X(pic),Z(pic)--ZX(pic),pic.userMin--Y(pic), - pic.userMin.x,pic.userMax.x,pos,pic.scale.x); + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--X(pic),Z(pic)--ZX(pic),m--Y(pic), + m.x,M.x,pos,pic.scale.x); return og; }; }; @@ -74,8 +80,10 @@ grid3routine XZgrid=XZgrid(); grid3routine ZXgrid(position pos=Relative(0)) { return new grid3(picture pic) { grid3 og; - og.create(pic,pic.userMin--Z(pic),X(pic)--ZX(pic),pic.userMin--Y(pic), - pic.userMin.z,pic.userMax.z,pos,pic.scale.z); + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Z(pic),X(pic)--ZX(pic),m--Y(pic), + m.z,M.z,pos,pic.scale.z); return og; }; }; @@ -84,8 +92,10 @@ grid3routine ZXgrid=ZXgrid(); grid3routine YZgrid(position pos=Relative(0)) { return new grid3(picture pic) { grid3 og; - og.create(pic,pic.userMin--Y(pic),Z(pic)--YZ(pic),pic.userMin--X(pic), - pic.userMin.y,pic.userMax.y,pos,pic.scale.y); + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Y(pic),Z(pic)--YZ(pic),m--X(pic), + m.y,M.y,pos,pic.scale.y); return og; }; }; @@ -94,8 +104,10 @@ grid3routine YZgrid=YZgrid(); grid3routine ZYgrid(position pos=Relative(0)) { return new grid3(picture pic) { grid3 og; - og.create(pic,pic.userMin--Z(pic),Y(pic)--YZ(pic),pic.userMin--X(pic), - pic.userMin.z,pic.userMax.z,pos,pic.scale.z); + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Z(pic),Y(pic)--YZ(pic),m--X(pic), + m.z,M.z,pos,pic.scale.z); return og; }; }; diff --git a/Build/source/utils/asymptote/base/math.asy b/Build/source/utils/asymptote/base/math.asy index 0dfddff7f2f..77c0664895f 100644 --- a/Build/source/utils/asymptote/base/math.asy +++ b/Build/source/utils/asymptote/base/math.asy @@ -113,6 +113,30 @@ real[] partialsum(real[] a, real[] dx) return b; } +// Given an integer array a, return its partial sums. +int[] partialsum(int[] a) +{ + int[] b=new int[a.length]; + int sum=0; + for(int i=0; i < a.length; ++i) { + sum += a[i]; + b[i]=sum; + } + return b; +} + +// Given an integer array a, return its partial dx-weighted sums. +int[] partialsum(int[] a, int[] dx) +{ + int[] b=new int[a.length]; + int sum=0; + for(int i=0; i < a.length; ++i) { + sum += a[i]*dx[i]; + b[i]=sum; + } + return b; +} + // If strict=false, return whether i > j implies a[i] >= a[j] // If strict=true, return whether i > j implies a[i] > a[j] bool increasing(real[] a, bool strict=false) @@ -232,11 +256,12 @@ bool rectangular(triple[][] m) // size of picture pic. void drawline(picture pic=currentpicture, pair P, pair Q, pen p=currentpen) { - pic.add(new void (frame f, transform t, transform, pair m, pair M) { + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { // Reduce the bounds by the size of the pen. m -= min(p); M -= max(p); // Calculate the points and direction vector in the transformed space. + t=t*T; pair z=t*P; pair v=t*Q-z; diff --git a/Build/source/utils/asymptote/base/palette.asy b/Build/source/utils/asymptote/base/palette.asy index 41a40a04a85..88f8ae2c42f 100644 --- a/Build/source/utils/asymptote/base/palette.asy +++ b/Build/source/utils/asymptote/base/palette.asy @@ -138,6 +138,18 @@ void image(picture pic=currentpicture, pen[][] data, pair initial, pair final, pic.addBox(initial,final); } +void image(picture pic=currentpicture, pen f(int, int), int width, int height, + pair initial, pair final, bool antialias=false) +{ + initial=Scale(pic,initial); + final=Scale(pic,final); + + pic.add(new void(frame F, transform t) { + _image(F,f,width,height,initial,final,t,antialias=antialias); + },true); + pic.addBox(initial,final); +} + bounds image(picture pic=currentpicture, pair[] z, real[] f, range range=Full, pen[] palette) { diff --git a/Build/source/utils/asymptote/base/plain_arrows.asy b/Build/source/utils/asymptote/base/plain_arrows.asy index 490f76f777c..59b65aa4126 100644 --- a/Build/source/utils/asymptote/base/plain_arrows.asy +++ b/Build/source/utils/asymptote/base/plain_arrows.asy @@ -469,30 +469,87 @@ void draw(frame f, path g, pen p=currentpen, arrowbar arrow) add(f,pic.fit()); } -void draw(picture pic=currentpicture, Label L="", path g, align align=NoAlign, - pen p=currentpen, arrowbar arrow=None, arrowbar bar=None, - margin margin=NoMargin, Label legend="", marker marker=nomarker) -{ - Label L=L.copy(); - L.align(align); - if(marker != nomarker && !marker.above) marker.mark(pic,g); - bool drawpath=arrow(pic,g,p,margin); - if(bar(pic,g,p,margin) && drawpath) _draw(pic,g,p,margin); - if(L.s != "") { - L.p(p); - L.out(pic,g); +void draw(picture pic=currentpicture, Label L=null, path g, + align align=NoAlign, pen p=currentpen, arrowbar arrow=None, + arrowbar bar=None, margin margin=NoMargin, Label legend=null, + marker marker=nomarker) +{ + // These if statements are ordered in such a way that the most common case + // (with just a path and a pen) executes the least bytecode. + if (marker == nomarker) + { + if (arrow == None && bar == None) + { + if (margin == NoMargin && size(nib(p)) == 0) + { + pic.addExactAbove( + new void(frame f, transform t, transform T, pair, pair) { + _draw(f,t*T*g,p); + }); + pic.addPath(g,p); + + // Jumping over else clauses takes time, so test if we can return + // here. + if (L == null && legend == null) + return; + } + else // With margin or polygonal pen. + { + _draw(pic, g, p, margin); + } + } + else /* arrow or bar */ + { + // Note we are using & instead of && as both arrow and bar need to be + // called. + if (arrow(pic, g, p, margin) & bar(pic, g, p, margin)) + _draw(pic, g, p, margin); + } + + if(L != null && L.s != "") { + L=L.copy(); + L.align(align); + L.p(p); + L.out(pic,g); + } + + if(legend != null && legend.s != "") { + legend.p(p); + pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above)); + } } - if(legend.s != "") { - legend.p(p); - pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above)); + else /* marker != nomarker */ + { + if(marker != nomarker && !marker.above) marker.mark(pic,g); + + // Note we are using & instead of && as both arrow and bar need to be + // called. + if ((arrow == None || arrow(pic, g, p, margin)) & + (bar == None || bar(pic, g, p, margin))) + { + _draw(pic, g, p, margin); + } + + if(L != null && L.s != "") { + L=L.copy(); + L.align(align); + L.p(p); + L.out(pic,g); + } + + if(legend != null && legend.s != "") { + legend.p(p); + pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above)); + } + + if(marker != nomarker && marker.above) marker.mark(pic,g); } - if(marker != nomarker && marker.above) marker.mark(pic,g); } // Draw a fixed-size line about the user-coordinate 'origin'. -void draw(pair origin, picture pic=currentpicture, Label L="", path g, +void draw(pair origin, picture pic=currentpicture, Label L=null, path g, align align=NoAlign, pen p=currentpen, arrowbar arrow=None, - arrowbar bar=None, margin margin=NoMargin, Label legend="", + arrowbar bar=None, margin margin=NoMargin, Label legend=null, marker marker=nomarker) { picture opic; @@ -501,21 +558,22 @@ void draw(pair origin, picture pic=currentpicture, Label L="", path g, } void draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen, - Label legend="", marker marker=nomarker) + Label legend=null, marker marker=nomarker) { + // This could be optimized to size and draw the entire array as a batch. for(int i=0; i < g.length-1; ++i) draw(pic,g[i],p,marker); if(g.length > 0) draw(pic,g[g.length-1],p,legend,marker); } void draw(picture pic=currentpicture, guide[] g, pen p=currentpen, - Label legend="", marker marker=nomarker) + Label legend=null, marker marker=nomarker) { draw(pic,(path[]) g,p,legend,marker); } void draw(pair origin, picture pic=currentpicture, explicit path[] g, - pen p=currentpen, Label legend="", marker marker=nomarker) + pen p=currentpen, Label legend=null, marker marker=nomarker) { picture opic; draw(opic,g,p,legend,marker); @@ -523,21 +581,23 @@ void draw(pair origin, picture pic=currentpicture, explicit path[] g, } void draw(pair origin, picture pic=currentpicture, guide[] g, pen p=currentpen, - Label legend="", marker marker=nomarker) + Label legend=null, marker marker=nomarker) { draw(origin,pic,(path[]) g,p,legend,marker); } // Align an arrow pointing to b from the direction dir. The arrow is // 'length' PostScript units long. -void arrow(picture pic=currentpicture, Label L="", pair b, pair dir, +void arrow(picture pic=currentpicture, Label L=null, pair b, pair dir, real length=arrowlength, align align=NoAlign, pen p=currentpen, arrowbar arrow=Arrow, margin margin=EndMargin) { - Label L=L.copy(); - if(L.defaultposition) L.position(0); - L.align(L.align,dir); - L.p(p); + if(L != null && L.s != "") { + L=L.copy(); + if(L.defaultposition) L.position(0); + L.align(L.align,dir); + L.p(p); + } marginT margin=margin(b--b,p); // Extract margin.begin and margin.end pair a=(margin.begin+length+margin.end)*unit(dir); draw(b,pic,L,a--(0,0),align,p,arrow,margin); @@ -562,6 +622,7 @@ frame[] fit2(picture[] pictures, picture all) } // Fit an array of pictures simultaneously using the size of the first picture. +// TODO: Remove unused arguments. frame[] fit(string prefix="", picture[] pictures, string format="", bool view=true, string options="", string script="", projection P=currentprojection) diff --git a/Build/source/utils/asymptote/base/plain_bounds.asy b/Build/source/utils/asymptote/base/plain_bounds.asy new file mode 100644 index 00000000000..71c304da02f --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_bounds.asy @@ -0,0 +1,783 @@ +include plain_scaling; + +// After an transformation, produce new coordinate bounds. For paths that +// have been added, this is only an approximation since it takes the bounds of +// their transformed bounding box. +private void addTransformedCoords(coords2 dest, transform t, + coords2 point, coords2 min, coords2 max) +{ + dest.push(t, point, point); + + // Add in all 4 corner coords, to properly size rectangular pictures. + dest.push(t,min,min); + dest.push(t,min,max); + dest.push(t,max,min); + dest.push(t,max,max); +} + +// Adds another sizing restriction to the coordinates, but only if it is +// maximal, that is, if under some scaling, this coordinate could be the +// largest. +private void addIfMaximal(coord[] coords, real user, real truesize) { + // TODO: Test promoting coordinates for efficiency. + + for (coord c : coords) + if (user <= c.user && truesize <= c.truesize) + // Not maximal. + return; + + // The coordinate is not dominated by any existing extreme, so it is + // maximal and will be added, but first remove any coords it now dominates. + int i = 0; + while (i < coords.length) { + coord c = coords[i]; + if (c.user <= user && c.truesize <= truesize) + coords.delete(i); + else + ++i; + } + + // Add the coordinate to the extremes. + coords.push(coord.build(user, truesize)); +} + +private void addIfMaximal(coord[] dest, coord[] src) +{ + // This may be inefficient, as it rebuilds the coord struct when adding it. + for (coord c : src) + addIfMaximal(dest, c.user, c.truesize); +} + +// Same as addIfMaximal, but testing for minimal coords. +private void addIfMinimal(coord[] coords, real user, real truesize) { + for (coord c : coords) + if (user >= c.user && truesize >= c.truesize) + return; + + int i = 0; + while (i < coords.length) { + coord c = coords[i]; + if (c.user >= user && c.truesize >= truesize) + coords.delete(i); + else + ++i; + } + + coords.push(coord.build(user, truesize)); +} + +private void addIfMinimal(coord[] dest, coord[] src) +{ + for (coord c : src) + addIfMinimal(dest, c.user, c.truesize); +} + +// This stores a list of sizing bounds for picture data. If the object is +// frozen, then it cannot be modified further, and therefore can be safely +// passed by reference and stored in the sizing data for multiple pictures. +private struct freezableBounds { + restricted bool frozen = false; + void freeze() { + frozen = true; + } + + // Optional links to further (frozen) sizing data. + private freezableBounds[] links; + + // Links to (frozen) sizing data that is transformed when added here. + private static struct transformedBounds { + transform t; + freezableBounds link; + }; + private transformedBounds[] tlinks; + + // The sizing data. It cannot be modified once this object is frozen. + private coords2 point, min, max; + + // A bound represented by a path. Using the path instead of the bounding + // box means it will be accurate after a transformation by coordinates. + private path[] pathBounds; + + // A bound represented by a path and a pen. + // As often many paths use the same pen, we store an array of paths. + private static struct pathpen { + path[] g; pen p; + + void operator init(path g, pen p) { + this.g.push(g); + this.p = p; + } + } + private static pathpen operator *(transform t, pathpen pp) { + // Should the pen be transformed? + pathpen newpp; + for (path g : pp.g) + newpp.g.push(t*g); + newpp.p = pp.p; + return newpp; + } + + // WARNING: Due to crazy optimizations, if this array is changed between an + // empty and non-empty state, the assignment of a method to + // addPath(path,pen) must also change. + private pathpen[] pathpenBounds; + + // Once frozen, the sizing is immutable, and therefore we can compute and + // store the extremal coordinates. + public static struct extremes { + coord[] left, bottom, right, top; + + void operator init(coord[] left, coord[] bottom, + coord[] right, coord[] top) { + this.left = left; + this.bottom = bottom; + this.right = right; + this.top = top; + } + + } + private static void addMaxToExtremes(extremes e, pair user, pair truesize) { + addIfMaximal(e.right, user.x, truesize.x); + addIfMaximal(e.top, user.y, truesize.y); + } + private static void addMinToExtremes(extremes e, pair user, pair truesize) { + addIfMinimal(e.left, user.x, truesize.x); + addIfMinimal(e.bottom, user.y, truesize.y); + } + private static void addMaxToExtremes(extremes e, coords2 coords) { + addIfMaximal(e.right, coords.x); + addIfMaximal(e.top, coords.y); + } + private static void addMinToExtremes(extremes e, coords2 coords) { + addIfMinimal(e.left, coords.x); + addIfMinimal(e.bottom, coords.y); + } + + private extremes cachedExtremes = null; + + // Once frozen, getMutable returns a new object based on this one, which can + // be modified. + freezableBounds getMutable() { + assert(frozen); + var f = new freezableBounds; + f.links.push(this); + return f; + } + + freezableBounds transformed(transform t) { + // Freeze these bounds, as we are storing a reference to them. + freeze(); + + var tlink = new transformedBounds; + tlink.t = t; + tlink.link = this; + + var b = new freezableBounds; + b.tlinks.push(tlink); + + return b; + } + + void append(freezableBounds b) { + // Check that we can modify the object. + assert(!frozen); + + //TODO: If b is "small", ie. a single tlink or cliplink, just copy the + //link. + + // As we only reference b, we must freeze it to ensure it does not change. + b.freeze(); + links.push(b); + } + + void addPoint(pair user, pair truesize) { + assert(!frozen); + point.push(user, truesize); + } + + void addBox(pair userMin, pair userMax, pair trueMin, pair trueMax) { + assert(!frozen); + this.min.push(userMin, trueMin); + this.max.push(userMax, trueMax); + } + + void addPath(path g) { + // This, and other asserts have been removed to speed things up slightly. + //assert(!frozen); + this.pathBounds.push(g); + } + + void addPath(path[] g) { + //assert(!frozen); + this.pathBounds.append(g); + } + + // To squeeze out a bit more performance, this method is either assigned + // addPathToNonEmptyArray or addPathToEmptyArray depending on the state of + // the pathpenBounds array. + void addPath(path g, pen p); + + private void addPathToNonEmptyArray(path g, pen p) { + //assert(!frozen); + //assert(!pathpenBounds.empty()); + var pp = pathpenBounds[0]; + + // Test if the pens are equal or have the same bounds. + if (pp.p == p || (min(pp.p) == min(p) && max(pp.p) == max(p))) { + // If this path has the same pen as the last one, just add it to the + // array corresponding to that pen. + pp.g.push(g); + } + else { + // A different pen. Start a new bound and put it on the front. Put + // the old bound at the end of the array. + pathpenBounds[0] = pathpen(g,p); + pathpenBounds.push(pp); + } + } + void addPathToEmptyArray(path g, pen p) { + //assert(!frozen); + //assert(pathpenBounds.empty()); + + pathpenBounds.push(pathpen(g,p)); + addPath = addPathToNonEmptyArray; + } + + // Initial setting for addPath. + addPath = addPathToEmptyArray; + + // Transform the sizing info by t then add the result to the coords + // structure. + private void accumulateCoords(transform t, coords2 coords) { + for (var link : links) + link.accumulateCoords(t, coords); + + for (var tlink : tlinks) + tlink.link.accumulateCoords(t*tlink.t, coords); + + addTransformedCoords(coords, t, this.point, this.min, this.max); + + for (var g : pathBounds) { + g = t*g; + coords.push(min(g), (0,0)); + coords.push(max(g), (0,0)); + } + + for (var pp: pathpenBounds) { + pair pm = min(pp.p), pM = max(pp.p); + for (var g : pp.g) { + g = t*g; + coords.push(min(g), pm); + coords.push(max(g), pM); + } + } + } + + // Add all of the sizing info to the given coords structure. + private void accumulateCoords(coords2 coords) { + for (var link : links) + link.accumulateCoords(coords); + + for (var tlink : tlinks) + tlink.link.accumulateCoords(tlink.t, coords); + + coords.append(this.point); + coords.append(this.min); + coords.append(this.max); + + for (var g : pathBounds) { + coords.push(min(g), (0,0)); + coords.push(max(g), (0,0)); + } + + for (var pp: pathpenBounds) { + pair pm = min(pp.p), pM = max(pp.p); + for (var g : pp.g) { + coords.push(min(g), pm); + coords.push(max(g), pM); + } + } + } + + // Returns all of the coords that this sizing data represents. + private coords2 allCoords() { + coords2 coords; + accumulateCoords(coords); + return coords; + } + + private void addLocalsToExtremes(transform t, extremes e) { + coords2 coords; + addTransformedCoords(coords, t, this.point, this.min, this.max); + addMinToExtremes(e, coords); + addMaxToExtremes(e, coords); + + if (pathBounds.length > 0) { + addMinToExtremes(e, minAfterTransform(t, pathBounds), (0,0)); + addMaxToExtremes(e, maxAfterTransform(t, pathBounds), (0,0)); + } + + for (var pp : pathpenBounds) { + if (pp.g.length > 0) { + addMinToExtremes(e, minAfterTransform(t, pp.g), min(pp.p)); + addMaxToExtremes(e, maxAfterTransform(t, pp.g), max(pp.p)); + } + } + } + + private void addToExtremes(transform t, extremes e) { + for (var link : links) + link.addToExtremes(t, e); + + for (var tlink : tlinks) + tlink.link.addToExtremes(t*tlink.t, e); + + addLocalsToExtremes(t, e); + } + + private void addLocalsToExtremes(extremes e) { + addMinToExtremes(e, point); + addMaxToExtremes(e, point); + addMinToExtremes(e, min); + addMaxToExtremes(e, max); + + if (pathBounds.length > 0) { + addMinToExtremes(e, min(pathBounds), (0,0)); + addMaxToExtremes(e, max(pathBounds), (0,0)); + } + + for (var pp : pathpenBounds) { + if (pp.g.length > 0) { + addMinToExtremes(e, min(pp.g), min(pp.p)); + addMaxToExtremes(e, max(pp.g), max(pp.p)); + } + } + } + + private void addToExtremes(extremes e) { + for (var link : links) + link.addToExtremes(e); + + for (var tlink : tlinks) + tlink.link.addToExtremes(tlink.t, e); + + addLocalsToExtremes(e); + } + + private static void write(extremes e) { + static void write(coord[] coords) { + for (coord c : coords) + write(" " + (string)c.user + " u + " + (string)c.truesize); + } + write("left:"); + write(e.left); + write("bottom:"); + write(e.bottom); + write("right:"); + write(e.right); + write("top:"); + write(e.top); + } + + // Returns the extremal coordinates of the sizing data. + public extremes extremes() { + if (cachedExtremes == null) { + freeze(); + + extremes e; + addToExtremes(e); + cachedExtremes = e; + } + + return cachedExtremes; + } + + // Helper functions for computing the usersize bounds. usermin and usermax + // would be easily computable from extremes, except that the picture + // interface actually allows calls that manually change the usermin and + // usermax values. Therefore, we have to compute these values separately. + private static struct bounds { + bool areSet=false; + pair min; + pair max; + } + private static struct boundsAccumulator { + pair[] mins; + pair[] maxs; + + void push(pair m, pair M) { + mins.push(m); + maxs.push(M); + } + + void push(bounds b) { + if (b.areSet) + push(b.min, b.max); + } + + void push(transform t, bounds b) { + if (b.areSet) { + pair[] box = { t*(b.min.x,b.max.y), t*b.max, + t*b.min.x, t*(b.min.x,b.max.y) }; + for (var z : box) + push(z,z); + } + } + + void pushUserCoords(coords2 min, coords2 max) { + int n = min.x.length; + assert(min.y.length == n); + assert(max.x.length == n); + assert(max.y.length == n); + + for (int i = 0; i < n; ++i) + push((min.x[i].user, min.y[i].user), + (max.x[i].user, max.y[i].user)); + } + + bounds collapse() { + bounds b; + if (mins.length > 0) { + b.areSet = true; + b.min = minbound(mins); + b.max = maxbound(maxs); + } + else { + b.areSet = false; + } + return b; + } + } + + // The user bounds already calculated for this data. + private bounds storedUserBounds = null; + + private void accumulateUserBounds(boundsAccumulator acc) + { + if (storedUserBounds != null) { + assert(frozen); + acc.push(storedUserBounds); + } else { + acc.pushUserCoords(point, point); + acc.pushUserCoords(min, max); + if (pathBounds.length > 0) + acc.push(min(pathBounds), max(pathBounds)); + for (var pp : pathpenBounds) + acc.push(min(pp.g), max(pp.g)); + for (var link : links) + link.accumulateUserBounds(acc); + + // Transforms are handled as they were in the old system. + for (var tlink : tlinks) { + boundsAccumulator tacc; + tlink.link.accumulateUserBounds(tacc); + acc.push(tlink.t, tacc.collapse()); + } + } + } + + private void computeUserBounds() { + freeze(); + boundsAccumulator acc; + accumulateUserBounds(acc); + storedUserBounds = acc.collapse(); + } + + private bounds userBounds() { + if (storedUserBounds == null) + computeUserBounds(); + + assert(storedUserBounds != null); + return storedUserBounds; + } + + // userMin/userMax returns the minimal/maximal userspace coordinate of the + // sizing data. As coordinates for objects such as labels can have + // significant truesize dimensions, this userMin/userMax values may not + // correspond closely to the end of the screen, and are of limited use. + // userSetx and userSety determine if there is sizing data in order to even + // have userMin/userMax defined. + public bool userBoundsAreSet() { + return userBounds().areSet; + } + + public pair userMin() { + return userBounds().min; + } + public pair userMax() { + return userBounds().max; + } + + // To override the true userMin and userMax bounds, first compute the + // userBounds as they should be at this point, then change the values. + public void alterUserBound(string which, real val) { + // We are changing the bounds data, so it cannot be frozen yet. After the + // user bounds are set, however, the sizing data cannot change, so it will + // be frozen. + assert(!frozen); + computeUserBounds(); + assert(frozen); + + var b = storedUserBounds; + if (which == "minx") + b.min = (val, b.min.y); + else if (which == "miny") + b.min = (b.min.x, val); + else if (which == "maxx") + b.max = (val, b.max.y); + else { + assert(which == "maxy"); + b.max = (b.max.x, val); + } + } + + // A temporary measure. Stuffs all of the data from the links and paths + // into the coords. + private void flatten() { + assert(!frozen); + + // First, compute the user bounds, taking into account any manual + // alterations. + computeUserBounds(); + + // Calculate all coordinates. + coords2 coords = allCoords(); + + // Erase all the old data. + point.erase(); + min.erase(); + max.erase(); + pathBounds.delete(); + pathpenBounds.delete(); + addPath = addPathToEmptyArray; + links.delete(); + tlinks.delete(); + + // Put all of the coordinates into point. + point = coords; + } + + void xclip(real Min, real Max) { + assert(!frozen); + flatten(); + point.xclip(Min,Max); + min.xclip(Min,Max); + max.xclip(Min,Max); + + // Cap the userBounds. + bounds b = storedUserBounds; + b.min = (max(Min, b.min.x), b.min.y); + b.max = (min(Max, b.max.x), b.max.y); + } + + void yclip(real Min, real Max) { + assert(!frozen); + flatten(); + point.yclip(Min,Max); + min.yclip(Min,Max); + max.yclip(Min,Max); + + // Cap the userBounds. + bounds b = storedUserBounds; + b.min = (b.min.x, max(Min, b.min.y)); + b.max = (b.max.x, min(Max, b.max.y)); + } + + // Calculate the min for the final frame, given the coordinate transform. + pair min(transform t) { + extremes e = extremes(); + if (e.left.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(infinity, xs, e.left), min(infinity, ys, e.bottom)); + } + + // Calculate the max for the final frame, given the coordinate transform. + pair max(transform t) { + extremes e = extremes(); + if (e.right.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(-infinity, xs, e.right), max(-infinity, ys, e.top)); + } + + // Returns the transform for turning user-space pairs into true-space pairs. + transform scaling(real xsize, real ysize, + real xunitsize, real yunitsize, + bool keepAspect, bool warn) { + if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0) + return identity(); + + // Get the extremal coordinates. + extremes e = extremes(); + + real sx; + if(xunitsize == 0) { + if(xsize != 0) sx=calculateScaling("x",e.left,e.right,xsize,warn); + } else sx=xunitsize; + + /* Possible alternative code : + real sx = xunitsize != 0 ? xunitsize : + xsize != 0 ? calculateScaling("x", Coords.x, xsize, warn) : + 0; */ + + real sy; + if(yunitsize == 0) { + if(ysize != 0) sy=calculateScaling("y",e.bottom,e.top,ysize,warn); + } else sy=yunitsize; + + if(sx == 0) { + sx=sy; + if(sx == 0) + return identity(); + } else if(sy == 0) sy=sx; + + + if(keepAspect && (xunitsize == 0 || yunitsize == 0)) + return scale(min(sx,sy)); + else + return scale(sx,sy); + } +} + +struct bounds { + private var base = new freezableBounds; + + // We should probably put this back into picture. + bool exact = true; + + // Called just before modifying the sizing data. It ensures base is + // non-frozen. + // Note that this is manually inlined for speed reasons in a couple often + // called methods below. + private void makeMutable() { + if (base.frozen) + base = base.getMutable(); + //assert(!base.frozen); // Disabled for speed reasons. + } + + void erase() { + // Just discard the old bounds. + base = new freezableBounds; + + // We don't reset the 'exact' field, for backward compatibility. + } + + bounds copy() { + // Freeze the underlying bounds and make a shallow copy. + base.freeze(); + + var b = new bounds; + b.base = this.base; + b.exact = this.exact; + return b; + } + + bounds transformed(transform t) { + var b = new bounds; + b.base = base.transformed(t); + b.exact = this.exact; + return b; + } + + void append(bounds b) { + makeMutable(); + base.append(b.base); + } + + void append(transform t, bounds b) { + // makeMutable will be called by append. + if (t == identity()) + append(b); + else + append(b.transformed(t)); + } + + void addPoint(pair user, pair truesize) { + makeMutable(); + base.addPoint(user, truesize); + } + + void addBox(pair userMin, pair userMax, pair trueMin, pair trueMax) { + makeMutable(); + base.addBox(userMin, userMax, trueMin, trueMax); + } + + void addPath(path g) { + //makeMutable(); // Manually inlined here for speed reasons. + if (base.frozen) + base = base.getMutable(); + base.addPath(g); + } + + void addPath(path[] g) { + //makeMutable(); // Manually inlined here for speed reasons. + if (base.frozen) + base = base.getMutable(); + base.addPath(g); + } + + void addPath(path g, pen p) { + //makeMutable(); // Manually inlined here for speed reasons. + if (base.frozen) + base = base.getMutable(); + base.addPath(g, p); + } + + public bool userBoundsAreSet() { + return base.userBoundsAreSet(); + } + public pair userMin() { + return base.userMin(); + } + public pair userMax() { + return base.userMax(); + } + public void alterUserBound(string which, real val) { + makeMutable(); + base.alterUserBound(which, val); + } + + void xclip(real Min, real Max) { + makeMutable(); + base.xclip(Min,Max); + } + + void yclip(real Min, real Max) { + makeMutable(); + base.yclip(Min,Max); + } + + void clip(pair Min, pair Max) { + // TODO: If the user bounds have been manually altered, they may be + // incorrect after the clip. + xclip(Min.x,Max.x); + yclip(Min.y,Max.y); + } + + pair min(transform t) { + return base.min(t); + } + + pair max(transform t) { + return base.max(t); + } + + transform scaling(real xsize, real ysize, + real xunitsize, real yunitsize, + bool keepAspect, bool warn) { + return base.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn); + } +} + +bounds operator *(transform t, bounds b) { + return b.transformed(t); +} diff --git a/Build/source/utils/asymptote/base/plain_constants.asy b/Build/source/utils/asymptote/base/plain_constants.asy index d1d11b1b1c5..d41cfb770b8 100644 --- a/Build/source/utils/asymptote/base/plain_constants.asy +++ b/Build/source/utils/asymptote/base/plain_constants.asy @@ -63,6 +63,8 @@ restricted file stdout=output(""); void none(file file) {} void endl(file file) {write(file,'\n',flush);} void newl(file file) {write(file,'\n');} +void DOSendl(file file) {write(file,'\r\n',flush);} +void DOSnewl(file file) {write(file,'\r\n');} void tab(file file) {write(file,'\t');} void comma(file file) {write(file,',');} typedef void suffix(file); diff --git a/Build/source/utils/asymptote/base/plain_pens.asy b/Build/source/utils/asymptote/base/plain_pens.asy index 5b0bdbd5879..fcf9d31e638 100644 --- a/Build/source/utils/asymptote/base/plain_pens.asy +++ b/Build/source/utils/asymptote/base/plain_pens.asy @@ -17,8 +17,8 @@ void defaultpen(real w) {defaultpen(linewidth(w));} pen operator +(pen p, real w) {return p+linewidth(w);} pen operator +(real w, pen p) {return linewidth(w)+p;} -pen Dotted=dotted+1.0; -pen Dotted(pen p=currentpen) {return dotted+2*linewidth(p);} +pen Dotted(pen p=currentpen) {return linetype(new real[] {0,3})+2*linewidth(p);} +pen Dotted=Dotted(); restricted pen squarecap=linecap(0); restricted pen roundcap=linecap(1); @@ -129,7 +129,7 @@ pen palegrey=palegray; pen lightgrey=lightgray; pen mediumgrey=mediumgray; pen grey=gray; -pen heavygrey=gray; +pen heavygrey=heavygray; pen deepgrey=deepgray; pen darkgrey=darkgray; 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) diff --git a/Build/source/utils/asymptote/base/plain_prethree.asy b/Build/source/utils/asymptote/base/plain_prethree.asy new file mode 100644 index 00000000000..595bac2fad4 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_prethree.asy @@ -0,0 +1,224 @@ +// Critical definitions for transform3 needed by projection and picture. + +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; +} + +real camerafactor=2; // Factor used for camera adjustment. + +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)); + } + + + // This is redefined here to make projection as self-contained as possible. + static private real sqrtEpsilon = sqrt(realEpsilon); + + // 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; + diff --git a/Build/source/utils/asymptote/base/plain_scaling.asy b/Build/source/utils/asymptote/base/plain_scaling.asy new file mode 100644 index 00000000000..fd2dbae07c0 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_scaling.asy @@ -0,0 +1,246 @@ +real expansionfactor=sqrt(2); + +// 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; + } +} + +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; +} + +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); + } +} + +// 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; +} + +// Calculate the sizing constants for the given array and maximum size. +real calculateScaling(string dir, coord[] m, coord[] M, 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); + } + + 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) { + // TODO: Could just be return a; + 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(coord[] coords) { + for(var coord : coords) + if(coord.user != 0) return false; + return true; + } + + if((userzero(m) && userzero(M)) || size >= infinity) return 1; + + warning("cannotfit","cannot fit picture to "+dir+"size "+(string) size + +"...enlarging..."); + + return calculateScaling(dir,m,M,expansionfactor*size,warn); + } +} + +real calculateScaling(string dir, coord[] coords, real size, bool warn=true) +{ + coord[] m=maxcoords(coords,operator >=); + coord[] M=maxcoords(coords,operator <=); + + return calculateScaling(dir, m, M, size, warn); +} diff --git a/Build/source/utils/asymptote/base/plain_shipout.asy b/Build/source/utils/asymptote/base/plain_shipout.asy index e96a3a8acb1..ccf94e6c9fd 100644 --- a/Build/source/utils/asymptote/base/plain_shipout.asy +++ b/Build/source/utils/asymptote/base/plain_shipout.asy @@ -1,6 +1,8 @@ // Default file prefix used for inline LaTeX mode string defaultfilename; +string[] file3; + string outprefix(string prefix=defaultfilename) { return stripextension(prefix != "" ? prefix : outname()); } @@ -77,7 +79,8 @@ void shipout(string prefix=defaultfilename, frame f, int limit=2000; if(abs(m.x) > limit || abs(m.y) > limit) f=shift(-m)*f; - shipout(prefix,f,currentpatterns,format,wait,view,xformStack.pop0); + shipout(prefix,f,currentpatterns,format,wait,view, + xformStack.empty() ? null : xformStack.pop0); shipped=true; } @@ -91,8 +94,17 @@ void shipout(string prefix=defaultfilename, picture pic=currentpicture, bool inlinetex=settings.inlinetex; bool prc=prc(format); bool empty3=pic.empty3(); - if(prc && !empty3) + if(prc && !empty3) { + if(settings.render == 0) { + string image=outprefix(prefix)+"+"+(string) file3.length; + if(settings.inlineimage) image += "_0"; + settings.inlinetex=false; + settings.prc=false; + shipout(image,pic,orientation,nativeformat(),view=false,light,P); + settings.prc=true; + } settings.inlinetex=settings.inlineimage; + } frame f=pic.fit(prefix,format,view=view,options,script,light,P); if(!pic.empty2() || settings.render == 0 || prc || empty3) shipout(prefix,orientation(f),format,wait,view); diff --git a/Build/source/utils/asymptote/base/plain_strings.asy b/Build/source/utils/asymptote/base/plain_strings.asy index 138ab05ca05..98f72f460aa 100644 --- a/Build/source/utils/asymptote/base/plain_strings.asy +++ b/Build/source/utils/asymptote/base/plain_strings.asy @@ -155,16 +155,24 @@ private void notimplemented(string text) abort(text+" is not implemented for the '"+settings.tex+"' TeX engine"); } +string jobname(string name) +{ + int pos=rfind(name,"-"); + return pos >= 0 ? "\ASYprefix\jobname"+substr(name,pos) : name; +} + string graphic(string name, string options="") { if(latex()) { if(options != "") options="["+options+"]"; - if(find(name," ") < 0) - return "\includegraphics"+options+"{"+name+"}"; - else { - return "\includegraphics"+options+ - (pdf() ? "{\""+stripextension(name)+"\".pdf}" : "{\""+name+"\"}"); - } + bool pdf=pdf(); + string includegraphics="\includegraphics"+options; + if(settings.inlinetex) + return includegraphics+"{"+jobname(name)+"}"; + else + return includegraphics+ + (find(name," ") < 0 ? "{"+name+"}" : + (pdf ? "{\""+stripextension(name)+"\".pdf}" : "{\""+name+"\"}")); } if(settings.tex != "context") notimplemented("graphic"); diff --git a/Build/source/utils/asymptote/base/plain_xasy.asy b/Build/source/utils/asymptote/base/plain_xasy.asy index 162cbfb35ad..efc739d8385 100644 --- a/Build/source/utils/asymptote/base/plain_xasy.asy +++ b/Build/source/utils/asymptote/base/plain_xasy.asy @@ -113,11 +113,15 @@ struct framedTransformStack { toPush[tList[a].index]=transact(tList[a]); for(int a=0; a < toPush.length; ++a) if(!toPush.initialized(a)) - toPush[a]=transact(); + toPush[a]=transact(); report("Added"); report(toPush.length); stack.append(toPush); } + + bool empty() { + return stack.length == 0; + } } framedTransformStack xformStack; diff --git a/Build/source/utils/asymptote/base/simplex.asy b/Build/source/utils/asymptote/base/simplex.asy index bd9f69c9324..43920109ff4 100644 --- a/Build/source/utils/asymptote/base/simplex.asy +++ b/Build/source/utils/asymptote/base/simplex.asy @@ -184,8 +184,10 @@ struct problem { // Checks that constants are valid. bool validConstants() { for (int i = 0; i < rows.length; ++i) - if (rows[i].c < 0) - return false; + // Do not test the row for b, as it does not have a non-negativity + // condition. + if (i != VAR_B && rows[i].c < 0) + return false; return true; } diff --git a/Build/source/utils/asymptote/base/slide.asy b/Build/source/utils/asymptote/base/slide.asy index 092c3429145..0a3e0f65fab 100644 --- a/Build/source/utils/asymptote/base/slide.asy +++ b/Build/source/utils/asymptote/base/slide.asy @@ -401,27 +401,27 @@ void figure(string[] s, string options="", real margin=0, S[i]=graphic(s[i],options); } - display(S,margin,captions,caption,align,itempen,figuremattpen, final); + display(S,margin,captions,caption,align,itempen,figuremattpen,final); } void figure(string s, string options="", string caption="", pair align=S, pen p=itempen, pen figuremattpen=figuremattpen, bool final=true) { - figure(new string[] {s},options,caption,align,p,figuremattpen, final); + figure(new string[] {s},options,caption,align,p,figuremattpen,final); } void multifigure(string[] slist, string options="", string caption="", - pair align=S, pen p=itempen, pen figuremattpen=figuremattpen) + pair align=S, pen p=itempen, pen figuremattpen=figuremattpen, + bool step=itemstep) { - if (stepping) { - int lastnode = currentpicture.nodes.length-1; + if(step) { + int lastnode=currentpicture.nodes.length-1; for (int i=0; i<slist.length-1; ++i) { - figure(slist[i], options, caption, align, p, figuremattpen, final=false); + figure(slist[i],options,caption,align,p,figuremattpen,final=false); erasestep(lastnode); } } - figure(slist[slist.length-1], options, caption, align, p, figuremattpen, - final=true); + figure(slist[slist.length-1],options,caption,align,p,figuremattpen,final=true); if(!firststep) step(); firststep=false; @@ -429,12 +429,13 @@ void multifigure(string[] slist, string options="", string caption="", void indexedfigure(string prefix, int first, int last, string options="", string caption="", - pair align=S, pen p=itempen, pen figuremattpen=figuremattpen) + pair align=S, pen p=itempen, pen figuremattpen=figuremattpen, + bool step=itemstep) { string[] s; for(int i=first; i <= last; ++i) s.push(prefix+string(i)); - multifigure(s, options, caption, align, p, figuremattpen); + multifigure(s,options,caption,align,p,figuremattpen,step=step); } string[] codefile; diff --git a/Build/source/utils/asymptote/base/solids.asy b/Build/source/utils/asymptote/base/solids.asy index c9be44552c9..239bce4b9a0 100644 --- a/Build/source/utils/asymptote/base/solids.asy +++ b/Build/source/utils/asymptote/base/solids.asy @@ -1,4 +1,3 @@ -import three; import graph3; pen defaultbackpen=linetype(new real[] {4,4},4,scale=false); diff --git a/Build/source/utils/asymptote/base/three.asy b/Build/source/utils/asymptote/base/three.asy index 4221646f632..033461263eb 100644 --- a/Build/source/utils/asymptote/base/three.asy +++ b/Build/source/utils/asymptote/base/three.asy @@ -28,9 +28,11 @@ struct render bool closed; // use one-sided rendering? bool tessellate; // use tessellated mesh to store straight patches? - bool3 merge; // merge nodes before rendering, for lower quality - // but faster PRC rendering? - // (default=merge only transparent patches) + + bool3 merge; // merge nodes before rendering, for faster but + // lower quality PRC rendering (the value default means + // merge opaque patches only). + int sphere; // PRC sphere type (PRCsphere or NURBSsphere). // General parameters: @@ -2408,7 +2410,8 @@ triple point(frame f, triple dir) triple point(picture pic=currentpicture, triple dir, bool user=true, projection P=currentprojection) { - triple v=pic.userMin+realmult(rectify(dir),pic.userMax-pic.userMin); + triple min = pic.userMin(), max = pic.userMax(); + triple v=min+realmult(rectify(dir),max-min); return user ? v : pic.calculateTransform3(P)*v; } @@ -2509,8 +2512,6 @@ private string format(triple v, string sep=" ") return string(v.x)+sep+string(v.y)+sep+string(v.z); } -private string[] file3; - private string projection(bool infinity, real viewplanesize) { return "activeCamera=scene.cameras.getByIndex(0); @@ -2696,9 +2697,8 @@ string embed3D(string label="", string text=label, string prefix, writeJavaScript(name,lightscript+projection(P.infinity,viewplanesize)+ billboard(index,center),script); - prefix += ".prc"; if(!settings.inlinetex) - file3.push(prefix); + file3.push(prefix+".prc"); triple target=P.target; if(P.viewportshift != 0) { @@ -2730,9 +2730,11 @@ string embed3D(string label="", string text=label, string prefix, ",3Droo="+Format(abs(v))+ ",3Dbg="+Format(light.background()); if(options != "") options3 += ","+options; - if(name != "") options3 += ",3Djscript="+stripdirectory(name); + if(settings.inlinetex) + prefix=jobname(prefix); + options3 += ",3Djscript="+prefix+".js"; - return Embed(stripdirectory(prefix),options3,width,height); + return Embed(prefix+".prc",options3,width,height); } struct scene @@ -2983,7 +2985,7 @@ object embed(string label="", string text=label, string prefix=defaultfilename, } string image; - if(preview && settings.embed) { + if((preview || (prc && settings.render == 0)) && settings.embed) { image=prefix; if(settings.inlinetex) image += "_0"; image += "."+nativeformat(); diff --git a/Build/source/utils/asymptote/base/three_surface.asy b/Build/source/utils/asymptote/base/three_surface.asy index 8e48b0606a3..88e42cf6640 100644 --- a/Build/source/utils/asymptote/base/three_surface.asy +++ b/Build/source/utils/asymptote/base/three_surface.asy @@ -1435,7 +1435,8 @@ void label(frame f, Label L, triple position, align align=NoAlign, bool lighton=light.on(); for(patch S : surface(L,position,bbox=P.bboxonly).s) { draw3D(f,S,position,L.p,light,interaction); - if(render.labelfill && !lighton) // Fill subdivision cracks + // Fill subdivision cracks + if(render.labelfill && opacity(L.p) == 1 && !lighton) _draw(f,S.external(),position,L.p,interaction.type); } } else { @@ -1485,7 +1486,8 @@ void label(picture pic=currentpicture, Label L, triple position, if(is3D()) { for(patch S : surface(L,v,bbox=P.bboxonly).s) { draw3D(f,S,v,L.p,light,interaction); - if(render.labelfill && !lighton) // Fill subdivision cracks + // Fill subdivision cracks + if(render.labelfill && opacity(L.p) == 1 && !lighton) _draw(f,S.external(),v,L.p,interaction.type); } } @@ -1819,6 +1821,26 @@ void dot(picture pic=currentpicture, Label L, triple v, align align=NoAlign, label(pic,L,v,render); } +void pixel(picture pic=currentpicture, triple v, pen p=currentpen, + real width=1) +{ + real h=0.5*width; + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + triple V=t*v; + if(is3D()) + drawpixel(f,V,p,width); + if(pic != null) { + triple R=h*unit(cross(unit(P.vector()),P.up)); + pair z=project(V,P.t); + real h=0.5*abs(project(V+R,P.t)-project(V-R,P.t)); + pair r=h*(1,1)/mm; + fill(pic,box(z-r,z+r),p,false); + } + },true); + triple R=h*(1,1,1); + pic.addBox(v,v,-R,R); +} + pair minbound(triple[] A, projection P) { pair b=project(A[0],P); diff --git a/Build/source/utils/asymptote/base/trembling.asy b/Build/source/utils/asymptote/base/trembling.asy index 61e78a4fca3..6f6f5973332 100644 --- a/Build/source/utils/asymptote/base/trembling.asy +++ b/Build/source/utils/asymptote/base/trembling.asy @@ -1,246 +1,209 @@ -// Copyright (c) 2008, Philippe Ivaldi. -// http://www.piprime.fr/ +// Copyright(c) 2008, Philippe Ivaldi. +// Simplified by John Bowman 02Feb2011 +// http: //www.piprime.fr/ // trembling.asy: handwriting package for the software Asymptote. -// This program is free software ; you can redistribute it and/or modify +// This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation ; either version 3 of the License, or -// (at your option) any later version. +// the Free Software Foundation; either version 3 of the License, or +//(at your option) any later version. // This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY ; without even the implied warranty of +// WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public License -// along with this program ; if not, write to the Free Software +// along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// COMMENTARY: +// COMMENTARY: -// THANKS: +// THANKS: -// BUGS: +// BUGS: // magnetic points are experimental... -// CODE: +// CODE: -import geometry; -import stats; - -/*<asyxml><variable type="pair[]" signature="magneticPoints"><code></asyxml>*/ -pair[] magneticPoints; -/*<asyxml></code><documentation>Array of magnetic points. - When a point is magnetized, a trembled path will pass though P if the original path passes through P. - When trembling is enabled with the routine 'startTrembling' all the drawn points is added to this array. - For convenience, one can magnetize an arbitrary number of points with the routine 'magnetize'.<look href="#magnetize(...point[])"><look href="#startTrembling(real,real,real,real,bool"> - </documentation></variable></asyxml>*/ real magneticRadius=1; // unit is bp in postscript coordinates. real trembleFuzz(){return min(1e-3,magneticRadius/10);} -// real trembleFuzz(){return min(1e-1,1);} -pair[] single(pair[] P) +/* <asyxml> <variable type="real" signature="trembleAngle"> <code> </asyxml> */ +real trembleAngle=4, trembleFrequency=0.5, trembleRandom=2; +/* <asyxml> </code> <documentation> Variables used by the routine 'tremble'. + </documentation> </variable> </asyxml> */ + +struct tremble { - pair[] op; - bool allow; - for (int i=0; i < P.length-1; ++i) { - allow=true; - for (int j=i+1; j < P.length; ++j) { - if(abs(P[i]-P[j]) < magneticRadius) { - allow=false; - break; + static real test=5; + + real angle,frequency,random,fuzz; + + pair[] single(pair[] P) + { + pair[] op; + bool allow; + for(int i=0; i < P.length-1; ++i) { + allow=true; + for(int j=i+1; j < P.length; ++j) { + if(abs(P[i]-P[j]) < magneticRadius) { + allow=false; + break; + } } + if(allow) op.push(P[i]); } - if(allow) op.push(P[i]); + if(P.length > 0) op.push(P[P.length-1]); + return op; } - if(P.length > 0) op.push(P[P.length-1]); - return op; -} -/*<asyxml><function type="pair" signature="attract(pair,path,real)"><code></asyxml>*/ -real atime(pair m, path g, real fuzz=trembleFuzz()) -{/*<asyxml></code><documentation>Return the time of the nearest point of 'm' which is on the path g. - 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/ - if(length(g) == 0) return 0.0; - real[] t=intersect(m,g,fuzz); - if(t.length > 0) return t[1]; - real ot; - static real eps=sqrt(realEpsilon); - real abmax=abs(max(g)-m), abmin=abs(min(g)-m); - real initr=abs(m-midpoint(g)); - real maxR=2*max(abmax,abmin), step=eps, r=initr; - real shx=1e-4; - transform T=shift(m); - path ig; - // do { - // do {// Find a radius for intersection - // r += step; - // ig=T*scale(r)*unitcircle; - // if(ig == g) { - // T *= shift(shx,0); - // ig=T*scale(r)*unitcircle; - // } - // t=intersect(ig,g); - // } while(t.length <= 0 && r <= maxR && inside(g,ig) != 1);// - // if(t.length <= 0) { // degenerated case - // r=initr; - // T *= shift(shx,0); - // warning("atime","atime needs numerical adjustment.",position=true); - // } - // } while(t.length <= 0); - if(t.length > 0) ot=t[1]; - real rm=0, rM=r; - while(rM-rm > eps) { - r=(rm+rM)/2; - t=intersect(T*scale(r)*unitcircle,g,fuzz); - if(t.length <= 0) { - rm=r; - } else { - rM=r; - ot=t[1]; + /* <asyxml> <function type="pair" signature="attract(pair,path,real)"> <code> + </asyxml> */ + real atime(pair m, path g, real fuzz=trembleFuzz()) + {/* <asyxml> </code> <documentation> Return the time of the point on path g + nearest to 'm'. + 'fuzz' is the argument 'fuzz' of 'intersect'. </documentation> </function> + </asyxml> */ + if(length(g) == 0) return 0.0; + real[] t=intersect(m,g,fuzz); + if(t.length > 0) return t[1]; + real ot; + static real eps=sqrt(realEpsilon); + real abmax=abs(max(g)-m), abmin=abs(min(g)-m); + real initr=abs(m-midpoint(g)); + real maxR=2*max(abmax,abmin), step=eps, r=initr; + real shx=1e-4; + transform T=shift(m); + path ig; + if(t.length > 0) ot=t[1]; + real rm=0, rM=r; + while(rM-rm > eps) { + r=(rm+rM)/2; + t=intersect(T*scale(r)*unitcircle,g,fuzz); + if(t.length <= 0) { + rm=r; + } else { + rM=r; + ot=t[1]; + } } + return ot; } - return ot; -} - -/*<asyxml><function type="void" signature="magnetize(...pair[])"><code></asyxml>*/ -void magnetize(...pair[] P) -{/*<asyxml></code><documentation>Magnetize the points P. - When a point is magnetized, a trembled path will pass though P if the original path passes through P.</documentation></function></asyxml>*/ - if(magneticRadius <= 0) return; - currentpicture.uptodate=false; - currentpicture.nodes.insert(0, - new void(frame f, transform t, transform T, pair lb, pair rt) - { - for (pair PT:P) { - magneticPoints.push(t*PT); - } - }); -} -/*<asyxml><function type="void" signature="magnetize(...triangle[])"><code></asyxml>*/ -void magnetize(...triangle[] t) -{/*<asyxml></code><documentation>Magnetize the vertices of the triangles t.<look href="#magnetize(...pair[])"/></documentation></function></asyxml>*/ - for(int i=0; i < t.length; ++i) - magnetize(...new point[] {t[i].A, t[i].B, t[i].C}); -} - -void trueMagnetize(pair p) -{ - if(magneticRadius <= 0) return; - magneticPoints.push(p); -} - -/*<asyxml><function type="path" signature="addnode(path,real)"><code></asyxml>*/ -path addnode(path g, real t) -{/*<asyxml></code><documentation>Add a node to 'g' at point(g,t).</documentation></function></asyxml>*/ - real l=length(g); - real rt=t%1; - if (l==0 || (t > l && !cyclic(g)) || rt == 0) return g; - if(cyclic(g)) t=t%l; - int t0=floor(t); - int t1=t0+1; - pair z0=point(g,t0), z1=point(g,t1), - c0=postcontrol(g,t0), c1=precontrol(g,t1), - m0=(1-rt)*z0+rt*c0, m1=(1-rt)*c0+rt*c1, - m2=(1-rt)*c1+rt*z1, m3=(1-rt)*m0+rt*m1, - m4=(1-rt)*m1+rt*m2; - guide og=subpath(g,0,t0); - og=og..controls m0 and m3..point(g,t); - if(cyclic(g)) - if(t1 < l) { - og=og..controls m4 and m2..subpath(g,t1,l)&cycle; - } else og=og..controls m4 and m2..cycle; - else og=og..controls m4 and m2..subpath(g,t1,l); - return og; -} - -path addnodes(path g, real fuzz=trembleFuzz() ...pair[] P) -{ - pair[] P=single(P); - if(length(g) == 0 || P.length == 0 || magneticRadius <= 0) return g; - path og=g; - for(pair tp:P) { - real t=atime(tp,og,fuzz); - real d=abs(tp-point(og,t)); - if(d < magneticRadius) og=addnode(og,t); + /* <asyxml> <function type="path" signature="addnode(path,real)"> <code> + </asyxml> */ + path addnode(path g, real t) + {/* <asyxml> </code> <documentation> Add a node to 'g' at point(g,t). + </documentation> </function> </asyxml> */ + real l=length(g); + real rt=t % 1; + if(l == 0 || (t > l && !cyclic(g)) || rt == 0) return g; + if(cyclic(g)) t=t % l; + int t0=floor(t); + int t1=t0+1; + pair z0=point(g,t0), z1=point(g,t1), + c0=postcontrol(g,t0), c1=precontrol(g,t1), + m0=(1-rt)*z0+rt*c0, m1=(1-rt)*c0+rt*c1, + m2=(1-rt)*c1+rt*z1, m3=(1-rt)*m0+rt*m1, + m4=(1-rt)*m1+rt*m2; + guide og=subpath(g,0,t0)..controls m0 and m3..point(g,t); + if(cyclic(g)) { + if(t1 < l) + og=og..controls m4 and m2..subpath(g,t1,l)&cycle; + else og=og..controls m4 and m2..cycle; + } else og=og..controls m4 and m2..subpath(g,t1,l); + return og; } - return og; -} -/*<asyxml><function type="path" signature="addnodes(path,int)"><code></asyxml>*/ -path addnodes(path g, int n) -{/*<asyxml></code><documentation>Add 'n' nodes between each node of 'g'.</documentation></function></asyxml>*/ - real l=length(g); - if(n == 0 || l == 0) return g; - path og=g; - int np=0; - for (int i=0; i < l; ++i) { - real step=1/(n+1); - for (int j=0; j < n; ++j) { - og=addnode(og,i*(n+1)+j+step); - step=1/(n-j); + path addnodes(path g, real fuzz=trembleFuzz()...pair[] P) + { + pair[] P=single(P); + if(length(g) == 0 || P.length == 0 || magneticRadius <= 0) return g; + path og=g; + for(pair tp: P) { + real t=atime(tp,og,fuzz); + real d=abs(tp-point(og,t)); + if(d < magneticRadius) og=addnode(og,t); } + return og; } - return og; -} - - -/*<asyxml><variable type="real" signature="trembleAngle"><code></asyxml>*/ -real trembleAngle=4, trembleFrequency=0.5, trembleRandom=2;/*<asyxml></code><documentation>Variables used by the routine 'tremble'.</documentation></variable></asyxml>*/ - -/*<asyxml><function type="path" signature="tremble(path,real,real,real,real)"><code></asyxml>*/ -path tremble(path g, - real angle=trembleAngle, - real frequency=trembleFrequency, - real random=trembleRandom, - real fuzz=trembleFuzz()) -{/*<asyxml></code><documentation>Return g as it was handwriting. - The postcontrols and precontrols of the nodes of g will be rotated - by an angle proportional to 'angle' (in degrees). - If frequency < 1, floor(1/frequency) nodes will be added to g to increase the - control points. - If frequency >= 1, one point for floor(frequency) will be used to deform the path. - 'random' controls the randomized coefficient which will be multiplied 'angle'. - random is 0 means don't use randomized coefficient; - More 'random' is hight more the coefficient is hight and the trembling seems randomized.</documentation></function></asyxml>*/ - if(length(g) == 0) return g; - path tg=g; - frequency=abs(frequency); - int f=abs(floor(1/frequency)-1); - tg=addnodes(tg,f); - int frequency=floor(frequency); - int tf=(frequency == 0) ? 1 : frequency; - int l=length(tg); - guide og=point(tg,0); - random=abs(random); - int rsgn(real x){ - int d2=floor(100*x)-10*floor(10*x); - if(d2 == 0) return 1; - return 2%d2 == 0 ? 1 : -1; - } - real randf() - { - real or; - if(random != 0) - if(1%tf != 0) or=0; else { - real ur=unitrand(); - or=rsgn(ur)*angle*(1+ur^(1/random)); + /* <asyxml> <function type="path" signature="addnodes(path,int)"> <code> + </asyxml> */ + path addnodes(path g, int n) + {/* <asyxml> </code> <documentation> Add 'n' nodes between each node of 'g'. + </documentation> </function> </asyxml> */ + real l=length(g); + if(n == 0 || l == 0) return g; + path og=g; + int np=0; + for(int i=0; i < l; ++i) { + real step=1/(n+1); + for(int j=0; j < n; ++j) { + og=addnode(og,i*(n+1)+j+step); + step=1/(n-j); } - else or=rsgn(unitrand())*1.5*angle; - return or; + } + return og; } - real first=randf(), a=first; - real gle; - for(int i=1; i <= l; ++i) + /* <asyxml> <function type="path" + signature="tremble(path,real,real,real,real,pair[] )"> <code> </asyxml> */ + void operator init(real angle=trembleAngle, real frequency=trembleFrequency, + real random=trembleRandom, real fuzz=trembleFuzz()) { + this.angle=angle; + this.frequency=frequency; + this.random=random; + this.fuzz=fuzz; + } + + path deform(path g...pair[] magneticPoints) { + /* <asyxml> </code> <documentation> Return g as it was handwriting. + The postcontrols and precontrols of the nodes of g will be rotated + by an angle proportional to 'angle'(in degrees). + If frequency < 1, floor(1/frequency) nodes will be added to g to + increase the control points. + If frequency>= 1, one point for floor(frequency) will be used to deform + the path. + 'random' controls the randomized coefficient which will be multiplied + by 'angle'. + random is 0 means don't use randomized coefficient; + The higher 'random' is, the more the trembling is randomized. + </documentation> </function> </asyxml> */ + if(length(g) == 0) return g; + g=addnodes(g,fuzz*abs(max(g)-min(g))...magneticPoints); + path tg=g; + frequency=abs(frequency); + int f=abs(floor(1/frequency)-1); + tg=addnodes(tg,f); + int frequency=floor(frequency); + int tf=(frequency == 0) ? 1 : frequency; + int l=length(tg); + guide og=point(tg,0); + random=abs(random); + int rsgn(real x){ + int d2=floor(100*x)-10*floor(10*x); + if(d2 == 0) return 1; + return 2 % d2 == 0 ? 1 : -1; + } + real randf() { + real or; + if(random != 0) { + if(1 % tf != 0) or=0; + else { + real ur=unitrand(); + or=rsgn(ur)*angle*(1+ur^(1/random)); + } + } else or=rsgn(unitrand())*1.5*angle; + return or; + } + + real first=randf(); + for(int i=1; i <= l; ++i) { pair P=point(tg,i); - // gle=(i == l && (cyclic(tg))) ? first : a; - // pair post=rotate(gle,point(tg,i-1))*postcontrol(tg,i-1); - // pair pre=rotate(gle,P)*precontrol(tg,i); - a=randf(); + real a=randf(); pair post=rotate(a,point(tg,i-1))*postcontrol(tg,i-1); pair pre=rotate((a+randf())/2,P)*precontrol(tg,i); if(i == l && (cyclic(tg))) @@ -248,121 +211,6 @@ path tremble(path g, else og=og..controls post and pre..P; } - return og; -} - -typedef path pathModifier(path); -pathModifier NoModifier=new path(path g){return g;}; -pathModifier tremble(real angle=trembleAngle, - real frequency=trembleFrequency, - real random=trembleRandom, - real fuzz=trembleFuzz()) -{ - return new path(path g){return tremble(g,angle,frequency,random,fuzz);}; -} - -void orig_draw(frame f, path g, pen p=currentpen)=draw; -void tremble_draw(frame f, path g, pen p=currentpen){} - -void tremble_marknodes(picture pic=currentpicture, frame f, path g) {} -void tremble_markuniform(bool centered=false, int n, bool rotated=false) {} - -int tremble_circlenodesnumber(real r){return 5;} -int orig_circlenodesnumber(real r)=circlenodesnumber; - -int tremble_circlenodesnumber1(real r, real angle1, real angle2){return 4;} -int orig_circlenodesnumber1(real r, real angle1, real angle2)=circlenodesnumber; - -int tremble_ellipsenodesnumber(real a, real b){return 50;} -int orig_ellipsenodesnumber(real a, real b)=ellipsenodesnumber; - -int tremble_ellipsenodesnumber1(real a, real b, real angle1, real angle2, bool dir){return 20;} -int orig_ellipsenodesnumber1(real a, real b, real angle1, real angle2, bool dir)=ellipsenodesnumber; - -int tremble_parabolanodesnumber(parabola p, real angle1, real angle2){return 20;} -int orig_parabolanodesnumber(parabola p, real angle1, real angle2)=parabolanodesnumber; - -int tremble_hyperbolanodesnumber(hyperbola h, real angle1, real angle2){return 20;} -int orig_hyperbolanodesnumber(hyperbola h, real angle1, real angle2)=hyperbolanodesnumber; - -restricted bool tremblingMode=false; -/*<asyxml><function type="void" signature="startTrembling(real,real,real,real,bool"><code></asyxml>*/ -void startTrembling(real angle=trembleAngle, - real frequency=trembleFrequency, - real random=trembleRandom, - real fuzz=trembleFuzz(), - bool magnetizePoints=true) -{/*<asyxml></code><documentation>Calling this routine all drawn paths will be trembled with the givens parameters.<look href="#tremble(path,real,real,real,real)"/> - If 'magnetizePoints' is true, most dotted points are automatically magnetized.<look href="magnetize(...pair[])"/></documentation></function></asyxml>*/ - if(!tremblingMode) { - tremblingMode=true; - tremble_draw=new void(frame f, path g, pen p=currentpen) - { - if(length(g) == 0 && magnetizePoints) { - trueMagnetize(point(g,0)); - } - g=addnodes(g,fuzz*abs(max(g)-min(g)) ...magneticPoints); - orig_draw(f,tremble(g,angle,frequency,random,fuzz),p); - }; - - plain.draw=tremble_draw; - - if(magnetizePoints) { - marknodes=new void(picture pic=currentpicture, frame f, path g) - { - for(int i=0; i <= length(g); ++i) { - add(pic,f,point(g,i)); - magnetize(point(g,i)); - } - }; - dot=dot(); - - markuniform=new markroutine(bool centered=false, int n, bool rotated=false) - { - return new void(picture pic=currentpicture, frame f, path g) { - if(n <= 0) return; - void add(real x) { - real t=reltime(g,x); - add(pic,rotated ? rotate(degrees(dir(g,t)))*f : f,point(g,t)); - magnetize(point(g,t)); - } - if(centered) { - real width=1/n; - for(int i=0; i < n; ++i) add((i+0.5)*width); - } else { - if(n == 1) add(0.5); - else { - real width=1/(n-1); - for(int i=0; i < n; ++i) - add(i*width); - } - } - }; - }; - } - - circlenodesnumber=tremble_circlenodesnumber; - circlenodesnumber=tremble_circlenodesnumber1; - - ellipsenodesnumber=tremble_ellipsenodesnumber; - ellipsenodesnumber=tremble_ellipsenodesnumber1; - - parabolanodesnumber=tremble_parabolanodesnumber; - hyperbolanodesnumber=tremble_hyperbolanodesnumber; + return og; } } - -// void stopTrembling() -// { -// if(tremblingMode) { -// tremblingMode=false; -// plain.draw=orig_draw; -// // draw=orig_draw1; -// circlenodesnumber=orig_circlenodesnumber; -// circlenodesnumber=orig_circlenodesnumber1; -// ellipsenodesnumber=orig_ellipsenodesnumber; -// ellipsenodesnumber=orig_ellipsenodesnumber1; -// parabolanodesnumber=orig_parabolanodesnumber; -// hyperbolanodesnumber=orig_hyperbolanodesnumber; -// } -// } |