summaryrefslogtreecommitdiff
path: root/graphics/asymptote/base
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2024-06-23 03:02:33 +0000
committerNorbert Preining <norbert@preining.info>2024-06-23 03:02:33 +0000
commit6abdc79a45a3d88a2e0a33a28a364efdb1f597c5 (patch)
tree4d5737dc0a85ac1c0845b76a7900d7bb5a2f2d3d /graphics/asymptote/base
parent4012cf5682cd4d2571914ef5de72cf8b933b4b0f (diff)
CTAN sync 202406230302
Diffstat (limited to 'graphics/asymptote/base')
-rw-r--r--graphics/asymptote/base/asy-mode.el15
-rw-r--r--graphics/asymptote/base/plain_boxes.asy28
-rw-r--r--graphics/asymptote/base/plain_picture.asy11
-rw-r--r--graphics/asymptote/base/plain_scaling.asy26
-rw-r--r--graphics/asymptote/base/plain_shipout.asy9
-rw-r--r--graphics/asymptote/base/simplex2.asy310
-rw-r--r--graphics/asymptote/base/three.asy11
-rw-r--r--graphics/asymptote/base/v3dheadertypes.asy2
-rw-r--r--graphics/asymptote/base/v3dtypes.asy2
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
{