diff options
author | Karl Berry <karl@freefriends.org> | 2013-04-02 23:03:22 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2013-04-02 23:03:22 +0000 |
commit | 324efbd652f973cd70f42fe08c665c518995bcbe (patch) | |
tree | 818ae382dfa133f3dcceec8e12e4cbd42be8bcf4 /Build/source/utils/asymptote/base | |
parent | bbf699abe485e707133ff3ba502119f261f27269 (diff) |
asy 2.21 sources (no runtime update)
git-svn-id: svn://tug.org/texlive/trunk@29618 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/utils/asymptote/base')
22 files changed, 820 insertions, 495 deletions
diff --git a/Build/source/utils/asymptote/base/asy_filetype.vim b/Build/source/utils/asymptote/base/asy_filetype.vim new file mode 100644 index 00000000000..3b614edf12b --- /dev/null +++ b/Build/source/utils/asymptote/base/asy_filetype.vim @@ -0,0 +1,3 @@ +" Vim filetype detection file +" Language: Asymptote +au BufNewFile,BufRead *.asy setfiletype asy diff --git a/Build/source/utils/asymptote/base/bezulate.asy b/Build/source/utils/asymptote/base/bezulate.asy index 5b21d8c1605..a0f6689b504 100644 --- a/Build/source/utils/asymptote/base/bezulate.asy +++ b/Build/source/utils/asymptote/base/bezulate.asy @@ -32,7 +32,7 @@ path[][] containmentTree(path[] paths) resultj.insert(0,g); // check to see if any other groups are contained within this curve for(int k=j+1; k < result.length;) { - if(inside(g,result[k][0]) == 1) { + if(inside(g,result[k][0],zerowinding) == 1) { resultj.append(result[k]); result.delete(k); } else ++k; @@ -101,22 +101,45 @@ void connect(path[] paths, path[] result, path[] patch) real d=2*abs(max(outer)-min(outer)); while(inners.length > 0) { int curveIndex = 0; - pair direction=I*dir(inners[curveIndex],0,1); // Use outgoing direction - if(direction == 0) // Try a random direction - direction=expi(2pi*unitrand()); - pair start=point(inners[curveIndex],0); - + //pair direction=I*dir(inners[curveIndex],0,1); // Use outgoing direction + //if(direction == 0) // Try a random direction + // direction=expi(2pi*unitrand()); + //pair start=point(inners[curveIndex],0); + + // find shortest distance between a node on the inner curve and a node + // on the outer curve + + real mindist = d; + int inner_i = 0; + int outer_i = 0; + for(int ni = 0; ni < length(inners[curveIndex]); ++ni) + { + for(int no = 0; no < length(outer); ++no) + { + real dist = abs(point(inners[curveIndex],ni)-point(outer,no)); + if(dist < mindist) + { + inner_i = ni; + outer_i = no; + mindist = dist; + } + } + } + pair start=point(inners[curveIndex],inner_i); + pair end = point(outer,outer_i); + // find first intersection of line segment with outer curve - real[][] ints=intersections(start,start+d*direction,outer); + //real[][] ints=intersections(start,start+d*direction,outer); + real[][] ints=intersections(start,end,outer); assert(ints.length != 0); real endtime=ints[0][1]; // endtime is time on outer - pair end = point(outer,endtime); - + end = point(outer,endtime); // find first intersection of end--start with any inner curve - real starttime=0; // starttime is time on inners[curveIndex] + real starttime=inner_i; // starttime is time on inners[curveIndex] real earliestTime=1; for(int j=0; j < inners.length; ++j) { real[][] ints=intersections(end,start,inners[j]); + if(ints.length > 0 && ints[0][0] < earliestTime) { earliestTime=ints[0][0]; // time on end--start starttime=ints[0][1]; // time on inner curve @@ -124,49 +147,80 @@ void connect(path[] paths, path[] result, path[] patch) } } start=point(inners[curveIndex],starttime); - - real timeoffset=2; - bool found=false; - path portion; + + + bool found_forward = false; + real timeoffset_forward = 2; + path portion_forward; path[] allCurves = {outer}; allCurves.append(inners); - while(!found && timeoffset > fuzz) { - timeoffset /= 2; + while(!found_forward && timeoffset_forward > fuzz) { + timeoffset_forward /= 2; if(countIntersections(allCurves,start, - point(outer,endtime+timeoffset)) == 2) + point(outer,endtime+timeoffset_forward)) == 2) { - portion = subpath(outer,endtime,endtime+timeoffset)--start--cycle; - found=true; + portion_forward = subpath(outer,endtime,endtime+timeoffset_forward)--start--cycle; + + found_forward=true; // check if an inner curve is inside the portion - for(int k = 0; found && k < inners.length; ++k) + for(int k = 0; found_forward && k < inners.length; ++k) { if(k!=curveIndex && - inside(portion,point(inners[k],0),zerowinding)) - found = false; + inside(portion_forward,point(inners[k],0),zerowinding)) + found_forward = false; } } } - - if(!found) timeoffset=-2; - while(!found && timeoffset < -fuzz) { - timeoffset /= 2; + + bool found_backward = false; + real timeoffset_backward = -2; + path portion_backward; + while(!found_backward && timeoffset_backward < -fuzz) { + timeoffset_backward /= 2; if(countIntersections(allCurves,start, - point(outer,endtime+timeoffset))==2) + point(outer,endtime+timeoffset_backward))==2) { - portion = subpath(outer,endtime+timeoffset,endtime)--start--cycle; - found = true; + portion_backward = subpath(outer,endtime+timeoffset_backward,endtime)--start--cycle; + found_backward = true; // check if an inner curve is inside the portion - for(int k = 0; found && k < inners.length; ++k) + for(int k = 0; found_backward && k < inners.length; ++k) { if(k!=curveIndex && - inside(portion,point(inners[k],0),zerowinding)) - found = false; + inside(portion_backward,point(inners[k],0),zerowinding)) + found_backward = false; } } } - assert(found); + assert(found_forward || found_backward); + real timeoffset; + path portion; + if(found_forward && !found_backward) + { + timeoffset = timeoffset_forward; + portion = portion_forward; + } + else if(found_backward && !found_forward) + { + timeoffset = timeoffset_backward; + portion = portion_backward; + } + else // assert handles case of neither found + { + if(timeoffset_forward > -timeoffset_backward) + { + timeoffset = timeoffset_forward; + portion = portion_forward; + } + else + { + timeoffset = timeoffset_backward; + portion = portion_backward; + } + } + endtime=min(endtime,endtime+timeoffset); + // or go from timeoffset+timeoffset_backward to timeoffset+timeoffset_forward? timeoffset=abs(timeoffset); // depends on the curves having opposite orientations diff --git a/Build/source/utils/asymptote/base/binarytree.asy b/Build/source/utils/asymptote/base/binarytree.asy index 0a80aec4b66..31e0e3aab61 100644 --- a/Build/source/utils/asymptote/base/binarytree.asy +++ b/Build/source/utils/asymptote/base/binarytree.asy @@ -1,17 +1,21 @@ /* ********************************************************************** * binarytree: An Asymptote module to draw binary trees * * * - * Copyright (C) 2006 * + * Copyright(C) 2006 * * Tobias Langner tobias[at]langner[dot]nightlabs[dot]de * * * * Modified by John Bowman * * * + * Condensed mode: * + * Copyright(C) 2012 * + * Gerasimos Dimitriadis dimeg [at] intracom [dot] gr * + * * ************************************************************************ * * * This library 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. * + * version 3 of the License, or(at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * @@ -25,51 +29,105 @@ * Boston, MA 02110-1301 USA * * * * Or get it online: * - * http://www.gnu.org/copyleft/lesser.html * + * http: //www.gnu.org/copyleft/lesser.html * * * ***********************************************************************/ -/** - * default values - */ +// default values real minDistDefault=0.2cm; real nodeMarginDefault=0.1cm; -/** - * structure to represent nodes in a binary tree - */ +// structure to represent nodes in a binary tree struct binarytreeNode { int key; binarytreeNode left; binarytreeNode right; binarytreeNode parent; - - /** - * sets the left child of this node - */ + bool spans_calculated=false; + int left_span,total_left_span; + int right_span,total_right_span; + void update_spans(); + + // Get the horizontal span of the tree consisting of the current + // node plus the whole subtree that is rooted at the right child + // (condensed mode) + int getTotalRightSpan() { + if(spans_calculated == false) { + update_spans(); + } + + return total_right_span; + } + + // Get the horizontal span of the tree consisting of the current + // node plus the whole subtree that is rooted at the left child + // (condensed mode) + int getTotalLeftSpan() { + if(spans_calculated == false) { + update_spans(); + } + return total_left_span; + } + + // Get the horizontal distance between this node and its right child + // (condensed mode) + int getRightSpan() { + if(spans_calculated == false) { + update_spans(); + } + return right_span; + } + + // Get the horizontal distance between this node and its left child + // (condensed mode) + int getLeftSpan() { + if(spans_calculated == false) { + update_spans(); + } + return left_span; + } + + // Update all span figures for this node. + // condensed mode) + update_spans=new void() { + if(spans_calculated == true) + return; + + left_span=0; + total_left_span=0; + right_span=0; + total_right_span=0; + + if(left != null) { + left_span=left.getTotalRightSpan()+1; + total_left_span=left_span+left.getTotalLeftSpan(); + } + + if(right != null) { + right_span=right.getTotalLeftSpan()+1; + total_right_span=right_span+right.getTotalRightSpan(); + } + spans_calculated=true; + }; + + // set the left child of this node void setLeft(binarytreeNode left) { this.left=left; this.left.parent=this; } - /** - * sets the right child of this node - */ + // set the right child of this node void setRight(binarytreeNode right) { this.right=right; this.right.parent=this; } - /** - * returns a boolean indicating whether this node is the root - */ + // return a boolean indicating whether this node is the root bool isRoot() { return parent == null; } - /** - * Returns the level of the subtree rooted at this node. - */ + // return the level of the subtree rooted at this node. int getLevel() { if(isRoot()) return 1; @@ -77,26 +135,20 @@ struct binarytreeNode { return parent.getLevel()+1; } - /** - * sets the children of this binarytreeNode - */ + // set the children of this binarytreeNode void setChildren(binarytreeNode left, binarytreeNode right) { setLeft(left); setRight(right); } - /** - * creates a new binarytreeNode with key <key> - */ + // create a new binarytreeNode with key <key> static binarytreeNode binarytreeNode(int key) { binarytreeNode toReturn=new binarytreeNode; toReturn.key=key; return toReturn; } - /** - * returns the height of the subtree rooted at this node. - */ + // returns the height of the subtree rooted at this node. int getHeight() { if(left == null && right == null) return 1; @@ -111,43 +163,46 @@ struct binarytreeNode { binarytreeNode operator init() {return null;} -/** - * "constructor" for binarytreeNode - */ +// "constructor" for binarytreeNode binarytreeNode binarytreeNode(int key)=binarytreeNode.binarytreeNode; - -/** - * draws the tree rooted at the given <node> at the given position <pos>, with - * <height>: the height of the containing tree, - * <minDist>: the minimal horizontal distance of two nodes at the lowest level, - * <levelDist> the vertical distance between two levels, - * <nodeDiameter>: the diameter of one node. - */ +// draw the tree rooted at the given <node> at the given position <pos>, with +// <height>=the height of the containing tree, +// <minDist>=the minimal horizontal distance of two nodes at the lowest level, +// <levelDist>=the vertical distance between two levels, +// <nodeDiameter>=the diameter of one node. object draw(picture pic=currentpicture, binarytreeNode node, pair pos, int height, real minDist, real levelDist, real nodeDiameter, - pen p=currentpen) { + pen p=currentpen, bool condensed=false) { Label label=Label(math((string) node.key),pos); binarytreeNode left=node.left; binarytreeNode right=node.right; - /** - * returns the distance for two nodes at the given <level> when the - * containing tree has height <height> - * and the minimal distance between two nodes is <minDist>. - */ + // return the distance for two nodes at the given <level> when the + // containing tree has height <height> + // and the minimal distance between two nodes is <minDist> . real getDistance(int level, int height, real minDist) { return(nodeDiameter+minDist)*2^(height-level); } + // return the horiontal distance between node <n> and its left child + // (condensed mode) + real getLeftDistance(binarytreeNode n) { + return(nodeDiameter+minDist) *(real)n.getLeftSpan() * 0.5; + } + + // return the horiontal distance between node <n> and its right child + // (condensed mode) + real getRightDistance(binarytreeNode n) { + return(nodeDiameter+minDist) *(real)n.getRightSpan() * 0.5; + } + real dist=getDistance(node.getLevel(),height,minDist)/2; - /** - * draws the connection between the two nodes at the given positions - * by calculating the connection points - * and then drawing the corresponding arrow. - */ + // draw the connection between the two nodes at the given positions + // by calculating the connection points and drawing the corresponding + // arrow. void deferredDrawNodeConnection(pair parentPos, pair childPos) { pic.add(new void(frame f, transform t) { pair start,end; @@ -161,14 +216,26 @@ object draw(picture pic=currentpicture, binarytreeNode node, pair pos, } if(left != null) { - pair childPos=pos-(0,levelDist)-(dist/2,0); - draw(pic,left,childPos,height,minDist,levelDist,nodeDiameter,p); + pair childPos; + if(condensed == false) { + childPos=pos-(0,levelDist)-(dist/2,0); + } + else { + childPos=pos-(0,levelDist)-((real)getLeftDistance(node),0); + } + draw(pic,left,childPos,height,minDist,levelDist,nodeDiameter,p,condensed); deferredDrawNodeConnection(pos,childPos); } if(right != null) { - pair childPos=pos-(0,levelDist)+(dist/2,0); - draw(pic,right,childPos,height,minDist,levelDist,nodeDiameter,p); + pair childPos; + if(condensed == false) { + childPos=pos-(0,levelDist)+(dist/2,0); + } + else { + childPos=pos-(0,levelDist)+((real)getRightDistance(node),0); + } + draw(pic,right,childPos,height,minDist,levelDist,nodeDiameter,p,condensed); deferredDrawNodeConnection(pos,childPos); } @@ -199,16 +266,13 @@ int[] operator cast(key[] k) { key nil=key(0,false); -/** - * structure to represent a binary tree. - */ +// structure to represent a binary tree. struct binarytree { binarytreeNode root; int[] keys; - /** - * adds the given < key > to the tree by searching for its place and inserting it there. - */ + // add the given <key> to the tree by searching for its place and + // inserting it there. void addKey(int key) { binarytreeNode newNode=binarytreeNode(key); @@ -240,9 +304,7 @@ struct binarytree { } } - /** - * returns the height of the tree - */ + // return the height of the tree int getHeight() { if(root == null) return 0; @@ -250,9 +312,7 @@ struct binarytree { return root.getHeight(); } - /** - * adds all given keys to the tree sequentially - */ + // add all given keys to the tree sequentially void addSearchKeys(int[] keys) { for(int i=0; i < keys.length; ++i) { int key=keys[i]; @@ -283,9 +343,7 @@ struct binarytree { } - /** - * returns all key in the tree - */ + // return all key in the tree int[] getKeys() { return keys; } @@ -305,12 +363,10 @@ binarytree binarytree(...key[] keys) return bt; } -/** - * draws the given binary tree. - */ +// draw the given binary tree. void draw(picture pic=currentpicture, binarytree tree, real minDist=minDistDefault, real nodeMargin=nodeMarginDefault, - pen p=currentpen) + pen p=currentpen, bool condensed=false) { int[] keys=tree.getKeys(); @@ -322,5 +378,6 @@ void draw(picture pic=currentpicture, binarytree tree, real nodeDiameter=abs(max(f)-min(f))+2*nodeMargin; real levelDist=nodeDiameter*1.8; - draw(pic,tree.root,(0,0),tree.getHeight(),minDist,levelDist,nodeDiameter,p); + draw(pic,tree.root,(0,0),tree.getHeight(),minDist,levelDist,nodeDiameter,p, + condensed); } diff --git a/Build/source/utils/asymptote/base/embed.asy b/Build/source/utils/asymptote/base/embed.asy index 165de0b7534..8f982332d86 100644 --- a/Build/source/utils/asymptote/base/embed.asy +++ b/Build/source/utils/asymptote/base/embed.asy @@ -1,38 +1,36 @@ if(latex()) { usepackage("hyperref"); texpreamble("\hypersetup{"+settings.hyperrefOptions+"}"); - texpreamble(" -\ifx\pdfhorigin\undefined% -\usepackage[3D,dvipdfmx]{movie15} -\else% -\usepackage[3D]{movie15} -\fi% -\FPmessagesfalse% -"); + usepackage("media9","bigfiles"); } -// See http://www.ctan.org/tex-archive/macros/latex/contrib/movie15/README +// See http://www.ctan.org/tex-archive/macros/latex/contrib/media9/doc/media9.pdf // for documentation of the options. -// Embed object in pdf file -string embed(string name, string options="", real width=0, real height=0) +// Embed PRC or SWF content in pdf file +string embedplayer(string name, string text="", string options="", + real width=0, real height=0) { - if(options != "") options="["+options+"]{"; - if(width != 0) options += (string) (width/pt)+"pt"; - options += "}{"; - if(height != 0) options += (string) (height/pt)+"pt"; - return "\includemovie"+options+"}{"+name+"}"; + if(width != 0) options += ",width="+(string) (width/pt)+"pt"; + if(height != 0) options += ",height="+(string) (height/pt)+"pt"; + return "\includemedia["+options+"]{"+text+"}{"+name+"}"; } -string hyperlink(string url, string text) +// Embed media in pdf file +string embed(string name, string text="", string options="", + real width=0, real height=0) { - return "\href{"+url+"}{"+text+"}"; + return embedplayer("VPlayer.swf",text,"label="+name+ + ",activate=pageopen,addresource="+name+ + ",flashvars={source="+name+"&scaleMode=letterbox},"+ + options,width,height); } -string link(string label, string text, string options="") +string link(string label, string text="Play") { - // Run LaTeX twice to resolve references. - settings.twice=true; - if(options != "") options="["+options+"]"; - return "\movieref"+options+"{"+label+"}{"+text+"}"; + return "\PushButton[ + onclick={ + annotRM['"+label+"'].activated=true; + annotRM['"+label+"'].callAS('playPause'); + }]{\fbox{"+text+"}}"; } diff --git a/Build/source/utils/asymptote/base/external.asy b/Build/source/utils/asymptote/base/external.asy index 053f1eff4c1..f9ab5365aea 100644 --- a/Build/source/utils/asymptote/base/external.asy +++ b/Build/source/utils/asymptote/base/external.asy @@ -3,10 +3,10 @@ texpreamble("\hypersetup{"+settings.hyperrefOptions+"}"); // Embed object to be run in an external window. An image file name can be // specified; if not given one will be automatically generated. -string embed(string name, string options="", real width=0, real height=0, - string image="") +string embed(string name, string text="", string options="", + real width=0, real height=0, string image="") { - string options; // Ignore movie15 options. + string options; // Ignore passed options. if(image == "") { image=stripdirectory(stripextension(name))+"."+nativeformat(); convert(name+"[0]",image,nativeformat()); @@ -30,8 +30,8 @@ string hyperlink(string url, string text) return "\href{"+url+"}{"+text+"}"; } -string link(string url, string text, string options="") +string link(string label, string text="Play") { - return hyperlink("run:"+url,text); + return hyperlink("run:"+label,text); } diff --git a/Build/source/utils/asymptote/base/graph.asy b/Build/source/utils/asymptote/base/graph.asy index cbcea4aeae3..410c59cf13a 100644 --- a/Build/source/utils/asymptote/base/graph.asy +++ b/Build/source/utils/asymptote/base/graph.asy @@ -1451,9 +1451,9 @@ void autoscale(picture pic=currentpicture, axis axis) if(pic.scale.x.scale.logarithmic && floor(pic.userMin().x) == floor(pic.userMax().x)) { if(pic.scale.x.automin()) - pic.userMinx(floor(pic.userMin().x)); + pic.userMinx2(floor(pic.userMin().x)); if(pic.scale.x.automax()) - pic.userMaxx(ceil(pic.userMax().x)); + pic.userMaxx2(ceil(pic.userMax().x)); } } else {mx.min=mx.max=0; pic.scale.set=false;} @@ -1462,9 +1462,9 @@ void autoscale(picture pic=currentpicture, axis axis) if(pic.scale.y.scale.logarithmic && floor(pic.userMin().y) == floor(pic.userMax().y)) { if(pic.scale.y.automin()) - pic.userMiny(floor(pic.userMin().y)); + pic.userMiny2(floor(pic.userMin().y)); if(pic.scale.y.automax()) - pic.userMaxy(ceil(pic.userMax().y)); + pic.userMaxy2(ceil(pic.userMax().y)); } } else {my.min=my.max=0; pic.scale.set=false;} @@ -1611,12 +1611,13 @@ void yaxis(picture pic=currentpicture, Label L="", axis axis=XZero, // Draw x and y axes. void axes(picture pic=currentpicture, Label xlabel="", Label ylabel="", + bool extend=true, pair min=(-infinity,-infinity), pair max=(infinity,infinity), pen p=currentpen, arrowbar arrow=None, margin margin=NoMargin, bool above=false) { - xaxis(pic,xlabel,min.x,max.x,p,arrow,margin,above); - yaxis(pic,ylabel,min.y,max.y,p,arrow,margin,above); + xaxis(pic,xlabel,YZero(extend),min.x,max.x,p,arrow,margin,above); + yaxis(pic,ylabel,XZero(extend),min.y,max.y,p,arrow,margin,above); } // Draw a yaxis at x. @@ -1788,6 +1789,7 @@ picture secondaryX(picture primary=currentpicture, void f(picture)) { if(!primary.scale.set) abort(noprimary); picture pic; + size(pic,primary); if(primary.userMax().x == primary.userMin().x) return pic; f(pic); if(!pic.userSetx()) return pic; @@ -1817,6 +1819,7 @@ picture secondaryY(picture primary=currentpicture, void f(picture)) { if(!primary.scale.set) abort(noprimary); picture pic; + size(pic,primary); if(primary.userMax().y == primary.userMin().y) return pic; f(pic); if(!pic.userSety()) return pic; diff --git a/Build/source/utils/asymptote/base/graph3.asy b/Build/source/utils/asymptote/base/graph3.asy index 91e5194bea9..fd7871e1547 100644 --- a/Build/source/utils/asymptote/base/graph3.asy +++ b/Build/source/utils/asymptote/base/graph3.asy @@ -931,9 +931,9 @@ void autoscale3(picture pic=currentpicture, axis axis) if(pic.scale.z.scale.logarithmic && floor(pic.userMin().z) == floor(pic.userMax().z)) { if(pic.scale.z.automin()) - pic.userMinz(floor(pic.userMin().z)); + pic.userMinz3(floor(pic.userMin().z)); if(pic.scale.z.automax()) - pic.userMaxz(ceil(pic.userMax().z)); + pic.userMaxz3(ceil(pic.userMax().z)); } } else {mz.min=mz.max=0; pic.scale.set=false;} @@ -1199,13 +1199,14 @@ void limits(picture pic=currentpicture, triple min, triple max) // Draw x, y and z axes. void axes3(picture pic=currentpicture, Label xlabel="", Label ylabel="", Label zlabel="", + bool extend=false, triple min=(-infinity,-infinity,-infinity), triple max=(infinity,infinity,infinity), pen p=currentpen, arrowbar3 arrow=None, margin3 margin=NoMargin3) { - xaxis3(pic,xlabel,min.x,max.x,p,arrow,margin); - yaxis3(pic,ylabel,min.y,max.y,p,arrow,margin); - zaxis3(pic,zlabel,min.z,max.z,p,arrow,margin); + xaxis3(pic,xlabel,YZZero(extend),min.x,max.x,p,arrow,margin); + yaxis3(pic,ylabel,XZZero(extend),min.y,max.y,p,arrow,margin); + zaxis3(pic,zlabel,XYZero(extend),min.z,max.z,p,arrow,margin); } triple Scale(picture pic=currentpicture, triple v) @@ -1884,7 +1885,8 @@ void draw(picture pic=currentpicture, Label[] L=new Label[], interaction interaction=LabelInteraction()) { pen thin=is3D() ? thin() : defaultpen; - if(g.length > 1) + bool group=g.length > 1 && (name != "" || render.defaultnames); + if(group) begingroup3(pic,name == "" ? "contours" : name,render); for(int cnt=0; cnt < g.length; ++cnt) { guide3[] gcnt=g[cnt]; @@ -1899,7 +1901,7 @@ void draw(picture pic=currentpicture, Label[] L=new Label[], } } } - if(g.length > 1) + if(group) endgroup3(pic); } @@ -1945,7 +1947,9 @@ picture vectorfield(path3 vector(pair v), triple f(pair z), pair a, pair b, scale=max > 0 ? maxlength/max : 1; } else scale=1; - begingroup3(pic,name == "" ? "vectorfield" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(pic,name == "" ? "vectorfield" : name,render); for(int i=0; i <= nu; ++i) { real x=interp(a.x,b.x,i*du); for(int j=0; j <= nv; ++j) { @@ -1962,7 +1966,8 @@ picture vectorfield(path3 vector(pair v), triple f(pair z), pair a, pair b, } } } - endgroup3(pic); + if(group) + endgroup3(pic); return pic; } diff --git a/Build/source/utils/asymptote/base/palette.asy b/Build/source/utils/asymptote/base/palette.asy index b1d1985a323..62ca83f1c9a 100644 --- a/Build/source/utils/asymptote/base/palette.asy +++ b/Build/source/utils/asymptote/base/palette.asy @@ -92,11 +92,18 @@ bounds image(picture pic=currentpicture, real[][] f, range range=Full, initial=Scale(pic,initial); final=Scale(pic,final); - transform T=transpose ? swap : identity(); + pic.addBox(initial,final); + + transform T; + if(transpose) { + T=swap; + initial=T*initial; + final=T*final; + } + pic.add(new void(frame F, transform t) { - _image(F,f,T*initial,T*final,palette,t*T,copy=false,antialias=antialias); + _image(F,f,initial,final,palette,t*T,copy=false,antialias=antialias); },true); - pic.addBox(initial,final); return bounds; // Return bounds used for color space } @@ -131,11 +138,18 @@ void image(picture pic=currentpicture, pen[][] data, pair initial, pair final, initial=Scale(pic,initial); final=Scale(pic,final); - transform T=transpose ? swap : identity(); + pic.addBox(initial,final); + + transform T; + if(transpose) { + T=swap; + initial=T*initial; + final=T*final; + } + pic.add(new void(frame F, transform t) { - _image(F,data,T*initial,T*final,t*T,copy=false,antialias=antialias); + _image(F,data,initial,final,t*T,copy=false,antialias=antialias); },true); - pic.addBox(initial,final); } void image(picture pic=currentpicture, pen f(int, int), int width, int height, @@ -146,11 +160,21 @@ void image(picture pic=currentpicture, pen f(int, int), int width, int height, initial=Scale(pic,initial); final=Scale(pic,final); - transform T=transpose ? swap : identity(); + pic.addBox(initial,final); + + transform T; + if(transpose) { + T=swap; + int temp=width; + width=height; + height=temp; + initial=T*initial; + final=T*final; + } + pic.add(new void(frame F, transform t) { - _image(F,f,width,height,T*initial,T*final,t*T,antialias=antialias); + _image(F,f,width,height,initial,final,t*T,antialias=antialias); },true); - pic.addBox(initial,final); } bounds image(picture pic=currentpicture, pair[] z, real[] f, @@ -291,9 +315,19 @@ void palette(picture pic=currentpicture, Label L="", bounds bounds, } real[][] pdata={sequence(palette.length)}; - transform T=vertical ? swap : identity(); + transform T; + pair Tinitial,Tfinal; + if(vertical) { + T=swap; + Tinitial=T*initial; + Tfinal=T*final; + } else { + Tinitial=initial; + Tfinal=final; + } + pic.add(new void(frame f, transform t) { - _image(f,pdata,T*initial,T*final,palette,t*T,copy=false, + _image(f,pdata,Tinitial,Tfinal,palette,t*T,copy=false, antialias=antialias); },true); diff --git a/Build/source/utils/asymptote/base/plain.asy b/Build/source/utils/asymptote/base/plain.asy index e06bc66543c..14715bad918 100644 --- a/Build/source/utils/asymptote/base/plain.asy +++ b/Build/source/utils/asymptote/base/plain.asy @@ -39,8 +39,6 @@ include plain_markers; include plain_arrows; include plain_debugger; -pair viewportmargin=(0,0); // Horizontal and vertical 3D viewport margins. - typedef void exitfcn(); bool needshipout() { diff --git a/Build/source/utils/asymptote/base/plain_Label.asy b/Build/source/utils/asymptote/base/plain_Label.asy index a559ae88298..1e424ab1c0a 100644 --- a/Build/source/utils/asymptote/base/plain_Label.asy +++ b/Build/source/utils/asymptote/base/plain_Label.asy @@ -212,6 +212,9 @@ embed Rotate(pair z) { warn=false));}; } +path[] texpath(string s, pen p, bool tex=settings.tex != "none", + bool bbox=false); + struct Label { string s,size; position position; @@ -289,8 +292,11 @@ struct Label { void label(frame f, transform t=identity(), pair position, pair align) { pen p0=p == nullpen ? currentpen : p; align=length(align)*unit(rotation(t)*align); - label(f,s,size,embed(t)*shiftless(T), - t*position+align*labelmargin(p0)+shift(T)*0,align,p0); + pair S=t*position+align*labelmargin(p0)+shift(T)*0; + if(settings.tex != "none") + label(f,s,size,embed(t)*shiftless(T),S,align,p0); + else + fill(f,align(texpath(s,p0),S,align,p0),p0); } void out(frame f, transform t=identity(), pair position=position.position, @@ -352,8 +358,10 @@ struct Label { if(p != nullpen) write(file,", pen=",p); if(!defaulttransform) write(file,", transform=",T); - if(!defaulttransform3) + if(!defaulttransform3) { + write(file,", transform3=",endl); write(file,T3); + } write(file,"",suffix); } @@ -579,7 +587,7 @@ path[] texpath(Label L, bool tex=settings.tex != "none", bool bbox=false) void operator init(Label L) { - s=L.s; + s=replace(L.s,'\n',' '); fontsize=fontsize(L.p); font=font(L.p); } @@ -667,3 +675,8 @@ path[] texpath(Label L, bool tex=settings.tex != "none", bool bbox=false) return transform(pathcache[search(stringcache,stringfont(L),lexorder)],L); } + +texpath=new path[](string s, pen p, bool tex=settings.tex != "none", bool bbox=false) +{ + return texpath(Label(s,p)); +}; diff --git a/Build/source/utils/asymptote/base/plain_arrows.asy b/Build/source/utils/asymptote/base/plain_arrows.asy index 59b65aa4126..3ae60938ed1 100644 --- a/Build/source/utils/asymptote/base/plain_arrows.asy +++ b/Build/source/utils/asymptote/base/plain_arrows.asy @@ -40,8 +40,8 @@ real[] arrowbasepoints(path base, path left, path right) { real[][] Tl=transpose(intersections(left,base)); real[][] Tr=transpose(intersections(right,base)); - return new real[] {Tl.length > 0 ? Tl[0][0] : 1, - Tr.length > 0 ? Tr[0][0] : 1}; + return new real[] {Tl.length > 0 ? Tl[0][0] : 0, + Tr.length > 0 ? Tr[0][0] : 0}; } path arrowbase(path r, pair y, real t, real size) @@ -67,7 +67,7 @@ DefaultHead.head=new path(path g, position position=EndPoint, pen p=currentpen, real[] T=arrowbasepoints(base,left,right); pair denom=point(right,T[1])-y; real factor=denom != 0 ? length((point(left,T[0])-y)/denom) : 1; - path left=rotate(-angle,x)*r; + path left=rotate(-angle*factor,x)*r; path right=rotate(angle*factor,x)*r; real[] T=arrowbasepoints(base,left,right); return subpath(left,0,T[0])--subpath(right,T[1],0)&cycle; @@ -90,7 +90,7 @@ SimpleHead.head=new path(path g, position position=EndPoint, pen p=currentpen, real[] T=arrowbasepoints(base,left,right); pair denom=point(right,T[1])-y; real factor=denom != 0 ? length((point(left,T[0])-y)/denom) : 1; - path left=rotate(-angle,x)*r; + path left=rotate(-angle*factor,x)*r; path right=rotate(angle*factor,x)*r; real[] T=arrowbasepoints(base,left,right); return subpath(left,T[0],0)--subpath(right,0,T[1]); @@ -117,7 +117,7 @@ arrowhead HookHead(real dir=arrowdir, real barb=arrowbarb) real[] T=arrowbasepoints(base,left,right); pair denom=point(right,T[1])-y; real factor=denom != 0 ? length((point(left,T[0])-y)/denom) : 1; - path left=rotate(-angle,x)*r; + path left=rotate(-angle*factor,x)*r; path right=rotate(angle*factor,x)*r; real[] T=arrowbasepoints(base,left,right); left=subpath(left,0,T[0]); diff --git a/Build/source/utils/asymptote/base/plain_bounds.asy b/Build/source/utils/asymptote/base/plain_bounds.asy index 871d82f01b5..b74a6ceee6a 100644 --- a/Build/source/utils/asymptote/base/plain_bounds.asy +++ b/Build/source/utils/asymptote/base/plain_bounds.asy @@ -1,6 +1,6 @@ include plain_scaling; -// After an transformation, produce new coordinate bounds. For paths that +// After a 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, @@ -466,8 +466,9 @@ private struct freezableBounds { 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 pp : pathpenBounds) + if(size(pp.g) > 0) + acc.push(min(pp.g), max(pp.g)); for (var link : links) link.accumulateUserBounds(acc); diff --git a/Build/source/utils/asymptote/base/plain_paths.asy b/Build/source/utils/asymptote/base/plain_paths.asy index c768922ffd1..dad0d13be8e 100644 --- a/Build/source/utils/asymptote/base/plain_paths.asy +++ b/Build/source/utils/asymptote/base/plain_paths.asy @@ -115,26 +115,6 @@ void write(string s="", explicit guide[] x, suffix suffix=endl) write(stdout,s,x,suffix); } -private string nopoints="nullpath has no points"; - -pair min(explicit path[] p) -{ - if(p.length == 0) abort(nopoints); - pair minp=min(p[0]); - for(int i=1; i < p.length; ++i) - minp=minbound(minp,min(p[i])); - return minp; -} - -pair max(explicit path[] p) -{ - if(p.length == 0) abort(nopoints); - pair maxp=max(p[0]); - for(int i=1; i < p.length; ++i) - maxp=maxbound(maxp,max(p[i])); - return maxp; -} - interpolate operator ..(tensionSpecifier t) { return new guide(... guide[] a) { diff --git a/Build/source/utils/asymptote/base/plain_prethree.asy b/Build/source/utils/asymptote/base/plain_prethree.asy index 595bac2fad4..cfc0c3c7411 100644 --- a/Build/source/utils/asymptote/base/plain_prethree.asy +++ b/Build/source/utils/asymptote/base/plain_prethree.asy @@ -1,5 +1,7 @@ // Critical definitions for transform3 needed by projection and picture. +pair viewportmargin=(0.1,0.1); // Horizontal and vertical 3D viewport margins. + typedef real[][] transform3; restricted transform3 identity4=identity(4); diff --git a/Build/source/utils/asymptote/base/plain_shipout.asy b/Build/source/utils/asymptote/base/plain_shipout.asy index ccf94e6c9fd..9b30e6e1947 100644 --- a/Build/source/utils/asymptote/base/plain_shipout.asy +++ b/Build/source/utils/asymptote/base/plain_shipout.asy @@ -27,8 +27,8 @@ orientation orientation=Portrait; // Forward references to functions defined in module three. object embed3(string, frame, string, string, string, light, projection); -string Embed(string name, string options="", real width=0, real height=0); -string Link(string label, string text, string options=""); +string Embed(string name, string text="", string options="", real width=0, + real height=0); bool prc0(string format="") { diff --git a/Build/source/utils/asymptote/base/plain_strings.asy b/Build/source/utils/asymptote/base/plain_strings.asy index a466159d9c7..f4c856e8ad9 100644 --- a/Build/source/utils/asymptote/base/plain_strings.asy +++ b/Build/source/utils/asymptote/base/plain_strings.asy @@ -84,7 +84,8 @@ string verbatim(string s) } // Split a string into an array of substrings delimited by delimiter -// If delimiter is an empty string, use space delimiter, discarding duplicates. +// If delimiter is an empty string, use space delimiter but discard empty +// substrings. string[] split(string s, string delimiter="") { bool prune=false; @@ -99,11 +100,9 @@ string[] split(string s, string delimiter="") int N=length(delimiter); int n=length(s); while((i=find(s,delimiter,last)) >= 0) { - if(i >= last) + if(i > last || (i == last && !prune)) S.push(substr(s,last,i-last)); last=i+N; - if(prune) - while(substr(s,last,1) == " ") ++last; } if(n > last || (n == last && !prune)) S.push(substr(s,last,n-last)); @@ -143,7 +142,7 @@ string italic(string s) string baseline(string s, string template="\strut") { - return s != "" ? "\vphantom{"+template+"}"+s : s; + return s != "" && settings.tex != "none" ? "\vphantom{"+template+"}"+s : s; } string math(string s) @@ -221,7 +220,7 @@ string format(real x, string locale="") string phantom(string s) { - return "\phantom{"+s+"}"; + return settings.tex != "none" ? "\phantom{"+s+"}" : ""; } restricted int ocgindex=0; diff --git a/Build/source/utils/asymptote/base/solids.asy b/Build/source/utils/asymptote/base/solids.asy index e6613fc29c5..09bccd32aa9 100644 --- a/Build/source/utils/asymptote/base/solids.asy +++ b/Build/source/utils/asymptote/base/solids.asy @@ -351,7 +351,9 @@ void draw(picture pic=currentpicture, revolution r, int m=0, int n=nslice, } } - begingroup3(pic,name == "" ? "skeleton" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(pic,name == "" ? "skeleton" : name,render); pic.add(new void(frame f, transform3 t, picture pic, projection P) { drawskeleton(f,t,P); if(pic != null) @@ -360,7 +362,8 @@ void draw(picture pic=currentpicture, revolution r, int m=0, int n=nslice, frame f; drawskeleton(f,identity4,P); pic.addBox(min3(f),max3(f)); - endgroup3(pic); + if(group) + endgroup3(pic); } else { skeleton s=r.skeleton(m,n,P); if(frontpen != nullpen) { diff --git a/Build/source/utils/asymptote/base/three.asy b/Build/source/utils/asymptote/base/three.asy index 5d7ae1a8ba5..ceb8cf89574 100644 --- a/Build/source/utils/asymptote/base/three.asy +++ b/Build/source/utils/asymptote/base/three.asy @@ -6,8 +6,7 @@ if(prc0()) { if(settings.tex == "context") settings.prc=false; else { access embed; - Embed=embed.embed; - Link=embed.link; + Embed=embed.embedplayer; } } @@ -40,6 +39,9 @@ struct render real tubegranularity; // granularity for rendering tubes bool labelfill; // fill subdivision cracks in unlighted labels + bool partnames; // assign part name indices to compound objects + bool defaultnames; // assign default names to unnamed objects + static render defaultrender; void operator init(real compression=defaultrender.compression, @@ -50,7 +52,9 @@ struct render int sphere=defaultrender.sphere, real margin=defaultrender.margin, real tubegranularity=defaultrender.tubegranularity, - bool labelfill=defaultrender.labelfill) + bool labelfill=defaultrender.labelfill, + bool partnames=defaultrender.partnames, + bool defaultnames=defaultrender.defaultnames) { this.compression=compression; this.granularity=granularity; @@ -61,6 +65,8 @@ struct render this.margin=margin; this.tubegranularity=tubegranularity; this.labelfill=labelfill; + this.partnames=partnames; + this.defaultnames=defaultnames; } } @@ -76,19 +82,21 @@ defaultrender.margin=0.02; defaultrender.tubegranularity=0.005; defaultrender.sphere=NURBSsphere; defaultrender.labelfill=true; +defaultrender.partnames=false; +defaultrender.defaultnames=true; real defaultshininess=0.25; real angleprecision=1e-5; // Precision for centering perspective projections. int maxangleiterations=25; -string defaultembed3Doptions; +string defaultembed3Doptions="3Dmenu"; string defaultembed3Dscript; real defaulteyetoview=63mm/1000mm; -string partname(int i=0) +string partname(int i, render render=defaultrender) { - return string(i+1); + return render.partnames ? string(i+1) : ""; } triple O=(0,0,0); @@ -2027,7 +2035,7 @@ path3 arc(triple c, triple v1, triple v2, triple normal=O, bool direction=CCW) real t2=t2[0]; int n=length(unitcircle3); if(direction) { - if (t1 >= t2) t1 -= n; + if(t1 >= t2) t1 -= n; } else if(t2 >= t1) t2 -= n; path3 p=subpath(unitcircle3,t1,t2); @@ -2223,7 +2231,7 @@ draw=new void(frame f, path3 g, material p=currentpen, transform3 tL=shift(gL)*align(dirL); transform3 tc0=shift(c0)*align(-dirc0); transform3 tcL=shift(cL)*align(dircL); - if(linecap == 0 || linecap == 2){ + if(linecap == 0 || linecap == 2) { transform3 scale2r=scale(r,r,1); T.s.append(t0*scale2r*unitdisk); disk(tc0*scale2r); @@ -2251,7 +2259,8 @@ draw=new void(frame f, path3 g, material p=currentpen, } else _draw(f,g,q); } else _draw(f,g,q); } - if(q != nullpen) + bool group=q != nullpen && (name != "" || render.defaultnames); + if(group) begingroup3(f,name == "" ? "curve" : name,render); if(linetype(q).length == 0) drawthick(g); else { @@ -2273,7 +2282,7 @@ draw=new void(frame f, path3 g, material p=currentpen, } } } - if(q != nullpen) + if(group) endgroup3(f); } else draw(f,project(g,P),q); }; @@ -2282,11 +2291,12 @@ void draw(frame f, explicit path3[] g, material p=currentpen, light light=nolight, string name="", render render=defaultrender, projection P=currentprojection) { - if(g.length > 1) - begingroup3(f,name == "" ? "curve" : name,render); + bool group=g.length > 1 && (name != "" || render.defaultnames); + if(group) + begingroup3(f,name == "" ? "curves" : name,render); for(int i=0; i < g.length; ++i) - draw(f,g[i],p,light,partname(i),render,P); - if(g.length > 1) + draw(f,g[i],p,light,partname(i,render),render,P); + if(group) endgroup3(f); } @@ -2294,12 +2304,13 @@ void draw(picture pic=currentpicture, explicit path3[] g, material p=currentpen, margin3 margin=NoMargin3, light light=nolight, string name="", render render=defaultrender) { - if(g.length > 1) + bool group=g.length > 1 && (name != "" || render.defaultnames); + if(group) begingroup3(pic,name == "" ? "curves" : name,render); for(int i=0; i < g.length; ++i) - draw(pic,g[i],p,margin,light,partname(i),render); - if(g.length > 1) - endgroup3(pic); + draw(pic,g[i],p,margin,light,partname(i,render),render); + if(group) + endgroup3(pic); } include three_arrows; @@ -2444,12 +2455,6 @@ void add(picture dest=currentpicture, object src, pair position=0, pair align=0, plain.add(dest,src,position,align,group,filltype,above); } -string cameralink(string label, string text="View Parameters") -{ - if(!prc() || Link == null) return ""; - return Link(label,text,"3Dgetview"); -} - private struct viewpoint { triple target,camera,up; real angle; @@ -2498,13 +2503,35 @@ projection perspective(string s) return P; } +projection absorthographic(triple camera=Z, triple target=O, real roll=0) +{ + triple u=unit(target-camera); + triple w=unit(Z-u.z*u); + triple up=rotate(roll,O,u)*w; + projection P= + projection(camera,up,target,1,0,false,false, + new transformation(triple camera, triple up, triple target) + {return transformation(look(camera,up,target));}); + P.absolute=true; + return P; +} + +projection absperspective(triple camera=Z, triple target=O, real roll=0, + real angle=30) +{ + triple u=unit(target-camera); + triple w=unit(Z-u.z*u); + triple up=rotate(roll,O,u)*w; + projection P=perspective(camera,up,target); + P.angle=angle; + P.absolute=true; + return P; +} + private string Format(real x) { - // Work around movie15.sty division by zero bug; - // e.g. u=unit((1e-10,1e-10,0.9)); - if(abs(x) < 1e-9) x=0; assert(abs(x) < 1e17,"Number too large: "+string(x)); - return format("%.18f",x,"C"); + return format("%.9f",x,"C"); } private string Format(triple v, string sep=" ") @@ -2522,116 +2549,13 @@ private string format(triple v, string sep=" ") return string(v.x)+sep+string(v.y)+sep+string(v.z); } -private string projection(bool infinity, real viewplanesize) -{ - return "activeCamera=scene.cameras.getByIndex(0); -function asyProjection() {"+ - (infinity ? "activeCamera.projectionType=activeCamera.TYPE_ORTHOGRAPHIC;" : - "activeCamera.projectionType=activeCamera.TYPE_PERSPECTIVE;")+" -activeCamera.viewPlaneSize="+string(viewplanesize)+"; -activeCamera.binding=activeCamera.BINDING_"+(infinity ? "MAX" : "VERTICAL")+"; -} - -asyProjection(); - -handler=new CameraEventHandler(); -runtime.addEventHandler(handler); -handler.onEvent=function(event) -{ - asyProjection(); - scene.update(); -}"; -} - -private string projection(bool infinity, real viewplanesize) +private string Format(transform3 t, string sep=" ") { - return "activeCamera=scene.cameras.getByIndex(0); -function asyProjection() {"+ - (infinity ? "activeCamera.projectionType=activeCamera.TYPE_ORTHOGRAPHIC;" : - "activeCamera.projectionType=activeCamera.TYPE_PERSPECTIVE;")+" -activeCamera.viewPlaneSize="+string(viewplanesize)+"; -activeCamera.binding=activeCamera.BINDING_"+(infinity ? "MAX" : "VERTICAL")+"; -} - -asyProjection(); - -handler=new CameraEventHandler(); -runtime.addEventHandler(handler); -handler.onEvent=function(event) -{ - asyProjection(); - scene.update(); -}"; -} - -private string billboard(int[] index, triple[] center) -{ - if(index.length == 0) return ""; - string s=" -var zero=new Vector3(0,0,0); -var nodes=scene.nodes; -var count=nodes.count; - -var index=new Array(); -for(i=0; i < count; i++) { - var node=nodes.getByIndex(i); - var name=node.name; - end=name.lastIndexOf(\".\")-1; - if(end > 0) { - if(name.substr(end,1) == \"\001\") { - start=name.lastIndexOf(\"-\")+1; - n=end-start; - if(n > 0) { - index[name.substr(start,n)]=i; - node.name=name.substr(0,start-1); - } - } - } -} - -var center=new Array( -"; - for(int i=0; i < center.length; ++i) - s += "Vector3("+format(center[i],",")+"), -"; - s += "); - -billboardHandler=new RenderEventHandler(); -billboardHandler.onEvent=function(event) -{ - var camera=scene.cameras.getByIndex(0); - var position=camera.position; - var direction=position.subtract(camera.targetPosition); - var up=camera.up.subtract(position); - - function f(i,k) { - j=index[i]; - if(j >= 0) { - var node=nodes.getByIndex(j); - var name=node.name; - var R=Matrix4x4(); - R.setView(zero,direction,up); - var c=center[k]; - var T=node.transform; - T.setIdentity(); - T.translateInPlace(c.scale(-1)); - T.multiplyInPlace(R); - T.translateInPlace(c); - } - } -"; - for(int i=0; i < index.length; ++i) - s += "f("+string(i)+","+string(index[i])+"); -"; - s += " - runtime.refresh(); -} - -runtime.addEventHandler(billboardHandler); - -runtime.refresh(); -"; - return s; + return + Format(t[0][0])+sep+Format(t[1][0])+sep+Format(t[2][0])+sep+ + Format(t[0][1])+sep+Format(t[1][1])+sep+Format(t[2][1])+sep+ + Format(t[0][2])+sep+Format(t[1][2])+sep+Format(t[2][2])+sep+ + Format(t[0][3])+sep+Format(t[1][3])+sep+Format(t[2][3]); } string lightscript(light light) { @@ -2675,11 +2599,12 @@ pair viewportmargin(pair lambda) return maxbound(0.5*(viewportsize-lambda),viewportmargin); } -string embed3D(string label="", string text=label, string prefix, +string embed3D(string prefix, string label=prefix, string text=label, frame f, string format="", real width=0, real height=0, string options="", string script="", - light light=currentlight, projection P=currentprojection) + light light=currentlight, projection P=currentprojection, + real viewplanesize=0) { if(!prc(format) || Embed == null) return ""; @@ -2688,63 +2613,53 @@ string embed3D(string label="", string text=label, string prefix, if(script == "") script=defaultembed3Dscript; - // Adobe Reader doesn't appear to support user-specified viewport lights. - string lightscript=light.on() && light.viewport ? "" : lightscript(light); - - real viewplanesize; if(P.infinity) { - triple lambda=max3(f)-min3(f); - pair margin=viewportmargin((lambda.x,lambda.y)); - viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/P.zoom; + if(viewplanesize==0) { + triple lambda=max3(f)-min3(f); + pair margin=viewportmargin((lambda.x,lambda.y)); + viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/P.zoom; + } } else if(!P.absolute) P.angle=2*aTan(Tan(0.5*P.angle)); - int[] index; - triple[] center; - shipout3(prefix,f,index,center); + shipout3(prefix,f); string name=prefix+".js"; - writeJavaScript(name,lightscript+projection(P.infinity,viewplanesize)+ - billboard(index,center),script); + // Adobe Reader doesn't appear to support user-specified viewport lights. + bool lightscript=light.on() && !light.viewport; + if(lightscript) + writeJavaScript(name,lightscript(light),script); if(!settings.inlinetex) file3.push(prefix+".prc"); - triple target=P.target; - if(P.viewportshift != 0) { - triple lambda=max3(f)-min3(f); - target -= (P.viewportshift.x*lambda.x/P.zoom, - P.viewportshift.y*lambda.y/P.zoom,0); - } + static transform3 flipxz=xscale3(-1)*zscale3(-1); + transform3 inv=inverse(flipxz*P.T.modelview); - triple v=P.vector(); - triple u=unit(v); - triple w=Z-u.z*u; - real roll; - if(abs(w) > sqrtEpsilon) { - w=unit(w); - triple up=unit(perp(P.up,u)); - roll=degrees(acos1(dot(up,w)))*sgn(dot(cross(up,w),u)); - } else roll=0; - - string options3=light.viewport ? "3Dlights=Headlamp" : "3Dlights=File"; + string options3="3Dlights="+ + (light.on() ? (light.viewport ? "Headlamp" : "File") : "None"); if(defaultembed3Doptions != "") options3 += ","+defaultembed3Doptions; + if((settings.render < 0 || !settings.embed) && settings.auto3D) - options3 += ",poster"; - options3 += ",text={"+text+"},label="+label+ - ",toolbar="+(settings.toolbar ? "true" : "false")+ - ",3Daac="+Format(P.angle)+ - ",3Dc2c="+Format(u)+ - ",3Dcoo="+Format(target)+ - ",3Droll="+Format(roll)+ - ",3Droo="+Format(abs(v))+ + options3 += ",activate=pagevisible"; + options3 += ",3Dtoolbar="+(settings.toolbar ? "true" : "false")+ + ",label="+label+ + (P.infinity ? ",3Dortho="+Format(1/viewplanesize) : + ",3Daac="+Format(P.angle))+ + ",3Dc2w="+Format(inv)+ + ",3Droo="+Format(abs(P.vector()))+ + ",3Dpsob="+(P.infinity ? "Max" : "H")+ ",3Dbg="+Format(light.background()); if(options != "") options3 += ","+options; if(settings.inlinetex) prefix=jobname(prefix); - options3 += ",3Djscript="+prefix+".js"; + if(lightscript) + options3 += ",add3Djscript="+prefix+".js"; + options3 += ",add3Djscript=asylabels.js"; - return Embed(prefix+".prc",options3,width,height); + return text == "" ? Embed(prefix+".prc","",options3,width,height) : + "\hbox to 0pt{"+text+"\hss}"+Embed(prefix+".prc","\phantom{"+text+"}", + options3); } struct scene @@ -2895,9 +2810,9 @@ struct scene } } -object embed(string label="", string text=label, string prefix=defaultfilename, - scene S, string format="", bool view=true, string options="", - string script="", light light=currentlight) +object embed(string prefix=outprefix(), string label=prefix, + string text=label, scene S, string format="", bool view=true, + string options="", string script="", light light=currentlight) { object F; transform3 modelview; @@ -2905,15 +2820,17 @@ object embed(string label="", string text=label, string prefix=defaultfilename, transform3 tinv=inverse(S.t); projection Q; + triple orthoshift; modelview=P.T.modelview; + transform3 inv; if(P.absolute) { Q=modelview*P; + inv=inverse(modelview); } else { triple target=P.target; S.f=modelview*S.f; P=modelview*P; Q=P.copy(); - light=modelview*light; if(P.infinity) { triple m=min3(S.f); @@ -2922,7 +2839,9 @@ object embed(string label="", string text=label, string prefix=defaultfilename, S.viewportmargin=viewportmargin((lambda.x,lambda.y)); S.width=ceil(lambda.x+2*S.viewportmargin.x); S.height=ceil(lambda.y+2*S.viewportmargin.y); - S.f=shift((-0.5(m.x+M.x),-0.5*(m.y+M.y),0))*S.f; // Eye will be at (0,0,0) + orthoshift=(-0.5(m.x+M.x),-0.5*(m.y+M.y),0); + S.f=shift(orthoshift)*S.f; // Eye will be at (0,0,0) + inv=inverse(modelview); } else { if(P.angle == 0) { P.angle=S.angle(P); @@ -2930,9 +2849,9 @@ object embed(string label="", string text=label, string prefix=defaultfilename, if(S.viewportmargin.y != 0) P.angle=2*aTan(Tan(0.5*P.angle)-S.viewportmargin.y/P.target.z); } + inv=inverse(modelview); Q.angle=P.angle; if(settings.verbose > 0) { - transform3 inv=inverse(modelview); if(S.adjusted) write("adjusting camera to ",tinv*inv*P.camera); target=inv*P.target; @@ -2944,12 +2863,13 @@ object embed(string label="", string text=label, string prefix=defaultfilename, write("adjusting target to ",tinv*target); } } - + light Light=modelview*light; + if(prefix == "") prefix=outprefix(); bool prc=prc(format); bool preview=settings.render > 0; if(prc) { - // The movie15.sty package cannot handle spaces or dots in filenames. + // The media9.sty package cannot handle spaces or dots in filenames. string dir=stripfile(prefix); prefix=dir+replace(stripdirectory(prefix), new string[][]{{" ","_"},{".","_"}}); @@ -2987,10 +2907,9 @@ object embed(string label="", string text=label, string prefix=defaultfilename, S.width-defaultrender.margin,S.height-defaultrender.margin, P.infinity ? 0 : 2aTan(Tan(0.5*P.angle)*P.zoom), P.zoom,m,M,P.viewportshift, - tinv*inverse(modelview)*shift(0,0,zcenter),light.background(), - P.absolute ? (modelview*light).position : light.position, - light.diffuse,light.ambient,light.specular, - light.viewport,view && !preview); + tinv*inv*shift(0,0,zcenter),Light.background(),Light.position, + Light.diffuse,Light.ambient,Light.specular, + Light.viewport,view && !preview); if(!preview) return F; } @@ -3008,19 +2927,48 @@ object embed(string label="", string text=label, string prefix=defaultfilename, image=graphic(image,"hiresbb"); } if(prc) { - if(!P.infinity && P.viewportshift != 0) + if(P.viewportshift != 0) { + if(!P.infinity) warning("offaxis", "PRC does not support off-axis projections; use pan instead of shift"); - F.L=embed3D(label,text=image,prefix,S.f,format, - S.width-2,S.height-2,options,script,light,Q); + + triple lambda=max3(S.f)-min3(S.f); + Q.target -= (P.viewportshift.x*lambda.x/P.zoom, + P.viewportshift.y*lambda.y/P.zoom,0); + } + + real viewplanesize=0; + if(P.absolute) { + if(P.infinity) { + S.f=modelview*S.f; + triple lambda=max3(S.f)-min3(S.f); + pair margin=viewportmargin((lambda.x,lambda.y)); + viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/Q.zoom; + S.f=inv*S.f; + } + Q=inv*Q; + } else { + if(P.infinity) { + triple lambda=max3(S.f)-min3(S.f); + pair margin=viewportmargin((lambda.x,lambda.y)); + viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/(Q.zoom); + transform3 t=inv*shift(-orthoshift); + Q=t*Q; + S.f=t*S.f; + } else { + Q=inv*Q; + S.f=inv*S.f; + } + } + F.L=embed3D(prefix,label,text=image,S.f,format, + S.width-2,S.height-2,options,script,light,Q,viewplanesize); } return F; } -object embed(string label="", string text=label, - string prefix=defaultfilename, - picture pic, string format="", +object embed(string prefix=outprefix(), string label=prefix, + string text=label, picture pic, string format="", real xsize=pic.xsize, real ysize=pic.ysize, bool keepAspect=pic.keepAspect, bool view=true, string options="", string script="", light light=currentlight, @@ -3029,7 +2977,7 @@ object embed(string label="", string text=label, bool is3D=is3D(format); scene S=scene(pic,xsize,ysize,keepAspect,is3D,P); if(is3D) - return embed(label,text,prefix,S,format,view,options,script,light); + return embed(prefix,label,text,S,format,view,options,script,light); else { object F; transform T=S.pic2.scaling(xsize,ysize,keepAspect); @@ -3039,8 +2987,8 @@ object embed(string label="", string text=label, } } -object embed(string label="", string text=label, - string prefix=defaultfilename, +object embed(string prefix=outprefix(), string label=prefix, + string text=label, frame f, string format="", real width=0, real height=0, bool view=true, string options="", string script="", light light=currentlight, projection P=currentprojection) diff --git a/Build/source/utils/asymptote/base/three_arrows.asy b/Build/source/utils/asymptote/base/three_arrows.asy index 8346a5bc6c1..5b9bd45d56f 100644 --- a/Build/source/utils/asymptote/base/three_arrows.asy +++ b/Build/source/utils/asymptote/base/three_arrows.asy @@ -118,7 +118,8 @@ struct arrowhead3 static path[] align(path H, path h) { static real fuzz=1000*realEpsilon; real[][] t=intersections(H,h,fuzz*max(abs(max(h)),abs(min(h)))); - return rotate(-degrees(point(H,t[0][0])-point(H,t[1][0]),warn=false))*H; + return t.length >= 2 ? + rotate(-degrees(point(H,t[0][0])-point(H,t[1][0]),warn=false))*H : H; } } @@ -248,8 +249,8 @@ real[] arrowbasepoints(path3 base, path3 left, path3 right) { real[][] Tl=transpose(intersections(left,base)); real[][] Tr=transpose(intersections(right,base)); - return new real[] {Tl.length > 0 ? Tl[0][0] : 1, - Tr.length > 0 ? Tr[0][0] : 1}; + return new real[] {Tl.length > 0 ? Tl[0][0] : 0, + Tr.length > 0 ? Tr[0][0] : 0}; } path3 arrowbase(path3 r, triple y, real t, real size) diff --git a/Build/source/utils/asymptote/base/three_light.asy b/Build/source/utils/asymptote/base/three_light.asy index eb85ed0c3e5..73f68b62a86 100644 --- a/Build/source/utils/asymptote/base/three_light.asy +++ b/Build/source/utils/asymptote/base/three_light.asy @@ -26,6 +26,11 @@ struct material { void specular(pen q) {p[3]=q;} } +material operator init() +{ + return material(); +} + void write(file file, string s="", material x, suffix suffix=none) { write(file,s); diff --git a/Build/source/utils/asymptote/base/three_surface.asy b/Build/source/utils/asymptote/base/three_surface.asy index 7d356e45e22..0ef497f6d56 100644 --- a/Build/source/utils/asymptote/base/three_surface.asy +++ b/Build/source/utils/asymptote/base/three_surface.asy @@ -689,7 +689,7 @@ struct surface { return ucyclic() ? g&cycle : g; } - // A constructor for a possibly nonconvex cyclic path in a given plane. + // A constructor for a possibly nonconvex simple cyclic path in a given plane. void operator init(path p, triple plane(pair)=XYplane) { bool straight=piecewisestraight(p); for(path g : regularize(p)) @@ -947,54 +947,54 @@ private triple[] split(triple z0, triple c0, triple c1, triple z1, real t=0.5) // produced by a horizontal split of P triple[][][] hsplit(triple[][] P) { - // get control points in rows - triple[] P0=P[0]; - triple[] P1=P[1]; - triple[] P2=P[2]; - triple[] P3=P[3]; - - triple[] c0=split(P0[0],P0[1],P0[2],P0[3]); - triple[] c1=split(P1[0],P1[1],P1[2],P1[3]); - triple[] c2=split(P2[0],P2[1],P2[2],P2[3]); - triple[] c3=split(P3[0],P3[1],P3[2],P3[3]); - // bottom, top - return new triple[][][] { + // get control points in rows + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + triple[] c0=split(P0[0],P0[1],P0[2],P0[3]); + triple[] c1=split(P1[0],P1[1],P1[2],P1[3]); + triple[] c2=split(P2[0],P2[1],P2[2],P2[3]); + triple[] c3=split(P3[0],P3[1],P3[2],P3[3]); + // bottom, top + return new triple[][][] { {{P0[0],c0[0],c0[1],c0[2]}, {P1[0],c1[0],c1[1],c1[2]}, {P2[0],c2[0],c2[1],c2[2]}, {P3[0],c3[0],c3[1],c3[2]}}, - {{c0[2],c0[3],c0[4],P0[3]}, - {c1[2],c1[3],c1[4],P1[3]}, - {c2[2],c2[3],c2[4],P2[3]}, - {c3[2],c3[3],c3[4],P3[3]}} - }; + {{c0[2],c0[3],c0[4],P0[3]}, + {c1[2],c1[3],c1[4],P1[3]}, + {c2[2],c2[3],c2[4],P2[3]}, + {c3[2],c3[3],c3[4],P3[3]}} + }; } // Return the control points of the subpatches // produced by a vertical split of P triple[][][] vsplit(triple[][] P) { - // get control points in rows - triple[] P0=P[0]; - triple[] P1=P[1]; - triple[] P2=P[2]; - triple[] P3=P[3]; - - triple[] c0=split(P0[0],P1[0],P2[0],P3[0]); - triple[] c1=split(P0[1],P1[1],P2[1],P3[1]); - triple[] c2=split(P0[2],P1[2],P2[2],P3[2]); - triple[] c3=split(P0[3],P1[3],P2[3],P3[3]); - // left, right - return new triple[][][] { - {{P0[0],P0[1],P0[2],P0[3]}, - {c0[0],c1[0],c2[0],c3[0]}, - {c0[1],c1[1],c2[1],c3[1]}, - {c0[2],c1[2],c2[2],c3[2]}}, - {{c0[2],c1[2],c2[2],c3[2]}, - {c0[3],c1[3],c2[3],c3[3]}, - {c0[4],c1[4],c2[4],c3[4]}, - {P3[0],P3[1],P3[2],P3[3]}} - }; + // get control points in rows + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + triple[] c0=split(P0[0],P1[0],P2[0],P3[0]); + triple[] c1=split(P0[1],P1[1],P2[1],P3[1]); + triple[] c2=split(P0[2],P1[2],P2[2],P3[2]); + triple[] c3=split(P0[3],P1[3],P2[3],P3[3]); + // left, right + return new triple[][][] { + {{P0[0],P0[1],P0[2],P0[3]}, + {c0[0],c1[0],c2[0],c3[0]}, + {c0[1],c1[1],c2[1],c3[1]}, + {c0[2],c1[2],c2[2],c3[2]}}, + {{c0[2],c1[2],c2[2],c3[2]}, + {c0[3],c1[3],c2[3],c3[3]}, + {c0[4],c1[4],c2[4],c3[4]}, + {P3[0],P3[1],P3[2],P3[3]}} + }; } // Return a 2D array of the control point arrays of the subpatches @@ -1023,7 +1023,7 @@ triple[][][][] split(triple[][] P, real u=1/2, real v=1/2) triple[] c9=split(c0[4],c1[4],c2[4],c3[4],u); triple[] cA=split(P0[3],P1[3],P2[3],P3[3],u); -// {{bottom-left, top-left}, {bottom-right, top-right}} + // {{bottom-left, top-left}, {bottom-right, top-right}} return new triple[][][][] { {{{P0[0],c0[0],c0[1],c0[2]}, {c4[0],c5[0],c6[0],c7[0]}, @@ -1206,7 +1206,7 @@ interaction LabelInteraction() return settings.autobillboard ? Billboard : Embedded; } -private material material(material m, light light) +material material(material m, light light) { return light.on() || invisible((pen) m) ? m : emissive(m); } @@ -1223,8 +1223,96 @@ void draw3D(frame f, int type=0, patch s, triple center=O, material m, PRCshininess=PRCshininess(m.shininess); draw(f,s.P,center,s.straight,m.p,m.opacity,m.shininess,PRCshininess, - s.planar ? s.normal(0.5,0.5) : O,s.colors, - light.on(),interaction.type,prc); + s.planar ? s.normal(0.5,0.5) : O,s.colors,interaction.type,prc); +} + +// Draw triangles on a frame. +void draw(frame f, triple[] v, int[][] vi, + triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={}, + int[][] pi={}, light light=currentlight) +{ + if(p.length > 0) + m=mean(p); + m=material(m,light); + real PRCshininess; + if(prc()) + PRCshininess=PRCshininess(m.shininess); + draw(f,v,vi,n,ni,m.p,m.opacity,m.shininess,PRCshininess,p,pi); +} + +// Draw triangles on a picture. +void draw(picture pic=currentpicture, triple[] v, int[][] vi, + triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={}, + int[][] pi={}, light light=currentlight) +{ + bool colors=pi.length > 0; + bool normals=ni.length > 0; + if(!colors && !normals) { + n=new triple[]; + ni=new int[vi.length][3]; + triple lastnormal=O; + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + int[] nii=ni[i]; + triple normal=normal(new triple[] {v[vii[0]],v[vii[1]],v[vii[2]]}); + if(normal != lastnormal || n.length == 0) { + n.push(normal); + lastnormal=normal; + } + nii[0]=nii[1]=nii[2]=n.length-1; + } + } + + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + triple[] v=t*v; + triple[] n=t*n; + + if(is3D()) { + draw(f,v,vi,n,ni,m,p,pi,light); + if(pic != null) { + for(int[] vii : vi) + for(int viij : vii) + pic.addPoint(project(v[viij],P)); + } + } else if(pic != null) { + static int[] edges={0,0,1}; + if(colors) { + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + int[] pii=pi[i]; + gouraudshade(pic,project(v[vii[0]],P)--project(v[vii[1]],P)-- + project(v[vii[2]],P)--cycle, + new pen[] {p[pii[0]],p[pii[1]],p[pii[2]]},edges); + } + } else { + if(normals) { + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + int[] nii=ni[i]; + gouraudshade(pic,project(v[vii[0]],P)--project(v[vii[1]],P)-- + project(v[vii[2]],P)--cycle, + new pen[] {color(n[nii[0]],m,light), + color(n[nii[1]],m,light), + color(n[nii[2]],m,light)},edges); + } + } else { + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + path g=project(v[vii[0]],P)--project(v[vii[1]],P)-- + project(v[vii[2]],P)--cycle; + pen p=color(n[ni[i][0]],m,light); + fill(pic,g,p); + if(opacity(m.diffuse()) == 1) // Fill subdivision cracks + draw(pic,g,p); + } + } + } + } + },true); + + for(int[] vii : vi) + for(int viij : vii) + pic.addPoint(v[viij]); } void drawPRCsphere(frame f, transform3 t=identity4, bool half=false, material m, @@ -1273,25 +1361,30 @@ void draw(transform t=identity(), frame f, surface s, int nu=1, int nv=1, { bool is3D=is3D(); if(is3D) { - begingroup3(f,name == "" ? "surface" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "surface" : name,render); for(int i=0; i < s.s.length; ++i) draw3D(f,s.s[i],surfacepen[i],light); - endgroup3(f); + if(group) + endgroup3(f); pen modifiers=thin()+squarecap; for(int k=0; k < s.s.length; ++k) { pen meshpen=meshpen[k]; if(!invisible(meshpen)) { - begingroup3(f,meshname(name),render); + if(group) + begingroup3(f,meshname(name),render); meshpen=modifiers+meshpen; real step=nu == 0 ? 0 : 1/nu; for(int i=0; i <= nu; ++i) - draw(f,s.s[k].uequals(i*step),meshpen,meshlight,partname(i), + draw(f,s.s[k].uequals(i*step),meshpen,meshlight,partname(i,render), render); step=nv == 0 ? 0 : 1/nv; for(int j=0; j <= nv; ++j) - draw(f,s.s[k].vequals(j*step),meshpen,meshlight,partname(j), + draw(f,s.s[k].vequals(j*step),meshpen,meshlight,partname(j,render), render); - endgroup3(f); + if(group) + endgroup3(f); } } } @@ -1473,6 +1566,46 @@ private path[] path(Label L, pair z=0, projection P) shift(z)*g; } +transform3 alignshift(path3[] g, transform3 t=identity4, triple position, + triple align) +{ + if(determinant(t) == 0) return identity4; + triple m=min(g); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(g)-m); + return shift(-a); +} + +transform3 alignshift(surface s, transform3 t=identity4, triple position, + triple align) +{ + if(determinant(t) == 0) return identity4; + triple m=min(s); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(s)-m); + return shift(-a); +} + +transform3 aligntransform(path3[] g, transform3 t=identity4, triple position, + triple align, pen p=currentpen) +{ + if(determinant(t) == 0) return identity4; + triple m=min(g); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(g)-m); + return shift(position+align*labelmargin(p))*t*shift(-a); +} + +transform3 aligntransform(surface s, transform3 t=identity4, triple position, + triple align, pen p=currentpen) +{ + if(determinant(t) == 0) return identity4; + triple m=min(s); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(s)-m); + return shift(position+align*labelmargin(p))*t*shift(-a); +} + void label(frame f, Label L, triple position, align align=NoAlign, pen p=currentpen, light light=nolight, string name="", render render=defaultrender, @@ -1484,16 +1617,55 @@ void label(frame f, Label L, triple position, align align=NoAlign, L.p(p); if(interaction.targetsize && settings.render != 0) L.T=L.T*scale(abs(P.camera-position)/abs(P.vector())); + transform3 T=transform3(P); if(L.defaulttransform3) - L.T3=transform3(P); - begingroup3(f,name == "" ? L.s : name,render); + L.T3=T; + if(is3D()) { bool lighton=light.on(); - for(patch S : surface(L,position,bbox=P.bboxonly).s) { - draw3D(f,S,position,L.p,light,interaction); - // Fill subdivision cracks - if(render.labelfill && opacity(L.p) == 1 && !lighton) - _draw(f,S.external(),position,L.p,interaction.type); + if(name == "") name=L.s; + if(prc() && interaction.type == Billboard.type) { + surface s=surface(texpath(L)); + transform3 centering=L.align.is3D ? + alignshift(s,L.T3,position,L.align.dir3) : identity4; + transform3 positioning= + shift(L.align.is3D ? position+L.align.dir3*labelmargin(L.p) : position); + frame f1,f2,f3; + begingroup3(f1,name,render); + if(L.defaulttransform3) + begingroup3(f3,render,position,interaction.type); + else { + begingroup3(f2,render,position,interaction.type); + begingroup3(f3,render,position); + } + for(patch S : s.s) { + S=centering*S; + draw3D(f3,S,position,L.p,light,interaction); + // Fill subdivision cracks + if(render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f3,S.external(),position,L.p,interaction.type); + } + endgroup3(f3); + if(L.defaulttransform3) + add(f1,T*f3); + else { + add(f2,inverse(T)*L.T3*f3); + endgroup3(f2); + add(f1,T*f2); + } + endgroup3(f1); + add(f,positioning*f1); + } else { + begingroup3(f,name,render); + for(patch S : surface(L,position).s) { + triple V=L.align.is3D ? position+L.align.dir3*labelmargin(L.p) : + position; + draw3D(f,S,V,L.p,light,interaction); + // Fill subdivision cracks + if(render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f,S.external(),V,L.p,interaction.type); + } + endgroup3(f); } } else { pen p=color(L.T3*Z,L.p,light,shiftless(P.T.modelview)); @@ -1509,7 +1681,6 @@ void label(frame f, Label L, triple position, align align=NoAlign, for(patch S : surface(L,position).s) fill(f,project(S.external(),P,1),p); } - endgroup3(f); } void label(picture pic=currentpicture, Label L, triple position, @@ -1533,18 +1704,54 @@ void label(picture pic=currentpicture, Label L, triple position, if(interaction.targetsize && settings.render != 0) L.T=L.T*scale(abs(P.camera-v)/abs(P.vector())); + transform3 T=transform3(P); if(L.defaulttransform3) - L.T3=transform3(P); + L.T3=T; - begingroup3(f,name == "" ? L.s : name,render,v,interaction.type); - bool lighton=light.on(); - if(is3D()) { - for(patch S : surface(L,v,bbox=P.bboxonly).s) { - draw3D(f,S,v,L.p,light,interaction); - // Fill subdivision cracks - if(render.labelfill && opacity(L.p) == 1 && !lighton) - _draw(f,S.external(),v,L.p,interaction.type); + bool lighton=light.on(); + if(name == "") name=L.s; + if(prc() && interaction.type == Billboard.type) { + surface s=surface(texpath(L,bbox=P.bboxonly)); + transform3 centering=L.align.is3D ? + alignshift(s,L.T3,v,L.align.dir3) : identity4; + transform3 positioning= + shift(L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v); + frame f1,f2,f3; + begingroup3(f1,name,render); + if(L.defaulttransform3) + begingroup3(f3,render,v,interaction.type); + else { + begingroup3(f2,render,v,interaction.type); + begingroup3(f3,render,v); + } + for(patch S : s.s) { + S=centering*S; + draw3D(f3,S,v,L.p,light,interaction); + // Fill subdivision cracks + if(render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f3,S.external(),v,L.p,interaction.type); + } + endgroup3(f3); + if(L.defaulttransform3) + add(f1,T*f3); + else { + add(f2,inverse(T)*L.T3*f3); + endgroup3(f2); + add(f1,T*f2); + } + endgroup3(f1); + add(f,positioning*f1); + } else { + begingroup3(f,name,render); + for(patch S : surface(L,v,bbox=P.bboxonly).s) { + triple V=L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v; + draw3D(f,S,V,L.p,light,interaction); + // Fill subdivision cracks + if(render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f,S.external(),V,L.p,interaction.type); + } + endgroup3(f); } } @@ -1564,7 +1771,6 @@ void label(picture pic=currentpicture, Label L, triple position, fill(f,T*project(S.external(),P,1),p); }); } - endgroup3(f); },!L.defaulttransform3); @@ -1732,14 +1938,17 @@ void dot(frame f, triple v, material p=currentpen, { pen q=(pen) p; if(is3D()) { - begingroup3(f,name == "" ? "dot" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "dot" : name,render); real size=0.5*linewidth(dotsize(q)+q); transform3 T=shift(v)*scale3(size); for(patch s : unitsphere.s) draw3D(f,T*s,v,p,light,prc=false); if(prc()) drawPRCsphere(f,T,p,light); - endgroup3(f); + if(group) + endgroup3(f); } else dot(f,project(v,P.t),q); } @@ -1752,7 +1961,7 @@ void dot(frame f, triple[] v, material p=currentpen, light light=nolight, v=sort(v,lexorder); triple last=v[0]; - dot(f,last,p,light,name,P); + dot(f,last,p,light,name,render,P); for(int i=1; i < v.length; ++i) { triple V=v[i]; if(V != last) { @@ -1798,13 +2007,16 @@ void dot(picture pic=currentpicture, triple v, material p=currentpen, pic.add(new void(frame f, transform3 t, picture pic, projection P) { triple V=t*v; if(is3D()) { - begingroup3(f,name == "" ? "dot" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "dot" : name,render); transform3 T=shift(V)*scale3(size); for(patch s : unitsphere.s) draw3D(f,T*s,V,p,light,prc=false); if(prc()) drawPRCsphere(f,T,p,light,render); - endgroup3(f); + if(group) + endgroup3(f); } if(pic != null) dot(pic,project(V,P.t),q); @@ -1821,17 +2033,20 @@ void dot(picture pic=currentpicture, triple[] v, material p=currentpen, v=sort(v,lexorder); triple last=v[0]; - begingroup3(pic,name == "" ? "dots" : name,render); - dot(pic,last,p,light,partname(0),render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(pic,name == "" ? "dots" : name,render); + dot(pic,last,p,light,partname(0,render),render); int k=0; for(int i=1; i < v.length; ++i) { triple V=v[i]; if(V != last) { - dot(pic,V,p,light,partname(++k),render); + dot(pic,V,p,light,partname(++k,render),render); last=V; } } - endgroup3(pic); + if(group) + endgroup3(pic); } } @@ -1957,9 +2172,12 @@ void draw(picture pic=currentpicture, triple[] P, real[] knot, pic.add(new void(frame f, transform3 t, picture pic, projection Q) { if(is3D()) { triple[] P=t*P; - begingroup3(f,name == "" ? "curve" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "curve" : name,render); draw(f,P,knot,weights,p); - endgroup3(f); + if(group) + endgroup3(f); if(pic != null) pic.addBox(minbound(P,Q),maxbound(P,Q)); } @@ -1975,6 +2193,7 @@ void draw(picture pic=currentpicture, triple[][] P, real[] uknot, real[] vknot, { if(colors.length > 0) m=mean(colors); + m=material(m,light); bool lighton=light.on(); P=copy(P); uknot=copy(uknot); @@ -1983,14 +2202,17 @@ void draw(picture pic=currentpicture, triple[][] P, real[] uknot, real[] vknot, colors=copy(colors); pic.add(new void(frame f, transform3 t, picture pic, projection Q) { if(is3D()) { - begingroup3(f,name == "" ? "surface" : name,render); + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "surface" : name,render); triple[][] P=t*P; real PRCshininess; if(prc()) PRCshininess=PRCshininess(m.shininess); draw(f,P,uknot,vknot,weights,m.p,m.opacity,m.shininess,PRCshininess, - colors,lighton); - endgroup3(f); + colors); + if(group) + endgroup3(f); if(pic != null) pic.addBox(minbound(P,Q),maxbound(P,Q)); } diff --git a/Build/source/utils/asymptote/base/three_tube.asy b/Build/source/utils/asymptote/base/three_tube.asy index aa5688f081e..8b621e92dda 100644 --- a/Build/source/utils/asymptote/base/three_tube.asy +++ b/Build/source/utils/asymptote/base/three_tube.asy @@ -405,10 +405,9 @@ struct tube if(abs(dir(p,i,1)-dir(p,i,-1)) > sqrtEpsilon) { generate(subpath(p,begin,i)); triple dir=dir(p,i,-1); - s.append(shift(point(p,i))*t*align(dir)* - (dir != O ? unithemisphere : unitsphere)); - int L=length(center); - sphere(shift(point(center,L))*t*align(dir(center,L,-1)), + transform3 T=t*align(dir); + s.append(shift(point(p,i))*T*(dir != O ? unithemisphere : unitsphere)); + sphere(shift(point(center,length(center)))*T, half=straight(p,i-1) && straight(p,i)); begin=i; } |