diff options
author | Norbert Preining <norbert@preining.info> | 2024-06-23 03:02:33 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2024-06-23 03:02:33 +0000 |
commit | 6abdc79a45a3d88a2e0a33a28a364efdb1f597c5 (patch) | |
tree | 4d5737dc0a85ac1c0845b76a7900d7bb5a2f2d3d /graphics/asymptote/base | |
parent | 4012cf5682cd4d2571914ef5de72cf8b933b4b0f (diff) |
CTAN sync 202406230302
Diffstat (limited to 'graphics/asymptote/base')
-rw-r--r-- | graphics/asymptote/base/asy-mode.el | 15 | ||||
-rw-r--r-- | graphics/asymptote/base/plain_boxes.asy | 28 | ||||
-rw-r--r-- | graphics/asymptote/base/plain_picture.asy | 11 | ||||
-rw-r--r-- | graphics/asymptote/base/plain_scaling.asy | 26 | ||||
-rw-r--r-- | graphics/asymptote/base/plain_shipout.asy | 9 | ||||
-rw-r--r-- | graphics/asymptote/base/simplex2.asy | 310 | ||||
-rw-r--r-- | graphics/asymptote/base/three.asy | 11 | ||||
-rw-r--r-- | graphics/asymptote/base/v3dheadertypes.asy | 2 | ||||
-rw-r--r-- | graphics/asymptote/base/v3dtypes.asy | 2 |
9 files changed, 368 insertions, 46 deletions
diff --git a/graphics/asymptote/base/asy-mode.el b/graphics/asymptote/base/asy-mode.el index 9f6eda793c..7b6d6f5c36 100644 --- a/graphics/asymptote/base/asy-mode.el +++ b/graphics/asymptote/base/asy-mode.el @@ -1600,5 +1600,20 @@ If optional argument Force is t then force compilation." (asy-master-tex-view 'lasy-view-pdf-via-ps2pdf t t)) (define-key asy-mode-map (kbd "<C-M-S-return>") 'asy-master-tex-view-ps2pdf-f) +;; Integrate with flycheck +(with-eval-after-load 'flycheck + (flycheck-define-command-checker 'asy + "Syntax checking of asymptote files." + :command (append + (split-string (concat asy-command-location asy-command)) + '("-noV" "-o" temporary-file-name source)) + :error-patterns + ;; filename.asy: 123.45: warning: message + ;; filename.asy: 123.45: error message + '((warning line-start (file-name) ":" (* space) line (? ?\. column) ": warning: " (message) line-end) + (error line-start (file-name) ":" (* space) line (? ?\. column) ": " (message) line-end)) + :modes '(asy-mode)) + (add-to-list 'flycheck-checkers 'asy)) + (provide `asy-mode) ;;; asy-mode.el ends here diff --git a/graphics/asymptote/base/plain_boxes.asy b/graphics/asymptote/base/plain_boxes.asy index 9576caff55..0b6746736a 100644 --- a/graphics/asymptote/base/plain_boxes.asy +++ b/graphics/asymptote/base/plain_boxes.asy @@ -1,3 +1,12 @@ +void fillbox(frame dest, path g, pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + if(above == false) { + frame F; + filltype.fill(F,g,p); + prepend(dest,F); + } else filltype.fill(dest,g,p); +} + // Draw and/or fill a box on frame dest using the dimensions of frame src. path box(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, pen p=currentpen, filltype filltype=NoFill, bool above=true) @@ -6,11 +15,7 @@ path box(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, int sign=filltype == NoFill ? 1 : -1; pair h=0.5*sign*(max(p)-min(p)); path g=box(min(src)-h-z,max(src)+h+z); - frame F; - if(above == false) { - filltype.fill(F,g,p); - prepend(dest,F); - } else filltype.fill(dest,g,p); + fillbox(dest,g,p,filltype,above); return g; } @@ -28,12 +33,7 @@ path roundbox(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, (dw,b)--(a-dw,b){right}..{down} (a,b-dw)--(a,dw){down}..{left} (a-dw,0)--(dw,0){left}..{up}cycle); - - frame F; - if(above == false) { - filltype.fill(F,g,p); - prepend(dest,F); - } else filltype.fill(dest,g,p); + fillbox(dest,g,p,filltype,above); return g; } @@ -47,11 +47,7 @@ path ellipse(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, int sign=filltype == NoFill ? 1 : -1; pair h=0.5*sign*(max(p)-min(p)); path g=ellipse(0.5*(M+m),factor*D.x+h.x+xmargin,factor*D.y+h.y+ymargin); - frame F; - if(above == false) { - filltype.fill(F,g,p); - prepend(dest,F); - } else filltype.fill(dest,g,p); + fillbox(dest,g,p,filltype,above); return g; } diff --git a/graphics/asymptote/base/plain_picture.asy b/graphics/asymptote/base/plain_picture.asy index 13619e80ab..9df0e5753e 100644 --- a/graphics/asymptote/base/plain_picture.asy +++ b/graphics/asymptote/base/plain_picture.asy @@ -1,5 +1,4 @@ // Pre picture <<<1 -import plain_scaling; import plain_bounds; include plain_prethree; @@ -222,6 +221,8 @@ struct picture { // <<<1 node3[] nodes3; bool uptodate=true; + bool queueErase=false; + bool queueErase3=false; struct bounds3 { coords3 point,min,max; @@ -286,6 +287,8 @@ struct picture { // <<<1 // Erase the current picture, retaining bounds. void clear() { + queueErase=nodes.length > 0; + queueErase3=nodes3.length > 0; nodes.delete(); nodes3.delete(); legend.delete(); @@ -1587,13 +1590,11 @@ void add(picture src, bool group=true, filltype filltype=NoFill, currentpicture.add(src,group,filltype,above); } -// Fit the picture src using the identity transformation (so user -// coordinates and truesize coordinates agree) and add it about the point -// position to picture dest. +// Fit the picture src and add it about the point position to picture dest. void add(picture dest, picture src, pair position, bool group=true, filltype filltype=NoFill, bool above=true) { - add(dest,src.fit(identity()),position,group,filltype,above); + add(dest,src.fit(),position,group,filltype,true); } void add(picture src, pair position, bool group=true, filltype filltype=NoFill, diff --git a/graphics/asymptote/base/plain_scaling.asy b/graphics/asymptote/base/plain_scaling.asy index 4c6beae655..dc21462c8e 100644 --- a/graphics/asymptote/base/plain_scaling.asy +++ b/graphics/asymptote/base/plain_scaling.asy @@ -192,8 +192,6 @@ real max(real M, scaling s, coord[] c) { return M; } -import simplex; - /* Calculate the sizing constants for the given array and maximum size. Solve the two-variable linear programming problem using the simplex method. @@ -203,19 +201,16 @@ import simplex; */ real calculateScaling(string dir, coord[] m, coord[] M, real size, bool warn=true) { - real[][] A; - real[] b; - real[] c=new real[] {-1,0,0}; + from simplex2 access problem; + problem p=new problem; void addMinCoord(coord c) { // (a*user + b) + truesize >= 0: - A.push(new real[] {c.user,1,-1}); - b.push(-c.truesize); + p.addRestriction(c.user,1,c.truesize); } void addMaxCoord(coord c) { // (a*user + b) + truesize <= size: - A.push(new real[] {-c.user,-1,1}); - b.push(c.truesize-size); + p.addRestriction(-c.user,-1,size-c.truesize); } for (int i=0; i < m.length; ++i) @@ -223,17 +218,16 @@ real calculateScaling(string dir, coord[] m, coord[] M, real size, for (int i=0; i < M.length; ++i) addMaxCoord(M[i]); - int[] s=array(A.length,1); - simplex S=simplex(c,A,s,b); - - if(S.case == S.OPTIMAL) { - return S.x[0]; - } else if(S.case == S.UNBOUNDED) { + if(p.rows.length == 2) return 1; // Don't warn if there are no constraints + int status=p.optimize(); + if(status == problem.OPTIMAL) { + // TODO: Could just be return a; + return scaling.build(p.a(),p.b()).a; + } else if(status == 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; diff --git a/graphics/asymptote/base/plain_shipout.asy b/graphics/asymptote/base/plain_shipout.asy index a001ce045c..c784c5f462 100644 --- a/graphics/asymptote/base/plain_shipout.asy +++ b/graphics/asymptote/base/plain_shipout.asy @@ -122,7 +122,6 @@ void shipout(string prefix=defaultfilename, picture pic=currentpicture, string options="", string script="", light light=currentlight, projection P=currentprojection) { - pic.uptodate=true; if(!uptodate()) { bool inlinetex=settings.inlinetex; bool prc=prc(format) || settings.v3d; @@ -139,7 +138,7 @@ void shipout(string prefix=defaultfilename, picture pic=currentpicture, settings.inlinetex=settings.inlineimage; } frame f; - transform t=pic.calculateTransform(); + transform t=empty3 ? pic.calculateTransform() : identity; if(currentpicture.fitter == null) { pen background=currentlight.background; if(settings.outformat == "html" && background == nullpen) @@ -152,10 +151,14 @@ void shipout(string prefix=defaultfilename, picture pic=currentpicture, else f=pic.fit(prefix,format,view=view,options,script,light,P); - if(!prconly() && (!pic.empty2() || settings.render == 0 || prc || empty3)) + if(!prconly() && (!pic.empty2() || settings.render == 0 || prc || + pic.queueErase)) { shipout(prefix,orientation(f),format,wait,view,t); + pic.queueErase=false; + } settings.inlinetex=inlinetex; } + pic.uptodate=true; } void newpage(picture pic=currentpicture) diff --git a/graphics/asymptote/base/simplex2.asy b/graphics/asymptote/base/simplex2.asy new file mode 100644 index 0000000000..e8d073ef0b --- /dev/null +++ b/graphics/asymptote/base/simplex2.asy @@ -0,0 +1,310 @@ +/***** + * simplex.asy + * Andy Hammerlindl 2004/07/27 + * + * Solves the two-variable linear programming problem using the simplex method. + * This problem is specialized in that the second variable, "b", does not have + * a non-negativity condition, and the first variable, "a", is the quantity + * being maximized. + * Correct execution of the algorithm also assumes that the coefficient of "b" + * will be +1 or -1 in every added restriction, and that the problem can be + * initialized to a valid state by pivoting b with one of the slack + * variables. This assumption may in fact be incorrect. + *****/ + +struct problem { + typedef int var; + static var VAR_A = 0; + static var VAR_B = 1; + + static int OPTIMAL = -1; + static var UNBOUNDED = -2; + static int INVALID = -3; + + struct row { + real c, t[]; + } + + // The variables of the rows. + // Initialized for the two variable problem. + var[] v = {VAR_A, VAR_B}; + + // The rows of equalities. + row rowA() { + row r = new row; + r.c = 0; + r.t = new real[] {1, 0}; + return r; + } + row rowB() { + row r = new row; + r.c = 0; + r.t = new real[] {0, 1}; + return r; + } + row[] rows = {rowA(), rowB()}; + + // The number of original variables. + int n = rows.length; + + // Pivot the variable v[col] with vp. + void pivot(int col, var vp) + { + var vc=v[col]; + + // Recalculate rows v[col] and vp for the pivot-swap. + row rvc = rows[vc], rvp = rows[vp]; + real factor=1/rvp.t[col]; // NOTE: Handle rvp.t[col] == 0 case. + rvc.c=-rvp.c*factor; + rvp.c=0; + rvc.t=-rvp.t*factor; + rvp.t *= 0; + rvc.t[col]=factor; + rvp.t[col]=1; + + var a=min(vc,vp); + var b=max(vc,vp); + + // Recalculate the rows other than the two used for the above pivot. + for (var i = 0; i < a; ++i) { + row r=rows[i]; + real m = r.t[col]; + r.c += m*rvc.c; + r.t += m*rvc.t; + r.t[col]=m*factor; + } + for (var i = a+1; i < b; ++i) { + row r=rows[i]; + real m = r.t[col]; + r.c += m*rvc.c; + r.t += m*rvc.t; + r.t[col]=m*factor; + } + for (var i = b+1; i < rows.length; ++i) { + row r=rows[i]; + real m = r.t[col]; + r.c += m*rvc.c; + r.t += m*rvc.t; + r.t[col]=m*factor; + } + + // Relabel the vars. + v[col] = vp; + } + + // As b does not have a non-negativity condition, it must initially be + // pivoted out for a variable that does. This selects the initial + // variable to pivot with b. It also assumes that there is a valid + // solution with a == 0 to the linear programming problem, and if so, it + // picks a pivot to get to that state. In our case, a == 0 corresponds to + // a picture with the user coordinates shrunk down to zero, and if that + // doesn't fit, nothing will. + // + // If b has a minimal value, choose a pivot that will give b its minimal + // value. Otherwise, if b has maximal value, choose a pivot to give b its + // maximal value. + var initVar() + { + real min; + var argmin; + var i=2; + for (; i < rows.length; ++i) { + row r=rows[i]; + if (r.t[VAR_B] > 0) { + min=r.c/r.t[VAR_B]; + argmin=i; + break; + } + } + for (; i < rows.length; ++i) { + row r=rows[i]; + if (r.t[VAR_B] > 0) { + real val=r.c/r.t[VAR_B]; + if (val < min) { + min=val; + argmin=i; + } + } + } + + if(argmin != 0) return argmin; + + real max; + var argmax; + var i=2; + for (; i < rows.length; ++i) { + row r=rows[i]; + if (r.t[VAR_B] < 0) { + max=r.c/r.t[VAR_B]; + argmax=i; + break; + } + } + + for (; i < rows.length; ++i) { + row r=rows[i]; + if (r.t[VAR_B] < 0) { + real val=r.c/r.t[VAR_B]; + if (val > max) { + max=val; + argmax=i; + } + } + } + + if(argmax != 0) return argmax; + + return UNBOUNDED; + } + + // Initialize the linear program problem by moving into an acceptable state + // this assumes that b is unrestrained and is the second variable. + // NOTE: Works in limited cases, may be bug-ridden. + void init() + { + // Find the lowest constant term in the equations. + var lowest = 0; + for (var i = 2; i < rows.length; ++i) { + if (rows[i].c < rows[lowest].c) + lowest = i; + } + + // Pivot if necessary. + if (lowest != 0) + pivot(VAR_B, lowest); + } + + // Selects a column to pivot on. Returns OPTIMAL if the current state is + // optimal. Assumes we are optimizing the first row. + int selectColumn() + { + int i=find(rows[0].t > 0,1); + return (i >= 0) ? i : OPTIMAL; + } + + // Select the new variable associated with a pivot on the column given. + // Returns UNBOUNDED if the space is unbounded. + var selectVar(int col) + { + // We assume that the first two vars (a and b) once swapped out, won't be + // swapped back in. This finds the variable which gives the tightest + // non-negativity condition restricting our optimization. This turns + // out to be the max of c/t[col]. Note that as c is positive, and + // t[col] is negative, all c/t[col] will be negative, so we are finding + // the smallest in magnitude. + var vp=UNBOUNDED; + real max=0; + int i=2; + for (; i < rows.length; ++i) { + row r=rows[i]; + if(r.c < 0) r.c=0; // Fix any numerical precision error + if(r.t[col] < 0) { + max=r.c/r.t[col]; vp=i; + break; + } + } + for (; i < rows.length; ++i) { + row r=rows[i]; + if(r.c < 0) r.c=0; // Fix any numerical precision error + if(r.c < max*r.t[col]) { + max=r.c/r.t[col]; vp=i; + } + } + + return vp; + } + + // Checks that the rows are in a valid state. + bool valid() + { + // Checks that constants are valid. + bool validConstants() { + for (int i = 0; i < rows.length; ++i) + // 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; + } + + // Check a variable to see if its row is simple. + // NOTE: Simple rows could be optimized out, since they are not really + // used. + bool validVar(int col) { + + var vc = v[col]; + row rvc = rows[vc]; + + if (rvc.c != 0) + return false; + for (int i = 0; i < n; ++i) + if (rvc.t[i] != (i == col ? 1 : 0)) + return false; + + return true; + } + + if (!validConstants()) { + return false; + } + for (int i = 0; i < n; ++i) + if (!validVar(i)) { + return false; + } + + return true; + } + + + // Perform the algorithm to find the optimal solution. Returns OPTIMAL, + // UNBOUNDED, or INVALID (if no solution is possible). + int optimize() + { + // Put into a valid state to begin and pivot b out. + var iv=initVar(); + if (iv == UNBOUNDED) + return iv; + pivot(VAR_B, iv); + + if (!valid()) + return INVALID; + + while(true) { + int col = selectColumn(); + + if (col == OPTIMAL) + return col; + var vp = selectVar(col); + + if (vp == UNBOUNDED) + return vp; + + pivot(col, vp); + } + + // Shouldn't reach here. + return INVALID; + } + + // Add a restriction to the problem: + // t1*a + t2*b + c >= 0 + void addRestriction(real t1, real t2, real c) + { + row r = new row; + r.c = c; + r.t = new real[] {t1, t2}; + rows.push(r); + } + + // Return the value of a computed. + real a() + { + return rows[VAR_A].c; + } + + // Return the value of b computed. + real b() + { + return rows[VAR_B].c; + } +} diff --git a/graphics/asymptote/base/three.asy b/graphics/asymptote/base/three.asy index cde32f6acb..b93833f666 100644 --- a/graphics/asymptote/base/three.asy +++ b/graphics/asymptote/base/three.asy @@ -2681,7 +2681,7 @@ struct scene if(P.absolute) this.P=P.copy(); - else if(P.showtarget) + else if(P.showtarget && !pic.empty3()) draw(pic,P.target,nullpen); t=pic.scaling(xsize3,ysize3,zsize3,keepAspect,warn); @@ -2893,7 +2893,7 @@ object embed(string prefix=outprefix(), string label=prefix, triple margin=(S.viewportmargin.x,S.viewportmargin.y,0); M += margin; m -= margin; - } else if(M.z >= 0) abort("camera too close"); + } else if(M.z >= 0 && !S.pic2.empty()) abort("camera too close"); if(primitive()) format=settings.v3d ? "v3d" : settings.outformat; @@ -3038,12 +3038,15 @@ currentpicture.fitter=new frame(string prefix, picture pic, string format, light light, projection P) { frame f; bool empty3=pic.empty3(); - if(!empty3) f=embedder(new object(string prefix, string format) { + if(!empty3 || pic.queueErase3) { + f=embedder(new object(string prefix, string format) { return embed(prefix=prefix,pic,format,xsize,ysize,keepAspect,view, options,script,light,P); },prefix,format,view,light); + pic.queueErase3=false; + } - if(is3D(format) || empty3) + if(is3D(format) || pic.queueErase) add(f,pic.fit2(xsize,ysize,keepAspect)); return f; }; diff --git a/graphics/asymptote/base/v3dheadertypes.asy b/graphics/asymptote/base/v3dheadertypes.asy index 0118208970..aff3ebd34d 100644 --- a/graphics/asymptote/base/v3dheadertypes.asy +++ b/graphics/asymptote/base/v3dheadertypes.asy @@ -1,6 +1,6 @@ // Enum class for v3dheadertypes // AUTO-GENERATED from v3dheadertypes.csv -// Generated at 2024-03-23 21:31:00 +// Generated at 2024-06-21 17:45:18+00:00 struct v3dheadertypes { diff --git a/graphics/asymptote/base/v3dtypes.asy b/graphics/asymptote/base/v3dtypes.asy index b808b2ae44..97517ebae2 100644 --- a/graphics/asymptote/base/v3dtypes.asy +++ b/graphics/asymptote/base/v3dtypes.asy @@ -1,6 +1,6 @@ // Enum class for v3dtypes // AUTO-GENERATED from v3dtypes.csv -// Generated at 2024-03-23 21:31:00 +// Generated at 2024-06-21 17:45:18+00:00 struct v3dtypes { |