summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/base
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2013-04-02 23:03:22 +0000
committerKarl Berry <karl@freefriends.org>2013-04-02 23:03:22 +0000
commit324efbd652f973cd70f42fe08c665c518995bcbe (patch)
tree818ae382dfa133f3dcceec8e12e4cbd42be8bcf4 /Build/source/utils/asymptote/base
parentbbf699abe485e707133ff3ba502119f261f27269 (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')
-rw-r--r--Build/source/utils/asymptote/base/asy_filetype.vim3
-rw-r--r--Build/source/utils/asymptote/base/bezulate.asy120
-rw-r--r--Build/source/utils/asymptote/base/binarytree.asy211
-rw-r--r--Build/source/utils/asymptote/base/embed.asy44
-rw-r--r--Build/source/utils/asymptote/base/external.asy10
-rw-r--r--Build/source/utils/asymptote/base/graph.asy15
-rw-r--r--Build/source/utils/asymptote/base/graph3.asy23
-rw-r--r--Build/source/utils/asymptote/base/palette.asy56
-rw-r--r--Build/source/utils/asymptote/base/plain.asy2
-rw-r--r--Build/source/utils/asymptote/base/plain_Label.asy21
-rw-r--r--Build/source/utils/asymptote/base/plain_arrows.asy10
-rw-r--r--Build/source/utils/asymptote/base/plain_bounds.asy7
-rw-r--r--Build/source/utils/asymptote/base/plain_paths.asy20
-rw-r--r--Build/source/utils/asymptote/base/plain_prethree.asy2
-rw-r--r--Build/source/utils/asymptote/base/plain_shipout.asy4
-rw-r--r--Build/source/utils/asymptote/base/plain_strings.asy11
-rw-r--r--Build/source/utils/asymptote/base/solids.asy7
-rw-r--r--Build/source/utils/asymptote/base/three.asy346
-rw-r--r--Build/source/utils/asymptote/base/three_arrows.asy7
-rw-r--r--Build/source/utils/asymptote/base/three_light.asy5
-rw-r--r--Build/source/utils/asymptote/base/three_surface.asy384
-rw-r--r--Build/source/utils/asymptote/base/three_tube.asy7
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;
}