summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/asymptote
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2013-04-07 18:19:31 +0000
committerKarl Berry <karl@freefriends.org>2013-04-07 18:19:31 +0000
commit752012c605d34cd943795527a9738475a6958fcc (patch)
tree4ee06acdd8333a662c2d6f6ef716235053468f55 /Master/texmf-dist/asymptote
parent9789d09132f18a838e84f041b4b3aff28d3426ec (diff)
texmf -> texmf-dist: start with unique dirs from texmf
git-svn-id: svn://tug.org/texlive/trunk@29712 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/asymptote')
-rw-r--r--Master/texmf-dist/asymptote/CAD.asy367
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/CubicBezier.py101
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/UndoRedoStack.py117
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasy.py35
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasy2asy.py767
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyActions.py387
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyBezierEditor.py208
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyCodeEditor.py37
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyColorPicker.py248
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyFile.py315
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyGUIIcons.py92
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyMainWin.py1741
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyOptions.py126
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyOptionsDialog.py228
-rwxr-xr-xMaster/texmf-dist/asymptote/GUI/xasyVersion.py2
-rw-r--r--Master/texmf-dist/asymptote/animate.asy3
-rw-r--r--Master/texmf-dist/asymptote/animation.asy191
-rw-r--r--Master/texmf-dist/asymptote/annotate.asy16
-rw-r--r--Master/texmf-dist/asymptote/asy-init.el4
-rw-r--r--Master/texmf-dist/asymptote/asy-kate.sh146
-rw-r--r--Master/texmf-dist/asymptote/asy-keywords.el17
-rw-r--r--Master/texmf-dist/asymptote/asy-mode.el1590
-rw-r--r--Master/texmf-dist/asymptote/asy.vim202
-rw-r--r--Master/texmf-dist/asymptote/asymptote.py48
-rw-r--r--Master/texmf-dist/asymptote/babel.asy4
-rw-r--r--Master/texmf-dist/asymptote/bezulate.asy254
-rw-r--r--Master/texmf-dist/asymptote/binarytree.asy326
-rw-r--r--Master/texmf-dist/asymptote/bsp.asy209
-rw-r--r--Master/texmf-dist/asymptote/contour.asy1491
-rw-r--r--Master/texmf-dist/asymptote/contour3.asy481
-rw-r--r--Master/texmf-dist/asymptote/drawtree.asy101
-rw-r--r--Master/texmf-dist/asymptote/embed.asy38
-rw-r--r--Master/texmf-dist/asymptote/external.asy37
-rw-r--r--Master/texmf-dist/asymptote/feynman.asy622
-rw-r--r--Master/texmf-dist/asymptote/flowchart.asy526
-rw-r--r--Master/texmf-dist/asymptote/fontsize.asy1
-rw-r--r--Master/texmf-dist/asymptote/geometry.asy7192
-rw-r--r--Master/texmf-dist/asymptote/graph.asy2237
-rw-r--r--Master/texmf-dist/asymptote/graph3.asy2041
-rw-r--r--Master/texmf-dist/asymptote/graph_settings.asy18
-rw-r--r--Master/texmf-dist/asymptote/graph_splinetype.asy264
-rw-r--r--Master/texmf-dist/asymptote/grid3.asy412
-rw-r--r--Master/texmf-dist/asymptote/interpolate.asy140
-rw-r--r--Master/texmf-dist/asymptote/labelpath.asy28
-rw-r--r--Master/texmf-dist/asymptote/labelpath3.asy83
-rw-r--r--Master/texmf-dist/asymptote/latin1.asy2
-rw-r--r--Master/texmf-dist/asymptote/lmfit.asy881
-rw-r--r--Master/texmf-dist/asymptote/markers.asy218
-rw-r--r--Master/texmf-dist/asymptote/math.asy417
-rw-r--r--Master/texmf-dist/asymptote/metapost.asy19
-rw-r--r--Master/texmf-dist/asymptote/nopapersize.ps3
-rw-r--r--Master/texmf-dist/asymptote/obj.asy113
-rw-r--r--Master/texmf-dist/asymptote/ode.asy457
-rw-r--r--Master/texmf-dist/asymptote/palette.asy495
-rw-r--r--Master/texmf-dist/asymptote/patterns.asy102
-rw-r--r--Master/texmf-dist/asymptote/plain.asy284
-rw-r--r--Master/texmf-dist/asymptote/plain_Label.asy669
-rw-r--r--Master/texmf-dist/asymptote/plain_arcs.asy44
-rw-r--r--Master/texmf-dist/asymptote/plain_arrows.asy639
-rw-r--r--Master/texmf-dist/asymptote/plain_bounds.asy787
-rw-r--r--Master/texmf-dist/asymptote/plain_boxes.asy137
-rw-r--r--Master/texmf-dist/asymptote/plain_constants.asy166
-rw-r--r--Master/texmf-dist/asymptote/plain_debugger.asy86
-rw-r--r--Master/texmf-dist/asymptote/plain_filldraw.asy248
-rw-r--r--Master/texmf-dist/asymptote/plain_margins.asy99
-rw-r--r--Master/texmf-dist/asymptote/plain_markers.asy398
-rw-r--r--Master/texmf-dist/asymptote/plain_paths.asy386
-rw-r--r--Master/texmf-dist/asymptote/plain_pens.asy361
-rw-r--r--Master/texmf-dist/asymptote/plain_picture.asy1646
-rw-r--r--Master/texmf-dist/asymptote/plain_prethree.asy224
-rw-r--r--Master/texmf-dist/asymptote/plain_scaling.asy246
-rw-r--r--Master/texmf-dist/asymptote/plain_shipout.asy123
-rw-r--r--Master/texmf-dist/asymptote/plain_strings.asy227
-rw-r--r--Master/texmf-dist/asymptote/plain_xasy.asy132
-rw-r--r--Master/texmf-dist/asymptote/pstoedit.asy18
-rw-r--r--Master/texmf-dist/asymptote/reload.js23
-rw-r--r--Master/texmf-dist/asymptote/roundedpath.asy84
-rw-r--r--Master/texmf-dist/asymptote/simplex.asy274
-rw-r--r--Master/texmf-dist/asymptote/size10.asy12
-rw-r--r--Master/texmf-dist/asymptote/size11.asy12
-rw-r--r--Master/texmf-dist/asymptote/slide.asy609
-rw-r--r--Master/texmf-dist/asymptote/slopefield.asy87
-rw-r--r--Master/texmf-dist/asymptote/solids.asy408
-rw-r--r--Master/texmf-dist/asymptote/stats.asy292
-rw-r--r--Master/texmf-dist/asymptote/syzygy.asy926
-rw-r--r--Master/texmf-dist/asymptote/texcolors.asy68
-rw-r--r--Master/texmf-dist/asymptote/three.asy3319
-rw-r--r--Master/texmf-dist/asymptote/three_arrows.asy730
-rw-r--r--Master/texmf-dist/asymptote/three_light.asy117
-rw-r--r--Master/texmf-dist/asymptote/three_margins.asy104
-rw-r--r--Master/texmf-dist/asymptote/three_surface.asy1999
-rw-r--r--Master/texmf-dist/asymptote/three_tube.asy417
-rw-r--r--Master/texmf-dist/asymptote/tree.asy86
-rw-r--r--Master/texmf-dist/asymptote/trembling.asy216
-rw-r--r--Master/texmf-dist/asymptote/tube.asy167
-rw-r--r--Master/texmf-dist/asymptote/unicode.asy2
-rw-r--r--Master/texmf-dist/asymptote/version.asy1
-rw-r--r--Master/texmf-dist/asymptote/x11colors.asy145
98 files changed, 43218 insertions, 0 deletions
diff --git a/Master/texmf-dist/asymptote/CAD.asy b/Master/texmf-dist/asymptote/CAD.asy
new file mode 100644
index 00000000000..b65e6478838
--- /dev/null
+++ b/Master/texmf-dist/asymptote/CAD.asy
@@ -0,0 +1,367 @@
+struct sCAD
+{
+ int nLineGroup = 0; // 0-3
+
+ pen
+ // A
+ pA,
+ pVisibleEdge, // Sichtbare Kanten
+ pVisibleContour, // Sichtbarer Umriss
+ pUsableWindingLength, // Nitzbare Gewindelänge
+ pSystemLine, // Systemlinie (Stahlbau)
+ pDiagramCurve, // Kurve in Diagrammen
+ pSurfaceStructure, // Oberflächenstrukturen
+ // B
+ pB,
+ pLightEdge, // Lichtkante
+ pMeasureLine, // Maßlinie
+ pMeasureHelpLine, // Maßhilfslinie
+ pMeasureLineBound, // Maßlinienbegrenzung
+ pReferenceLine, // Hinweislinie
+ pHatch, // Schraffur
+ pWindingGround, // Gewindegrund
+ pDiagonalCross, // Diagonalkreuz
+ pBendLine, // Biegelinie
+ pProjectionLine, // Projektionslinie
+ pGrid, // Rasterlinien
+ // C
+ pC,
+ pFreehand, // Begrenzung abgebrochener oder unterbrochener
+ // Schnitte, wenn die Begrenzung
+ // keine Mittellinie ist
+ // E
+ pE,
+ pSurfaceTreatmentAllowed, // Bereich zulässiger Oberflächenbehandlung
+ // F
+ pF,
+ pInvisibleEdge, // unsichtbare Kante
+ pInvisibleContour, // unsichtbarer Umriss
+ // G
+ pG,
+ pMiddleLine, // Mittellinie
+ pSymmetryLine, // Symmetrielinie
+ pPartialCircle, // Teilkreis
+ pCircularHole, // Lochkreis
+ pDivisionPlane, // Teilungsebene
+ pTransferLine, // Trajektorien (Übertragunslinien)
+ // J
+ pJ,
+ pCuttingPlane, // Schnittebene
+ pSurfaceTreatmentRequested, // Bereich geforderter Behandlungen
+ // K
+ pK,
+ pContourBeforeDeformation, // Umrisse vor Verformung
+ pAdjacentPartContour, // Umrisse angrenzender Teile
+ pEndShapeRawMaterial, // Fertigformen in Rohteilen
+ pContourEligibleType, // Umrisse wahlweiser Ausführungen
+ pPartInFrontOfCuttingPlane; // Teile vor der Schnittebene
+
+
+
+ static sCAD Create(int nLineGroup = 1)
+ {
+ sCAD cad = new sCAD;
+ if ( nLineGroup < 0 )
+ nLineGroup = 0;
+ if ( nLineGroup > 3 )
+ nLineGroup = 3;
+ cad.nLineGroup = nLineGroup;
+
+ restricted real[] dblFullWidth = {0.35mm, 0.5mm, 0.7mm, 1.0mm};
+ restricted real[] dblHalfWidth = {0.18mm, 0.25mm, 0.35mm, 0.5mm};
+
+ pen pFullWidth = linewidth(dblFullWidth[nLineGroup]);
+ pen pHalfWidth = linewidth(dblHalfWidth[nLineGroup]);
+
+ // Linienarten:
+ // A
+ cad.pA =
+ cad.pVisibleEdge =
+ cad.pVisibleContour =
+ cad.pUsableWindingLength =
+ cad.pSystemLine =
+ cad.pDiagramCurve =
+ cad.pSurfaceStructure =
+ pFullWidth + solid;
+ // B
+ cad.pB =
+ cad.pLightEdge =
+ cad.pMeasureLine =
+ cad.pMeasureHelpLine =
+ cad.pMeasureLineBound =
+ cad.pReferenceLine =
+ cad.pHatch =
+ cad.pWindingGround =
+ cad.pDiagonalCross =
+ cad.pBendLine =
+ cad.pProjectionLine =
+ cad.pGrid =
+ pHalfWidth + solid;
+ // C
+ cad.pC =
+ cad.pFreehand =
+ pHalfWidth + solid;
+ // D
+ // Missing, as I have no idea how to implement this...
+ // E
+ cad.pE =
+ cad.pSurfaceTreatmentAllowed =
+ pFullWidth + linetype(new real[] {10,2.5});
+ // F
+ cad.pF =
+ cad.pInvisibleEdge =
+ cad.pInvisibleContour =
+ pHalfWidth + linetype(new real[] {20,5});
+ // G
+ cad.pG =
+ cad.pMiddleLine =
+ cad.pSymmetryLine =
+ cad.pPartialCircle =
+ cad.pCircularHole =
+ cad.pDivisionPlane =
+ cad.pTransferLine =
+ pHalfWidth + linetype(new real[] {40,5,5,5});
+ // H
+ // see J
+ // I
+ // This letter is not used in DIN 15
+ // J
+ cad.pJ =
+ cad.pCuttingPlane =
+ cad.pSurfaceTreatmentRequested =
+ pFullWidth + linetype(new real[] {20,2.5,2.5,2.5});
+ // K
+ cad.pK =
+ cad.pContourBeforeDeformation =
+ cad.pAdjacentPartContour =
+ cad.pEndShapeRawMaterial =
+ cad.pContourEligibleType =
+ cad.pPartInFrontOfCuttingPlane =
+ pHalfWidth + linetype(new real[] {40,5,5,5,5,5});
+
+ return cad;
+ } // end of Create
+
+
+
+ real GetMeasurementBoundSize(bool bSmallBound = false)
+ {
+ if ( bSmallBound )
+ return 1.5 * linewidth(pVisibleEdge) / 2;
+ else
+ return 5 * linewidth(pVisibleEdge);
+ }
+
+
+
+ path GetMeasurementBound(bool bSmallBound = false)
+ {
+ if ( bSmallBound )
+ return scale(GetMeasurementBoundSize(bSmallBound = bSmallBound)) *
+ unitcircle;
+ else
+ return (0,0) --
+ (-cos(radians(7.5)), -sin(radians(7.5))) *
+ GetMeasurementBoundSize(bSmallBound = bSmallBound) --
+ (-cos(radians(7.5)), sin(radians(7.5))) *
+ GetMeasurementBoundSize(bSmallBound = bSmallBound) --
+ cycle;
+ }
+
+
+
+ void MeasureLine(picture pic = currentpicture,
+ Label L,
+ pair pFrom,
+ pair pTo,
+ real dblLeft = 0,
+ real dblRight = 0,
+ real dblRelPosition = 0.5,
+ bool bSmallBound = false)
+ {
+ if ( dblLeft < 0 )
+ dblLeft = 0;
+ if ( dblRight < 0 )
+ dblRight = 0;
+ if ( (dblLeft > 0) && (dblRight == 0) )
+ dblRight = dblLeft;
+ if ( (dblLeft == 0) && (dblRight > 0) )
+ dblLeft = dblRight;
+ pair pDiff = pTo - pFrom;
+ real dblLength = length(pDiff);
+ pair pBegin = pFrom - dblLeft * unit(pDiff);
+ pair pEnd = pTo + dblRight * unit(pDiff);
+ if ( bSmallBound )
+ {
+ draw(
+ pic = pic,
+ g = pBegin--pEnd,
+ p = pMeasureLine);
+ }
+ else
+ {
+ real dblBoundSize = GetMeasurementBoundSize(bSmallBound = bSmallBound);
+ if ( dblLeft == 0 )
+ draw(
+ pic = pic,
+ g = (pFrom + dblBoundSize/2 * unit(pDiff))
+ -- (pTo - dblBoundSize/2 * unit(pDiff)),
+ p = pMeasureLine);
+ else
+ draw(
+ pic = pic,
+ g = pBegin -- (pFrom - dblBoundSize/2 * unit(pDiff))
+ ^^ pFrom -- pTo
+ ^^ (pTo + dblBoundSize/2 * unit(pDiff)) -- pEnd,
+ p = pMeasureLine);
+ }
+ path gArrow = GetMeasurementBound(bSmallBound = bSmallBound);
+ picture picL;
+ label(picL, L);
+ pair pLabelSize = 1.2 * (max(picL) - min(picL));
+ if ( dblLeft == 0 )
+ {
+ fill(
+ pic = pic,
+ g = shift(pFrom) * rotate(degrees(-pDiff)) * gArrow,
+ p = pVisibleEdge);
+ fill(
+ pic = pic,
+ g = shift(pTo) * rotate(degrees(pDiff)) * gArrow,
+ p = pVisibleEdge);
+ if ( dblRelPosition < 0 )
+ dblRelPosition = 0;
+ if ( dblRelPosition > 1 )
+ dblRelPosition = 1;
+ label(
+ pic = pic,
+ L = rotate(degrees(pDiff)) * L,
+ position =
+ pFrom
+ + dblRelPosition * pDiff
+ + unit(rotate(90)*pDiff) * pLabelSize.y / 2);
+ }
+ else
+ {
+ fill(
+ pic = pic,
+ g = shift(pFrom) * rotate(degrees(pDiff)) * gArrow,
+ p = pVisibleEdge);
+ fill(
+ pic = pic,
+ g = shift(pTo) * rotate(degrees(-pDiff)) * gArrow,
+ p = pVisibleEdge);
+ if ( (dblRelPosition >= 0) && (dblRelPosition <= 1) )
+ label(
+ pic = pic,
+ L = rotate(degrees(pDiff)) * L,
+ position =
+ pFrom
+ + dblRelPosition * pDiff
+ + unit(rotate(90)*pDiff) * pLabelSize.y / 2);
+ else
+ {
+ // draw label outside
+ if ( dblRelPosition < 0 )
+ label(
+ pic = pic,
+ L = rotate(degrees(pDiff)) * L,
+ position =
+ pBegin
+ + pLabelSize.x / 2 * unit(pDiff)
+ + unit(rotate(90)*pDiff) * pLabelSize.y / 2);
+ else
+ // dblRelPosition > 1
+ label(
+ pic = pic,
+ L = rotate(degrees(pDiff)) * L,
+ position =
+ pEnd
+ - pLabelSize.x / 2 * unit(pDiff)
+ + unit(rotate(90)*pDiff) * pLabelSize.y / 2);
+ }
+ }
+ } // end of MeasureLine
+
+
+
+ void MeasureParallel(picture pic = currentpicture,
+ Label L,
+ pair pFrom,
+ pair pTo,
+ real dblDistance,
+ // Variables from MeasureLine
+ real dblLeft = 0,
+ real dblRight = 0,
+ real dblRelPosition = 0.5,
+ bool bSmallBound = false)
+ {
+ pair pDiff = pTo - pFrom;
+ pair pPerpendicularDiff = unit(rotate(90) * pDiff);
+ real dblDistancePlus;
+ if ( dblDistance >= 0 )
+ dblDistancePlus = dblDistance + 1mm;
+ else
+ dblDistancePlus = dblDistance - 1mm;
+ draw(
+ pic = pic,
+ g = pFrom--(pFrom + dblDistancePlus*pPerpendicularDiff),
+ p = pMeasureHelpLine
+ );
+ draw(
+ pic = pic,
+ g = pTo--(pTo + dblDistancePlus*pPerpendicularDiff),
+ p = pMeasureHelpLine
+ );
+ MeasureLine(
+ pic = pic,
+ L = L,
+ pFrom = pFrom + dblDistance * pPerpendicularDiff,
+ pTo = pTo + dblDistance * pPerpendicularDiff,
+ dblLeft = dblLeft,
+ dblRight = dblRight,
+ dblRelPosition = dblRelPosition,
+ bSmallBound = bSmallBound);
+ } // end of MeasureParallel
+
+
+
+ path MakeFreehand(pair pFrom, pair pTo,
+ real dblRelDivisionLength = 12.5,
+ real dblRelDistortion = 2.5,
+ bool bIncludeTo = true)
+ {
+ pair pDiff = pTo - pFrom;
+ pair pPerpendicular = dblRelDistortion * linewidth(pFreehand) *
+ unit(rotate(90) * pDiff);
+
+ int nNumOfSubDivisions=ceil(length(pDiff) /
+ (dblRelDivisionLength * linewidth(pFreehand)));
+
+ restricted real[] dblDistortion = {1, -.5, .75, -.25, .25, -1, .5, -.75,
+ .25, -.25};
+ int nDistortion = 0;
+
+ guide g;
+ g = pFrom;
+ for ( int i = 1 ; i < nNumOfSubDivisions ; ++i )
+ {
+ g = g ..
+ (pFrom
+ + pDiff * i / (real)nNumOfSubDivisions
+ + pPerpendicular * dblDistortion[nDistortion]);
+ nDistortion += 1;
+ if ( nDistortion > 9 )
+ nDistortion = 0;
+ }
+
+ if ( bIncludeTo )
+ g = g .. pTo;
+
+ return g;
+ } // end of MakeFreehand
+
+
+
+} // end of CAD
+
diff --git a/Master/texmf-dist/asymptote/GUI/CubicBezier.py b/Master/texmf-dist/asymptote/GUI/CubicBezier.py
new file mode 100755
index 00000000000..6455b700a79
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/CubicBezier.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# Convert a Bezier curve to a polyline
+#
+# Once Tk supports "RawCurves" this will not be needed.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+###########################################################################
+import math
+
+def norm(vector):
+ """Return the norm of a vector"""
+ return math.sqrt(vector[0]**2+vector[1]**2)
+
+def splitLine(end0,end1,t):
+ """Split a line at the distance t, with t in (0,1)"""
+ return (end0[0]+t*(end1[0]-end0[0]),end0[1]+t*(end1[1]-end0[1]))
+
+def splitBezier(node0,control0,control1,node1,t):
+ """Find the nodes and control points for the segments of a Bezier curve split at t"""
+ a = splitLine(node0,control0,t)
+ b = splitLine(control0,control1,t)
+ c = splitLine(control1,node1,t)
+ d = splitLine(a,b,t)
+ e = splitLine(b,c,t)
+ f = splitLine(d,e,t)#this is the point on the curve at t
+ return ([node0,a,d,f],[f,e,c,node1])
+
+def BezierWidth(node0,control0,control1,node1):
+ """Compute the distance of the control points from the node-node axis"""
+ deltax = node1[0] - node0[0]
+ deltay = node1[1] - node0[1]
+ length = norm((deltax,deltay))
+ if length == 0:
+ y1 = control0[1]-node0[1]
+ y2 = control1[1]-node0[1]
+ else:
+ cosine = deltax/length
+ sine = deltay/length
+ y1 = cosine*(control0[1]-node0[1])-sine*(control0[0]-node0[0])
+ y2 = cosine*(control1[1]-node0[1])-sine*(control1[0]-node0[0])
+ if y1*y2 >= 0:
+ #same sign
+ return max(abs(y1),abs(y2))
+ else:
+ #opposite sign
+ return abs(y1)+abs(y2)
+
+#If the above algorithm fails, this one will work, but it is far from elegant
+#def computeIntermediates(steps,node0,control0,control1,node1):
+ #pointList = []
+ #for a in range(0,100,100/steps)+[100]:
+ #t = a/100.0
+ #t1 = 1-t
+ #x = node0[0]*t1**3+3*control0[0]*t*t1**2+3*control1[0]*t**2*t1+node1[0]*t**3
+ #y = node0[1]*t1**3+3*control0[1]*t*t1**2+3*control1[1]*t**2*t1+node1[1]*t**3
+ #pointList.append((x,y))
+ #return pointList
+#def makeBezier(steps,node0,control0,control1,node1):
+ #if len(node0)!=2 or len(control0)!=2 or len(control1)!=2 or len(node1)!=2:
+ #return -1
+ #else:
+ #return [node0]+computeIntermediates(steps,node0,control0,control1,node1)+[node1]
+
+def makeBezierIntermediates(node0,control0,control1,node1,epsilon):
+ """Find the points, excluding node0, to be used as the line segment endpoints"""
+ if(BezierWidth(node0,control0,control1,node1) <= epsilon):
+ return [node1]
+ else:
+ splitUp = splitBezier(node0,control0,control1,node1,0.5)
+ return makeBezierIntermediates(*splitUp[0]+[epsilon])+makeBezierIntermediates(*splitUp[1]+[epsilon])
+
+def makeBezier(node0,control0,control1,node1,epsilon=1):
+ """Return the vertices to be used in the polyline representation of a Bezier curve"""
+ return [node0]+makeBezierIntermediates(node0,control0,control1,node1,epsilon)
+
+if __name__ == '__main__':
+ pointList = makeBezier((-80,0),(-150,40),(150,120),(80,0),0.5)
+ from timeit import Timer
+ t = Timer('makeBezier((-80,0),(-40,-40),(40,120),(80,0),1)','from __main__ import makeBezier')
+ print pointList
+ print len(pointList)
+ iterations = 1000
+ time = t.timeit(iterations)
+ print "%d iterations took %f seconds (%f ms for each)."%(iterations,time,1000.0*time/iterations)
+ points = []
+ for point in pointList:
+ points.append(point[0])
+ points.append(-point[1])
+ from Tkinter import *
+ root = Tk()
+ canv = Canvas(root,scrollregion=(-100,-100,100,100))
+ canv.pack()
+ canv.create_line(points)
+ for point in pointList:
+ canv.create_oval(point[0],-point[1],point[0],-point[1],fill='red',outline='red')
+ root.mainloop()
diff --git a/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py b/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py
new file mode 100755
index 00000000000..f4a247a411d
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# UndoRedoStack implements the usual undo/redo capabilities of a GUI
+#
+# Author: Orest Shardt
+# Created: July 23, 2007
+#
+###########################################################################
+
+class action:
+ def __init__(self,act,inv):
+ self.act = act
+ self.inv = inv
+ def undo(self):
+ #print "Undo:",self
+ self.inv()
+ def redo(self):
+ #print "Redo:",self
+ self.act()
+ def __str__(self):
+ return "A generic action"
+
+class beginActionGroup:
+ pass
+
+class endActionGroup:
+ pass
+
+class actionStack:
+ def __init__(self):
+ self.clear()
+
+ def add(self,action):
+ self.undoStack.append(action)
+ #print "Added",action
+ self.redoStack = []
+
+ def undo(self):
+ if len(self.undoStack) > 0:
+ op = self.undoStack.pop()
+ if op is beginActionGroup:
+ level = 1
+ self.redoStack.append(endActionGroup)
+ while level > 0:
+ op=self.undoStack.pop()
+ if op is endActionGroup:
+ level -= 1
+ self.redoStack.append(beginActionGroup)
+ elif op is beginActionGroup:
+ level += 1
+ self.redoStack.append(endActionGroup)
+ else:
+ op.undo()
+ self.redoStack.append(op)
+ elif op is endActionGroup:
+ raise Exception,"endActionGroup without previous beginActionGroup"
+ else:
+ self.redoStack.append(op)
+ op.undo()
+ #print "undid",op
+ else:
+ pass #print "nothing to undo"
+
+ def redo(self):
+ if len(self.redoStack) > 0:
+ op = self.redoStack.pop()
+ if op is beginActionGroup:
+ level = 1
+ self.undoStack.append(endActionGroup)
+ while level > 0:
+ op = self.redoStack.pop()
+ if op is endActionGroup:
+ level -= 1
+ self.undoStack.append(beginActionGroup)
+ elif op is beginActionGroup:
+ level += 1
+ self.undoStack.append(endActionGroup)
+ else:
+ op.redo()
+ self.undoStack.append(op)
+ elif op is endActionGroup:
+ raise Exception,"endActionGroup without previous beginActionGroup"
+ else:
+ self.undoStack.append(op)
+ op.redo()
+ #print "redid",op
+ else:
+ pass #print "nothing to redo"
+
+ def setCommitLevel(self):
+ self.commitLevel = len(self.undoStack)
+
+ def changesMade(self):
+ if len(self.undoStack) != self.commitLevel:
+ return True
+ else:
+ return False
+
+ def clear(self):
+ self.redoStack = []
+ self.undoStack = []
+ self.commitLevel = 0
+
+if __name__=='__main__':
+ import sys
+ def opq():
+ print "action1"
+ def unopq():
+ print "inverse1"
+ q = action(opq,unopq)
+ w = action(lambda:sys.stdout.write("action2\n"),lambda:sys.stdout.write("inverse2\n"))
+ e = action(lambda:sys.stdout.write("action3\n"),lambda:sys.stdout.write("inverse3\n"))
+ s = actionStack()
+ s.add(q)
+ s.add(w)
+ s.add(e)
diff --git a/Master/texmf-dist/asymptote/GUI/xasy.py b/Master/texmf-dist/asymptote/GUI/xasy.py
new file mode 100755
index 00000000000..ffca16a1e2d
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasy.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasy implements a graphical interface for Asymptote.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+############################################################################
+
+import getopt,sys,signal
+from Tkinter import *
+import xasyMainWin
+
+signal.signal(signal.SIGINT,signal.SIG_IGN)
+
+root = Tk()
+mag = 1.0
+try:
+ opts,args = getopt.getopt(sys.argv[1:],"x:")
+ if(len(opts)>=1):
+ mag = float(opts[0][1])
+except:
+ print "Invalid arguments."
+ print "Usage: xasy.py [-x magnification] [filename]"
+ sys.exit(1)
+if(mag <= 0.0):
+ print "Magnification must be positive."
+ sys.exit(1)
+if(len(args)>=1):
+ app = xasyMainWin.xasyMainWin(root,args[0],mag)
+else:
+ app = xasyMainWin.xasyMainWin(root,magnification=mag)
+root.mainloop()
diff --git a/Master/texmf-dist/asymptote/GUI/xasy2asy.py b/Master/texmf-dist/asymptote/GUI/xasy2asy.py
new file mode 100755
index 00000000000..7779e073f81
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasy2asy.py
@@ -0,0 +1,767 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasy2asy provides a Python interface to Asymptote
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+###########################################################################
+import sys,os,signal,threading
+from subprocess import *
+from string import *
+import xasyOptions
+import Queue
+from Tkinter import *
+from tempfile import mkdtemp
+
+# PIL support is now mandatory due to rotations
+try:
+ import ImageTk
+ import Image
+except:
+ pass
+
+import CubicBezier
+
+quickAsyFailed = True
+global AsyTempDir
+
+console=None
+
+def startQuickAsy():
+ global quickAsy
+ global quickAsyFailed
+ global AsyTempDir
+ global fout,fin
+ if quickAsyRunning():
+ return
+ try:
+ fout.close()
+ quickAsy.wait()
+ except:
+ pass
+ try:
+ quickAsyFailed = False
+ if os.name == "nt":
+ AsyTempDir=mkdtemp(prefix="asy_", dir="./")
+ else:
+ AsyTempDir=mkdtemp(prefix="asy_")+os.sep
+ if sys.platform[:3] == 'win':
+ quickAsy=Popen([xasyOptions.options['asyPath'],"-noV","-multiline","-q",
+ "-o"+AsyTempDir,"-inpipe=0","-outpipe=2"],stdin=PIPE,stderr=PIPE)
+ fout=quickAsy.stdin
+ fin=quickAsy.stderr
+ else:
+ (rx,wx) = os.pipe()
+ (ra,wa) = os.pipe()
+ quickAsy=Popen([xasyOptions.options['asyPath'],"-noV","-multiline","-q",
+ "-o"+AsyTempDir,"-inpipe="+str(rx),"-outpipe="+str(wa)])
+ fout=os.fdopen(wx,'w')
+ fin=os.fdopen(ra,'r')
+ if quickAsy.returncode != None:
+ quickAsyFailed = True
+ except:
+ quickAsyFailed = True
+
+def getAsyTempDir():
+ return AsyTempDir
+
+def quickAsyRunning():
+ if quickAsyFailed or quickAsy.returncode != None:
+ return False
+ else:
+ return True
+
+def asyExecute(command):
+ if not quickAsyRunning():
+ startQuickAsy()
+ fout.write(command)
+
+def closeConsole(event):
+ global console
+ console = None
+
+def consoleOutput(line):
+ global console
+ global ctl
+ if console == None:
+ ctl=Toplevel()
+ ctl.title("Asymptote Console")
+ ctl.bind("<Destroy>",closeConsole)
+ yscrollbar=Scrollbar(ctl)
+ yscrollbar.pack(side=RIGHT,fill=Y)
+ console=Text(ctl,yscrollcommand=yscrollbar.set)
+ console.pack()
+ yscrollbar.config(command=console.yview)
+ console.insert(END,line)
+ ctl.lift()
+
+class asyTransform:
+ """A python implementation of an asy transform"""
+ def __init__(self,initTuple,delete=False):
+ """Initialize the transform with a 6 entry tuple"""
+ if type(initTuple) == type((0,)) and len(initTuple) == 6:
+ self.t = initTuple
+ self.x,self.y,self.xx,self.xy,self.yx,self.yy = initTuple
+ self.deleted = delete
+ else:
+ raise Exception,"Illegal initializer for asyTransform"
+
+ def getCode(self):
+ """Obtain the asy code that represents this transform"""
+ if self.deleted:
+ return str(self.t) + ", false"
+ else:
+ return str(self.t)
+
+ def scale(self,s):
+ return asyTransform((0,0,s,0,0,s))*self
+
+ def __str__(self):
+ """Equivalent functionality to getCode(). It allows the expression str(asyTransform) to be meaningful."""
+ return self.getCode()
+
+ def __mul__(self,other):
+ """Define multiplication of transforms as composition."""
+ if type(other)==type((0,)):
+ if len(other) == 6:
+ return self*asyTransform(other)
+ elif len(other) == 2:
+ return ((self.t[0]+self.t[2]*other[0]+self.t[3]*other[1]),(self.t[1]+self.t[4]*other[0]+self.t[5]*other[1]))
+ else:
+ raise Exception, "Illegal multiplier of %s"%str(type(other))
+ elif isinstance(other,asyTransform):
+ result = asyTransform((0,0,0,0,0,0))
+ result.x = self.x+self.xx*other.x+self.xy*other.y
+ result.y = self.y+self.yx*other.x+self.yy*other.y
+ result.xx = self.xx*other.xx+self.xy*other.yx
+ result.xy = self.xx*other.xy+self.xy*other.yy
+ result.yx = self.yx*other.xx+self.yy*other.yx
+ result.yy = self.yx*other.xy+self.yy*other.yy
+ result.t = (result.x,result.y,result.xx,result.xy,result.yx,result.yy)
+ return result
+ else:
+ raise Exception, "Illegal multiplier of %s"%str(type(other))
+
+def identity():
+ return asyTransform((0,0,1,0,0,1))
+
+class asyObj:
+ """A base class for asy objects: an item represented by asymptote code."""
+ def __init__(self):
+ """Initialize the object"""
+ self.asyCode = ""
+
+ def updateCode(self,mag=1.0):
+ """Update the object's code: should be overriden."""
+ pass
+
+ def getCode(self):
+ """Return the code describing the object"""
+ self.updateCode()
+ return self.asyCode
+
+class asyPen(asyObj):
+ """A python wrapper for an asymptote pen"""
+ def __init__(self,color=(0,0,0),width=0.5,options=""):
+ """Initialize the pen"""
+ asyObj.__init__(self)
+ self.options=options
+ self.width=width
+ self.setColor(color)
+ self.updateCode()
+ if options != "":
+ self.computeColor()
+
+ def updateCode(self,mag=1.0):
+ """Generate the pen's code"""
+ self.asyCode = "rgb(%g,%g,%g)"%self.color+"+"+str(self.width)
+ if len(self.options) > 0:
+ self.asyCode += "+"+self.options
+
+ def setWidth(self,newWidth):
+ """Set the pen's width"""
+ self.width=newWidth
+ self.updateCode()
+
+ def setColor(self,color):
+ """Set the pen's color"""
+ if type(color) == type((1,)) and len(color) == 3:
+ self.color = color
+ else:
+ self.color = "(0,0,0)"
+ self.updateCode()
+
+ def computeColor(self):
+ """Find out the color of an arbitrary asymptote pen."""
+ fout.write("pen p="+self.getCode()+';\n')
+ fout.write("file fout=output(mode='pipe');\n")
+ fout.write("write(fout,colorspace(p),newl);\n")
+ fout.write("write(fout,colors(p));\n")
+ fout.write("flush(fout);\n")
+ fout.flush()
+ colorspace = fin.readline()
+ if colorspace.find("cmyk") != -1:
+ lines = fin.readline()+fin.readline()+fin.readline()+fin.readline()
+ parts = lines.split()
+ c,m,y,k = eval(parts[0]),eval(parts[1]),eval(parts[2]),eval(parts[3])
+ k = 1-k
+ r,g,b = ((1-c)*k,(1-m)*k,(1-y)*k)
+ elif colorspace.find("rgb") != -1:
+ lines = fin.readline()+fin.readline()+fin.readline()
+ parts = lines.split()
+ r,g,b = eval(parts[0]),eval(parts[1]),eval(parts[2])
+ elif colorspace.find("gray") != -1:
+ lines = fin.readline()
+ parts = lines.split()
+ r = g = b = eval(parts[0])
+ self.color = (r,g,b)
+
+ def tkColor(self):
+ """Return the tk version of the pen's color"""
+ self.computeColor()
+ r,g,b = self.color
+ r,g,b = int(256*r),int(256*g),int(256*b)
+ if r == 256:
+ r = 255
+ if g == 256:
+ g = 255
+ if b == 256:
+ b = 255
+ r,g,b = map(hex,(r,g,b))
+ r,g,b = r[2:],g[2:],b[2:]
+ if len(r) < 2:
+ r += '0'
+ if len(g) < 2:
+ g += '0'
+ if len(b) < 2:
+ b += '0'
+ return'#'+r+g+b
+
+class asyPath(asyObj):
+ """A python wrapper for an asymptote path"""
+ def __init__(self):
+ """Initialize the path to be an empty path: a path with no nodes, control points, or links."""
+ asyObj.__init__(self)
+ self.nodeSet = []
+ self.linkSet = []
+ self.controlSet = []
+ self.computed = False
+
+ def initFromNodeList(self,nodeSet,linkSet):
+ """Initialize the path from a set of nodes and link types, "--", "..", or "::" """
+ if len(nodeSet)>0:
+ self.nodeSet = nodeSet[:]
+ self.linkSet = linkSet[:]
+ self.computed = False
+
+ def initFromControls(self,nodeSet,controlSet):
+ """Initialize the path from nodes and control points"""
+ self.controlSet = controlSet[:]
+ self.nodeSet = nodeSet[:]
+ self.computed = True
+
+ def makeNodeStr(self,node):
+ """Represent a node as a string"""
+ if node == 'cycle':
+ return node
+ else:
+ return "("+str(node[0])+","+str(node[1])+")"
+
+ def updateCode(self,mag=1.0):
+ """Generate the code describing the path"""
+ if not self.computed:
+ count = 0
+ #this string concatenation could be optimised
+ self.asyCode = self.makeNodeStr(self.nodeSet[0])
+ for node in self.nodeSet[1:]:
+ self.asyCode += self.linkSet[count]+self.makeNodeStr(node)
+ count += 1
+ else:
+ count = 0
+ #this string concatenation could be optimised
+ self.asyCode = self.makeNodeStr(self.nodeSet[0])
+ for node in self.nodeSet[1:]:
+ self.asyCode += "..controls"
+ self.asyCode += self.makeNodeStr(self.controlSet[count][0])
+ self.asyCode += "and"
+ self.asyCode += self.makeNodeStr(self.controlSet[count][1])
+ self.asyCode += ".." + self.makeNodeStr(node) + "\n"
+ count += 1
+
+ def getNode(self,index):
+ """Return the requested node"""
+ return self.nodeSet[index]
+
+ def getLink(self,index):
+ """Return the requested link"""
+ return self.linkSet[index]
+
+ def setNode(self,index,newNode):
+ """Set a node to a new position"""
+ self.nodeSet[index] = newNode
+
+ def moveNode(self,index,offset):
+ """Translate a node"""
+ if self.nodeSet[index] != "cycle":
+ self.nodeSet[index] = (self.nodeSet[index][0]+offset[0],self.nodeSet[1]+offset[1])
+
+ def setLink(self,index,ltype):
+ """Change the specified link"""
+ self.linkSet[index] = ltype
+
+ def addNode(self,point,ltype):
+ """Add a node to the end of a path"""
+ self.nodeSet.append(point)
+ if len(self.nodeSet) != 1:
+ self.linkSet.append(ltype)
+ if self.computed:
+ self.computeControls()
+
+ def insertNode(self,index,point,ltype=".."):
+ """Insert a node, and its corresponding link, at the given index"""
+ self.nodeSet.insert(index,point)
+ self.linkSet.insert(index,ltype)
+ if self.computed:
+ self.computeControls()
+
+ def setControl(self,index,position):
+ """Set a control point to a new position"""
+ self.controlSet[index] = position
+
+ def moveControl(self,index,offset):
+ """Translate a control point"""
+ self.controlSet[index] = (self.controlSet[index][0]+offset[0],self.controlSet[index][1]+offset[1])
+
+ def computeControls(self):
+ """Evaluate the code of the path to obtain its control points"""
+ fout.write("file fout=output(mode='pipe');\n")
+ fout.write("path p="+self.getCode()+';\n')
+ fout.write("write(fout,length(p),newl);\n")
+ fout.write("write(fout,unstraighten(p),endl);\n")
+ fout.flush()
+ lengthStr = fin.readline()
+ pathSegments = eval(lengthStr.split()[-1])
+ pathStrLines = []
+ for i in range(pathSegments+1):
+ line=fin.readline()
+ line=line.replace("\n","")
+ pathStrLines.append(line)
+ oneLiner = "".join(split(join(pathStrLines)))
+ splitList = oneLiner.split("..")
+ nodes = [a for a in splitList if a.find("controls")==-1]
+ self.nodeSet = []
+ for a in nodes:
+ if a == 'cycle':
+ self.nodeSet.append(a)
+ else:
+ self.nodeSet.append(eval(a))
+ controls = [a.replace("controls","").split("and") for a in splitList if a.find("controls") != -1]
+ self.controlSet = [[eval(a[0]),eval(a[1])] for a in controls]
+ self.computed = True
+
+class asyLabel(asyObj):
+ """A python wrapper for an asy label"""
+ def __init__(self,text="",location=(0,0),pen=asyPen()):
+ """Initialize the label with the given test, location, and pen"""
+ asyObj.__init__(self)
+ self.text = text
+ self.location = location
+ self.pen = pen
+
+ def updateCode(self,mag=1.0):
+ """Generate the code describing the label"""
+ self.asyCode = "Label(\""+self.text+"\","+str((self.location[0],self.location[1]))+","+self.pen.getCode()+",align=SE)"
+
+ def setText(self,text):
+ """Set the label's text"""
+ self.text = text
+ self.updateCode()
+
+ def setPen(self,pen):
+ """Set the label's pen"""
+ self.pen = pen
+ self.updateCode()
+
+ def moveTo(self,newl):
+ """Translate the label's location"""
+ self.location = newl
+
+class asyImage:
+ """A structure containing an image and its format, bbox, and IDTag"""
+ def __init__(self,image,format,bbox):
+ self.image = image
+ self.format = format
+ self.bbox = bbox
+ self.IDTag = None
+
+class xasyItem:
+ """A base class for items in the xasy GUI"""
+ def __init__(self,canvas=None):
+ """Initialize the item to an empty item"""
+ self.transform = [identity()]
+ self.asyCode = ""
+ self.imageList = []
+ self.IDTag = None
+ self.asyfied = False
+ self.onCanvas = canvas
+
+ def updateCode(self,mag=1.0):
+ """Update the item's code: to be overriden"""
+ pass
+
+ def getCode(self):
+ """Return the code describing the item"""
+ self.updateCode()
+ return self.asyCode
+
+ def handleImageReception(self,file,format,bbox,count):
+ """Receive an image from an asy deconstruction. It replaces the default in asyProcess."""
+ image = Image.open(file)
+ self.imageList.append(asyImage(image,format,bbox))
+ if self.onCanvas != None:
+ self.imageList[-1].itk = ImageTk.PhotoImage(image)
+ self.imageList[-1].originalImage = image.copy()
+ self.imageList[-1].originalImage.theta = 0.0
+ self.imageList[-1].originalImage.bbox = bbox
+ if count >= len(self.transform) or self.transform[count].deleted == False:
+ self.imageList[-1].IDTag = self.onCanvas.create_image(bbox[0],-bbox[3],anchor=NW,tags=("image"),image=self.imageList[-1].itk)
+ self.onCanvas.update()
+
+ def asyfy(self,mag=1.0):
+ self.removeFromCanvas()
+ self.imageList = []
+ self.imageHandleQueue = Queue.Queue()
+ worker = threading.Thread(target=self.asyfyThread,args=(mag,))
+ worker.start()
+ item = self.imageHandleQueue.get()
+ if console != None:
+ console.delete(1.0,END)
+ while item != (None,) and item[0] != "ERROR":
+ if(item[0] == "OUTPUT"):
+ consoleOutput(item[1])
+ else:
+ self.handleImageReception(*item)
+ try:
+ os.remove(item[0])
+ except:
+ pass
+ item = self.imageHandleQueue.get()
+ #self.imageHandleQueue.task_done()
+ worker.join()
+
+ def asyfyThread(self,mag=1.0):
+ """Convert the item to a list of images by deconstructing this item's code"""
+ fout.write("reset;\n")
+ fout.write("initXasyMode();\n")
+ fout.write("atexit(null);\n")
+ global console
+ for line in self.getCode().splitlines():
+ fout.write(line+"\n");
+ fout.write("deconstruct(%f);\n"%mag)
+ fout.flush()
+ format = "png"
+ maxargs = int(split(fin.readline())[0])
+ boxes=[]
+ batch=0
+ n=0
+ text = fin.readline()
+ template=AsyTempDir+"%d_%d.%s"
+ def render():
+ for i in range(len(boxes)):
+ l,b,r,t = [float(a) for a in split(boxes[i])]
+ name=template%(batch,i+1,format)
+ self.imageHandleQueue.put((name,format,(l,b,r,t),i))
+ while text != "Done\n" and text != "Error\n":
+ boxes.append(text)
+ text = fin.readline()
+ n += 1
+ if n >= maxargs:
+ render()
+ boxes=[]
+ batch += 1
+ n=0
+ if text == "Error\n":
+ self.imageHandleQueue.put(("ERROR",fin.readline()))
+ else:
+ render()
+ self.imageHandleQueue.put((None,))
+ self.asyfied = True
+
+ def drawOnCanvas(self,canvas,mag,forceAddition=False):
+ pass
+ def removeFromCanvas(self):
+ pass
+
+class xasyDrawnItem(xasyItem):
+ """A base class for GUI items was drawn by the user. It combines a path, a pen, and a transform."""
+ def __init__(self,path,pen = asyPen(),transform = identity()):
+ """Initialize the item with a path, pen, and transform"""
+ xasyItem.__init__(self)
+ self.path = path
+ self.pen = pen
+ self.transform = [transform]
+
+ def appendPoint(self,point,link=None):
+ """Append a point to the path. If the path is cyclic, add this point before the 'cycle' node."""
+ if self.path.nodeSet[-1] == 'cycle':
+ self.path.nodeSet[-1] = point
+ self.path.nodeSet.append('cycle')
+ else:
+ self.path.nodeSet.append(point)
+ self.path.computed = False
+ if len(self.path.nodeSet) > 1 and link != None:
+ self.path.linkSet.append(link)
+
+ def clearTransform(self):
+ """Reset the item's transform"""
+ self.transform = [identity()]
+
+ def removeLastPoint(self):
+ """Remove the last point in the path. If the path is cyclic, remove the node before the 'cycle' node."""
+ if self.path.nodeSet[-1] == 'cycle':
+ del self.path.nodeSet[-2]
+ else:
+ del self.path.nodeSet[-1]
+ del self.path.linkSet[-1]
+ self.path.computed = False
+
+ def setLastPoint(self,point):
+ """Modify the last point in the path. If the path is cyclic, modify the node before the 'cycle' node."""
+ if self.path.nodeSet[-1] == 'cycle':
+ self.path.nodeSet[-2] = point
+ else:
+ self.path.nodeSet[-1] = point
+ self.path.computed = False
+
+class xasyShape(xasyDrawnItem):
+ """An outlined shape drawn on the GUI"""
+ def __init__(self,path,pen=asyPen(),transform=identity()):
+ """Initialize the shape with a path, pen, and transform"""
+ xasyDrawnItem.__init__(self,path,pen,transform)
+
+ def updateCode(self,mag=1.0):
+ """Generate the code to describe this shape"""
+ self.asyCode = "xformStack.push("+self.transform[0].getCode()+");\n"
+ self.asyCode += "draw("+self.path.getCode()+","+self.pen.getCode()+");"
+
+ def removeFromCanvas(self,canvas):
+ """Remove the shape's depiction from a tk canvas"""
+ if self.IDTag != None:
+ canvas.delete(self.IDTag)
+
+ def drawOnCanvas(self,canvas,mag,asyFy=False,forceAddition=False):
+ """Add this shape to a tk canvas"""
+ if not asyFy:
+ if self.IDTag == None or forceAddition:
+ #add ourselves to the canvas
+ self.path.computeControls()
+ self.IDTag = canvas.create_line(0,0,0,0,tags=("drawn","xasyShape"),fill=self.pen.tkColor(),width=self.pen.width*mag)
+ self.drawOnCanvas(canvas,mag)
+ else:
+ self.path.computeControls()
+ pointSet = []
+ previousNode = self.path.nodeSet[0]
+ nodeCount = 0
+ if len(self.path.nodeSet) == 0:
+ pointSet = [0,0,0,0]
+ elif len(self.path.nodeSet) == 1:
+ if self.path.nodeSet[-1] != 'cycle':
+ p = self.transform[0]*(self.path.nodeSet[0][0],self.path.nodeSet[0][1])
+ pointSet = [p[0],-p[1],p[0],-p[1],p[0],-p[1]]
+ else:
+ pointSet = [0,0,0,0]
+ else:
+ for node in self.path.nodeSet[1:]:
+ if node == 'cycle':
+ node = self.path.nodeSet[0]
+ transform = self.transform[0].scale(mag)
+ points = CubicBezier.makeBezier(transform*previousNode,transform*self.path.controlSet[nodeCount][0],transform*self.path.controlSet[nodeCount][1],transform*node)
+ for point in points:
+ pointSet += [point[0],-point[1]]
+ nodeCount += 1
+ previousNode = node
+ canvas.coords(self.IDTag,*pointSet)
+ canvas.itemconfigure(self.IDTag,fill=self.pen.tkColor(),width=self.pen.width*mag)
+ else:
+ #first asyfy then add an image list
+ pass
+
+ def __str__(self):
+ """Create a string describing this shape"""
+ return "xasyShape code:%s"%("\n\t".join(self.getCode().splitlines()))
+
+class xasyFilledShape(xasyShape):
+ """A filled shape drawn on the GUI"""
+ def __init__(self,path,pen=asyPen(),transform=identity()):
+ """Initialize this shape with a path, pen, and transform"""
+ if path.nodeSet[-1] != 'cycle':
+ raise Exception,"Filled paths must be cyclic"
+ xasyShape.__init__(self,path,pen,transform)
+
+ def updateCode(self,mag=1.0):
+ """Generate the code describing this shape"""
+ self.asyCode = "xformStack.push("+self.transform[0].getCode()+");\n"
+ self.asyCode += "fill("+self.path.getCode()+","+self.pen.getCode()+");"
+
+ def removeFromCanvas(self,canvas):
+ """Remove this shape's depiction from a tk canvas"""
+ if self.IDTag != None:
+ canvas.delete(self.IDTag)
+
+ def drawOnCanvas(self,canvas,mag,asyFy=False,forceAddition=False):
+ """Add this shape to a tk canvas"""
+ if not asyFy:
+ if self.IDTag == None or forceAddition:
+ #add ourselves to the canvas
+ self.path.computeControls()
+ self.IDTag = canvas.create_polygon(0,0,0,0,0,0,tags=("drawn","xasyFilledShape"),fill=self.pen.tkColor(),outline=self.pen.tkColor(),width=1*mag)
+ self.drawOnCanvas(canvas,mag)
+ else:
+ self.path.computeControls()
+ pointSet = []
+ previousNode = self.path.nodeSet[0]
+ nodeCount = 0
+ if len(self.path.nodeSet) == 0:
+ pointSet = [0,0,0,0,0,0]
+ elif len(self.path.nodeSet) == 1:
+ if self.path.nodeSet[-1] != 'cycle':
+ p = self.transform[0]*(self.path.nodeSet[0][0],self.path.nodeSet[0][1])
+ pointSet = [p[0],-p[1],p[0],-p[1],p[0],-p[1]]
+ else:
+ pointSet = [0,0,0,0,0,0]
+ elif len(self.path.nodeSet) == 2:
+ if self.path.nodeSet[-1] != 'cycle':
+ p = self.transform[0].scale(mag)*(self.path.nodeSet[0][0],self.path.nodeSet[0][1])
+ p2 = self.transform[0].scale(mag)*(self.path.nodeSet[1][0],self.path.nodeSet[1][1])
+ pointSet = [p[0],-p[1],p2[0],-p2[1],p[0],-p[1]]
+ else:
+ pointSet = [0,0,0,0,0,0]
+ else:
+ for node in self.path.nodeSet[1:]:
+ if node == 'cycle':
+ node = self.path.nodeSet[0]
+ transform = self.transform[0].scale(mag)
+ points = CubicBezier.makeBezier(transform*previousNode,transform*self.path.controlSet[nodeCount][0],transform*self.path.controlSet[nodeCount][1],transform*node)
+ for point in points:
+ pointSet += [point[0],-point[1]]
+ nodeCount += 1
+ previousNode = node
+ canvas.coords(self.IDTag,*pointSet)
+ canvas.itemconfigure(self.IDTag,fill=self.pen.tkColor(),outline=self.pen.tkColor(),width=1*mag)
+ else:
+ #first asyfy then add an image list
+ pass
+
+ def __str__(self):
+ """Return a string describing this shape"""
+ return "xasyFilledShape code:%s"%("\n\t".join(self.getCode().splitlines()))
+
+class xasyText(xasyItem):
+ """Text created by the GUI"""
+ def __init__(self,text,location,pen=asyPen(),transform=identity()):
+ """Initialize this item with text, a location, pen, and transform"""
+ xasyItem.__init__(self)
+ self.label=asyLabel(text,location,pen)
+ self.transform = [transform]
+ self.onCanvas = None
+
+ def updateCode(self,mag=1.0):
+ """Generate the code describing this object"""
+ self.asyCode = "xformStack.push("+self.transform[0].getCode()+");\n"
+ self.asyCode += "label("+self.label.getCode()+");"
+
+ def removeFromCanvas(self):
+ """Removes the label's images from a tk canvas"""
+ if self.onCanvas == None:
+ return
+ for image in self.imageList:
+ if image.IDTag != None:
+ self.onCanvas.delete(image.IDTag)
+
+ def drawOnCanvas(self,canvas,mag,asyFy=True,forceAddition=False):
+ """Adds the label's images to a tk canvas"""
+ if self.onCanvas == None:
+ self.onCanvas = canvas
+ elif self.onCanvas != canvas:
+ raise Exception,"Error: item cannot be added to more than one canvas"
+ self.asyfy(mag)
+
+ def __str__(self):
+ return "xasyText code:%s"%("\n\t".join(self.getCode().splitlines()))
+
+class xasyScript(xasyItem):
+ """A set of images create from asymptote code. It is always deconstructed."""
+ def __init__(self,canvas,script="",transforms=[]):
+ """Initialize this script item"""
+ xasyItem.__init__(self,canvas)
+ self.transform = transforms[:]
+ self.script = script
+
+ def clearTransform(self):
+ """Reset the transforms for each of the deconstructed images"""
+ self.transform = [identity() for im in self.imageList]
+
+ def updateCode(self,mag=1.0):
+ """Generate the code describing this script"""
+ self.asyCode = "";
+ if len(self.transform) > 0:
+ self.asyCode = "xformStack.add("
+ isFirst = True
+ count = 0
+ for xform in self.transform:
+ if not isFirst:
+ self.asyCode+=",\n"
+ self.asyCode += "indexedTransform(%d,%s)"%(count,str(xform))
+ isFirst = False
+ count += 1
+ self.asyCode += ");\n"
+ self.asyCode += "startScript(); {\n"
+ self.asyCode += self.script.replace("\t"," ")
+ self.asyCode = self.asyCode.rstrip()
+ self.asyCode += "\n} endScript();\n"
+
+ def setScript(self,script):
+ """Sets the content of the script item."""
+ self.script = script
+ self.updateCode()
+
+ def removeFromCanvas(self):
+ """Removes the script's images from a tk canvas"""
+ if self.onCanvas == None:
+ return
+ for image in self.imageList:
+ if image.IDTag != None:
+ self.onCanvas.delete(image.IDTag)
+
+ def asyfy(self,mag):
+ """Generate the list of images described by this object and adjust the length of the transform list."""
+ xasyItem.asyfy(self,mag)
+ while len(self.imageList) > len(self.transform):
+ self.transform.append(identity())
+ while len(self.imageList) < len(self.transform):
+ self.transform.pop()
+ self.updateCode()
+
+ def drawOnCanvas(self,canvas,mag,asyFy=True,forceAddition=False):
+ """Adds the script's images to a tk canvas"""
+ if self.onCanvas == None:
+ self.onCanvas = canvas
+ elif self.onCanvas != canvas:
+ raise Exception,"Error: item cannot be added to more than one canvas"
+ self.asyfy(mag)
+
+ def __str__(self):
+ """Return a string describing this script"""
+ retVal = "xasyScript\n\tTransforms:\n"
+ for xform in self.transform:
+ retVal += "\t"+str(xform)+"\n"
+ retVal += "\tCode Ommitted"
+ return retVal
+
+if __name__=='__main__':
+ root = Tk()
+ t=xasyText("test",(0,0))
+ t.asyfy()
diff --git a/Master/texmf-dist/asymptote/GUI/xasyActions.py b/Master/texmf-dist/asymptote/GUI/xasyActions.py
new file mode 100755
index 00000000000..38aae1d5c7e
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyActions.py
@@ -0,0 +1,387 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyActions implements the possible actions and their inverses
+# for the undo/redo stack in xasy
+#
+# Author: Orest Shardt
+# Created: July 23, 2007
+#
+###########################################################################
+import math
+import UndoRedoStack
+import xasy2asy
+from Tkinter import *
+
+class translationAction(UndoRedoStack.action):
+ def __init__(self,owner,itemList,indexList,translation):
+ self.translation = translation
+ self.owner = owner
+ self.itemList = itemList
+ self.indexList = indexList
+ UndoRedoStack.action.__init__(self,self.transF,self.unTransF)
+
+ def transF(self):
+ mag = self.owner.magnification
+ for i in range(len(self.itemList)):
+ for index in self.indexList[i]:
+ self.owner.translateSomething(-1,(self.translation[0]/mag,self.translation[1]/mag),self.itemList[i],index)
+ if index==None:
+ index = 0
+ try:
+ self.owner.mainCanvas.move(self.itemList[i].imageList[index].IDTag,self.translation[0]*mag,-self.translation[1]*mag)
+ except:
+ self.owner.mainCanvas.move(self.itemList[i].IDTag,self.translation[0]*mag,-self.translation[1]*mag)
+ self.owner.updateSelection()
+ self.owner.updateCanvasSize()
+
+ def unTransF(self):
+ mag = self.owner.magnification
+ for i in range(len(self.itemList)):
+ for index in self.indexList[i]:
+ self.owner.translateSomething(-1,(-self.translation[0]/mag,-self.translation[1]/mag),self.itemList[i],index)
+ try:
+ self.owner.mainCanvas.move(self.itemList[i].imageList[index].IDTag,-self.translation[0]*mag,self.translation[1]*mag)
+ except:
+ self.owner.mainCanvas.move(self.itemList[i].IDTag,-self.translation[0]*mag,self.translation[1]*mag)
+ self.owner.updateSelection()
+ self.owner.updateCanvasSize()
+
+ def __str__(self):
+ return "Translation of "+str(self.itemList)+str(self.indexList)+" by "+str(self.translation)
+
+class rotationAction(UndoRedoStack.action):
+ def __init__(self,owner,itemList,indexList,angle,origin):
+ self.owner = owner
+ self.itemList = itemList
+ self.indexList = indexList
+ self.angle = angle
+ self.origin = origin
+ UndoRedoStack.action.__init__(self,self.rotF,self.unRotF)
+
+ def rotF(self):
+ for i in range(len(self.itemList)):
+ for index in self.indexList[i]:
+ self.owner.rotateSomething(-1,self.angle,self.origin,self.itemList[i],index)
+ for item in self.itemList:
+ item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(item)
+ self.owner.updateSelection()
+ self.owner.updateCanvasSize()
+
+ def unRotF(self):
+ for i in range(len(self.itemList)):
+ for index in self.indexList[i]:
+ self.owner.rotateSomething(-1,-self.angle,self.origin,self.itemList[i],index)
+ for item in self.itemList:
+ item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(item)
+ self.owner.updateSelection()
+ self.owner.updateCanvasSize()
+
+ def __str__(self):
+ return "Rotation of "+str(self.itemList)+str(self.indexList)+" by "+"%.3f"%(self.angle*180.0/math.pi)+" about "+str(self.origin)
+
+class addLabelAction(UndoRedoStack.action):
+ def __init__(self,owner,label):
+ self.owner = owner
+ self.label = label
+ UndoRedoStack.action.__init__(self,self.addF,self.unAddF)
+
+ def addF(self):
+ self.owner.addItemToFile(self.label)
+ self.label.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.label)
+
+ def unAddF(self):
+ self.label.removeFromCanvas()
+ del self.owner.fileItems[-1]
+ self.owner.propList.delete(0)
+ self.owner.clearSelection()
+
+ def __str__(self):
+ return "Addition of a label"
+
+class deleteLabelAction(UndoRedoStack.action):
+ def __init__(self,owner,label,index):
+ self.owner = owner
+ self.label = label
+ self.index = index
+ UndoRedoStack.action.__init__(self,self.delF,self.unDelF)
+
+ def delF(self):
+ self.owner.fileItems[self.index].removeFromCanvas()
+ self.owner.propList.delete(len(self.owner.fileItems)-self.index-1)
+ del self.owner.fileItems[self.index]
+
+ def unDelF(self):
+ self.owner.fileItems.insert(self.index,self.label)
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.propList.insert(len(self.owner.fileItems)-self.index-1,self.owner.describeItem(self.label))
+ self.owner.bindItemEvents(self.label)
+
+ def __str__(self):
+ return "Deletion of a label"
+
+class editLabelTextAction(UndoRedoStack.action):
+ def __init__(self,owner,label,newText,oldText):
+ self.owner = owner
+ self.label = label
+ self.newText = newText
+ self.oldText = oldText
+ UndoRedoStack.action.__init__(self,self.modT,self.unModT)
+
+ def modT(self):
+ self.label.label.setText(self.newText)
+ self.label.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.label)
+
+ def unModT(self):
+ self.label.label.setText(self.oldText)
+ self.label.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.label)
+
+ def __str__(self):
+ return "Editing a label's text"
+
+class editLabelPenAction(UndoRedoStack.action):
+ def __init__(self,owner,oldPen,newPen,index):
+ self.owner = owner
+ self.newPen = newPen
+ self.oldPen = oldPen
+ self.index = index
+ UndoRedoStack.action.__init__(self,self.editF,self.unEditF)
+
+ def editF(self):
+ self.owner.fileItems[self.index].removeFromCanvas()
+ self.owner.fileItems[self.index].label.pen = self.newPen
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.owner.fileItems[self.index])
+
+ def unEditF(self):
+ self.owner.fileItems[self.index].removeFromCanvas()
+ self.owner.fileItems[self.index].label.pen = self.oldPen
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.owner.fileItems[self.index])
+
+ def __str__(self):
+ return "Changing a label's pen"
+
+class addScriptAction(UndoRedoStack.action):
+ def __init__(self,owner,script):
+ self.owner = owner
+ self.script = script
+ UndoRedoStack.action.__init__(self,self.addF,self.unAddF)
+
+ def addF(self):
+ self.owner.addItemToFile(self.script)
+ self.script.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.script)
+
+ def unAddF(self):
+ self.script.removeFromCanvas()
+ del self.owner.fileItems[-1]
+ self.owner.propList.delete(0)
+ self.owner.clearSelection()
+
+ def __str__(self):
+ return "Addition of a script"
+
+class deleteScriptAction(UndoRedoStack.action):
+ def __init__(self,owner,script,index):
+ self.owner = owner
+ self.script = script
+ self.index = index
+ UndoRedoStack.action.__init__(self,self.delF,self.unDelF)
+
+ def delF(self):
+ self.owner.fileItems[self.index].removeFromCanvas()
+ self.owner.propList.delete(len(self.owner.fileItems)-self.index-1)
+ del self.owner.fileItems[self.index]
+
+ def unDelF(self):
+ self.owner.fileItems.insert(self.index,self.script)
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.propList.insert(len(self.owner.fileItems)-self.index-1,self.owner.describeItem(self.script))
+ self.owner.bindItemEvents(self.script)
+
+ def __str__(self):
+ return "Deletion of a script"
+
+class deleteScriptItemAction(UndoRedoStack.action):
+ def __init__(self,owner,script,indices,oldTransforms):
+ self.owner = owner
+ self.script = script
+ self.indices = indices[:]
+ UndoRedoStack.action.__init__(self,self.delI,self.unDelI)
+ def delI(self):
+ for index in self.indices:
+ self.script.transform[index].deleted = True
+ self.owner.mainCanvas.delete(self.script.imageList[index].IDTag)
+
+ def unDelI(self):
+ for i in range(len(self.indices)):
+ index = self.indices[i]
+ self.script.transform[index].deleted = False
+ bbox = self.script.imageList[index].originalImage.bbox
+ self.script.imageList[index].IDTag = self.owner.mainCanvas.create_image(bbox[0],-bbox[3],anchor=NW,tags=("image"),image=self.script.imageList[index].itk)
+ self.owner.bindEvents(self.script.imageList[index].IDTag)
+ self.owner.resetStacking()
+
+ def __str__(self):
+ return "Deletion of item "+str(self.indices)+" in "+str(self.script)
+
+class editScriptAction(UndoRedoStack.action):
+ def __init__(self,owner,script,newText,oldText):
+ self.owner = owner
+ self.script = script
+ self.newText = newText
+ self.oldText = oldText
+ UndoRedoStack.action.__init__(self,self.modS,self.unModS)
+
+ def modS(self):
+ self.script.setScript(self.newText)
+ self.script.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.script)
+
+ def unModS(self):
+ self.script.setScript(self.oldText)
+ self.script.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+ self.owner.bindItemEvents(self.script)
+
+ def __str__(self):
+ return "Modification of a script"
+
+class clearItemTransformsAction(UndoRedoStack.action):
+ def __init__(self,owner,item,oldTransforms):
+ self.owner = owner
+ self.item = item
+ self.oldTransforms = oldTransforms
+ UndoRedoStack.action.__init__(self,self.clearF,self.unClearF)
+
+ def clearF(self):
+ for i in range(len(self.oldTransforms)):
+ self.item.transform[i] = xasy2asy.identity()
+ self.item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+
+ def unClearF(self):
+ for i in range(len(self.oldTransforms)):
+ self.item.transform[i] = self.oldTransforms[i]
+ self.item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification)
+
+ def __str__(self):
+ return "Clear the transforms of "+str(self.item)+" from "+str(self.oldTransforms)
+
+class itemRaiseAction(UndoRedoStack.action):
+ def __init__(self,owner,items,oldPositions):
+ self.owner = owner
+ self.items = items[:]
+ self.oldPositions = oldPositions[:]
+ UndoRedoStack.action.__init__(self,self.raiseI,self.unRaiseI)
+
+ def raiseI(self):
+ for item in self.items:
+ self.owner.raiseSomething(item)
+
+ def unRaiseI(self):
+ length = len(self.owner.fileItems)
+ indices = self.oldPositions[:]
+ indices = [length-i-1 for i in indices]
+ indices.reverse()
+ for index in indices:
+ for i in range(index):
+ self.owner.raiseSomething(self.owner.fileItems[length-index-1])
+
+ def __str__(self):
+ return "Raise items "+str(self.items)+" from positions "+str(self.oldPositions)
+
+class itemLowerAction(UndoRedoStack.action):
+ def __init__(self,owner,items,oldPositions):
+ self.owner = owner
+ self.items = items[:]
+ self.oldPositions = oldPositions[:]
+ UndoRedoStack.action.__init__(self,self.lowerI,self.unLowerI)
+
+ def lowerI(self):
+ for item in self.items:
+ self.owner.lowerSomething(item)
+
+ def unLowerI(self):
+ indices = self.oldPositions[:]
+ indices.reverse()
+ for index in indices:
+ for i in range(index):
+ self.owner.lowerSomething(self.owner.fileItems[index])
+
+ def __str__(self):
+ return "Lower items "+str(self.items)+" from positions "+str(self.oldPositions)
+
+class addDrawnItemAction(UndoRedoStack.action):
+ def __init__(self,owner,item):
+ self.owner = owner
+ self.item = item
+ UndoRedoStack.action.__init__(self,self.drawF,self.unDrawF)
+
+ def drawF(self):
+ self.owner.addItemToFile(self.item)
+ self.item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True)
+ self.owner.bindItemEvents(self.item)
+
+ def unDrawF(self):
+ self.item.removeFromCanvas(self.owner.mainCanvas)
+ del self.owner.fileItems[-1]
+ self.owner.propList.delete(0)
+ self.owner.clearSelection()
+
+ def __str__(self):
+ return "Drawing of an item"
+
+class deleteDrawnItemAction(UndoRedoStack.action):
+ def __init__(self,owner,item,index):
+ self.owner = owner
+ self.item = item
+ self.index = index
+ UndoRedoStack.action.__init__(self,self.delF,self.unDelF)
+
+ def delF(self):
+ self.owner.fileItems[self.index].removeFromCanvas(self.owner.mainCanvas)
+ self.owner.propList.delete(len(self.owner.fileItems)-self.index-1)
+ del self.owner.fileItems[self.index]
+
+ def unDelF(self):
+ self.owner.fileItems.insert(self.index,self.item)
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True)
+ self.owner.propList.insert(len(self.owner.fileItems)-self.index-1,self.owner.describeItem(self.item))
+ self.owner.bindItemEvents(self.item)
+
+ def __str__(self):
+ return "Deletion of a drawn item"
+
+class editDrawnItemAction(UndoRedoStack.action):
+ def __init__(self,owner,oldItem,newItem,index):
+ self.owner = owner
+ self.oldItem = oldItem
+ self.newItem = newItem
+ self.index = index
+ UndoRedoStack.action.__init__(self,self.editF,self.unEditF)
+
+ def editF(self):
+ self.owner.fileItems[self.index].removeFromCanvas(self.owner.mainCanvas)
+ self.owner.fileItems[self.index].path = self.newItem.path
+ self.owner.fileItems[self.index].pen = self.newItem.pen
+ self.owner.fileItems[self.index].transform = self.newItem.transform
+ self.owner.fileItems[self.index].IDTag = self.newItem.IDTag
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True)
+ self.owner.bindItemEvents(self.owner.fileItems[self.index])
+
+ def unEditF(self):
+ self.owner.fileItems[self.index].removeFromCanvas(self.owner.mainCanvas)
+ self.owner.fileItems[self.index].path = self.oldItem.path
+ self.owner.fileItems[self.index].pen = self.oldItem.pen
+ self.owner.fileItems[self.index].transform = self.oldItem.transform
+ self.owner.fileItems[self.index].IDTag = self.oldItem.IDTag
+ self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True)
+ self.owner.bindItemEvents(self.owner.fileItems[self.index])
+
+ def __str__(self):
+ return "Modification of a drawn item"
diff --git a/Master/texmf-dist/asymptote/GUI/xasyBezierEditor.py b/Master/texmf-dist/asymptote/GUI/xasyBezierEditor.py
new file mode 100755
index 00000000000..998ee4c7c25
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyBezierEditor.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyBezierEditor implements the ability to graphically edit the location
+# of the nodes and control points of a bezier curve.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+###########################################################################
+
+from Tkinter import *
+import math
+from CubicBezier import *
+import xasy2asy
+
+class node:
+ def __init__(self,precontrol,node,postcontrol,uid,isTied = True):
+ self.node = node
+ self.precontrol = precontrol
+ self.postcontrol = postcontrol
+ self.isTied = isTied
+ self.uid = uid
+ self.nodeID = self.precontrolID = self.prelineID = self.postcontrolID = self.postlineID = None
+
+ def shiftNode(self,delta):
+ self.node = (self.node[0]+delta[0],self.node[1]+delta[1])
+ if self.precontrol != None:
+ self.precontrol = (self.precontrol[0]+delta[0],self.precontrol[1]+delta[1])
+ if self.postcontrol != None:
+ self.postcontrol = (self.postcontrol[0]+delta[0],self.postcontrol[1]+delta[1])
+
+ def shiftPrecontrol(self,delta):
+ self.precontrol = (self.precontrol[0]+delta[0],self.precontrol[1]+delta[1])
+ if self.isTied and self.postcontrol != None:
+ self.rotatePostControl(self.precontrol)
+
+ def shiftPostcontrol(self,delta):
+ self.postcontrol = (self.postcontrol[0]+delta[0],self.postcontrol[1]+delta[1])
+ if self.isTied and self.precontrol != None:
+ self.rotatePrecontrol(self.postcontrol)
+
+ def rotatePrecontrol(self,after):
+ vx,vy = after[0]-self.node[0],after[1]-self.node[1]
+ l = norm((vx,vy))
+ if l == 0:
+ return
+ m = norm((self.precontrol[0]-self.node[0],self.precontrol[1]-self.node[1]))
+ vx = -m*vx/l
+ vy = -m*vy/l
+ self.precontrol = self.node[0]+vx,self.node[1]+vy
+
+ def rotatePostControl(self,after):
+ vx,vy = after[0]-self.node[0],after[1]-self.node[1]
+ l = norm((vx,vy))
+ if l == 0:
+ return
+ m = norm((self.postcontrol[0]-self.node[0],self.postcontrol[1]-self.node[1]))
+ vx = -m*vx/l
+ vy = -m*vy/l
+ self.postcontrol = self.node[0]+vx,self.node[1]+vy
+
+ def draw(self,canvas):
+ width = 3
+ if self.precontrol != None:
+ if self.prelineID == None:
+ self.prelineID = canvas.create_line(self.precontrol[0],-self.precontrol[1],self.node[0],-self.node[1],tags=("preline",self.uid))
+ else:
+ canvas.coords(self.prelineID,self.precontrol[0],-self.precontrol[1],self.node[0],-self.node[1])
+ if self.precontrolID == None:
+ self.precontrolID = canvas.create_oval(self.precontrol[0]-width,-self.precontrol[1]-width,self.precontrol[0]+width,-self.precontrol[1]+width,
+ fill="red",outline="black",tags=("precontrol",self.uid))
+ else:
+ canvas.coords(self.precontrolID,self.precontrol[0]-width,-self.precontrol[1]-width,self.precontrol[0]+width,-self.precontrol[1]+width)
+ if self.postcontrol != None:
+ if self.postlineID == None:
+ self.postlineID = canvas.create_line(self.postcontrol[0],-self.postcontrol[1],self.node[0],-self.node[1],tags=("postline",self.uid))
+ else:
+ canvas.coords(self.postlineID,self.postcontrol[0],-self.postcontrol[1],self.node[0],-self.node[1])
+ if self.postcontrolID == None:
+ self.postcontrolID = canvas.create_oval(self.postcontrol[0]-width,-self.postcontrol[1]-width,self.postcontrol[0]+width,-self.postcontrol[1]+width,
+ fill="red",outline="black",tags=("postcontrol",self.uid))
+ else:
+ canvas.coords(self.postcontrolID,self.postcontrol[0]-width,-self.postcontrol[1]-width,self.postcontrol[0]+width,-self.postcontrol[1]+width)
+ if self.isTied:
+ color = "blue"
+ else:
+ color = "green"
+ if self.nodeID == None:
+ self.nodeID = canvas.create_oval(self.node[0]-width,-self.node[1]-width,self.node[0]+width,-self.node[1]+width,
+ fill=color,outline="black",tags=("node",self.uid))
+ else:
+ canvas.coords(self.nodeID,self.node[0]-width,-self.node[1]-width,self.node[0]+width,-self.node[1]+width)
+ canvas.itemconfigure(self.nodeID,fill=color)
+
+class xasyBezierEditor:
+ def __init__(self,parent,shape,canvas):
+ self.parent = parent
+ self.shape = shape
+ self.transform = self.shape.transform[0]
+ self.path = self.shape.path
+ self.canvas = canvas
+ self.modified = False
+ self.path.computeControls()
+ isCyclic = self.path.nodeSet[-1] == 'cycle'
+ segments = len(self.path.controlSet)
+ self.nodeList = []
+ for i in range(segments):
+ if i == 0:
+ node0 = self.transform*self.path.nodeSet[i]
+ control = self.transform*self.path.controlSet[i][0]
+ self.nodeList.append(node(None,node0,control,len(self.nodeList)))
+ else:
+ node0 = self.transform*self.path.nodeSet[i]
+ precontrol = self.transform*self.path.controlSet[i-1][1]
+ postcontrol = self.transform*self.path.controlSet[i][0]
+ self.nodeList.append(node(precontrol,node0,postcontrol,len(self.nodeList)))
+ if not isCyclic:
+ node0 = self.transform*self.path.nodeSet[-1]
+ precontrol = self.transform*self.path.controlSet[-1][1]
+ self.nodeList.append(node(precontrol,node0,None,len(self.nodeList)))
+ else:
+ self.nodeList[0].precontrol = self.transform*self.path.controlSet[-1][1]
+ self.showControls()
+ self.bindNodeEvents()
+ self.bindControlEvents()
+
+ def showControls(self):
+ for n in self.nodeList:
+ n.draw(self.canvas)
+ self.bindNodeEvents()
+ self.bindControlEvents()
+ self.parent.updateCanvasSize()
+
+ def bindNodeEvents(self):
+ self.canvas.tag_bind("node","<B1-Motion>",self.nodeDrag)
+ self.canvas.tag_bind("node","<Button-1>",self.buttonDown)
+ self.canvas.tag_bind("node","<Double-Button-1>",self.toggleNode)
+
+ def unbindNodeEvents(self):
+ self.canvas.tag_unbind("node","<B1-Motion>")
+ self.canvas.tag_unbind("node","<Button-1>")
+ self.canvas.tag_unbind("node","<Double-Button-1>")
+
+ def bindControlEvents(self):
+ self.canvas.tag_bind("precontrol || postcontrol","<B1-Motion>",self.controlDrag)
+ self.canvas.tag_bind("precontrol || postcontrol","<Button-1>",self.buttonDown)
+
+ def unbindControlEvents(self):
+ self.canvas.tag_unbind("precontrol || postcontrol","<B1-Motion>")
+ self.canvas.tag_unbind("precontrol || postcontrol","<Button-1>")
+
+ def buttonDown(self,event):
+ self.parent.freeMouseDown = False
+ self.startx,self.starty = event.x,event.y
+
+ def toggleNode(self,event):
+ self.parent.freeMouseDown = False
+ tags = self.canvas.gettags(CURRENT)
+ obj = tags[0]
+ uid = int(tags[1])
+ self.nodeList[uid].isTied = not self.nodeList[uid].isTied
+ self.showControls()
+
+ def nodeDrag(self,event):
+ self.parent.freeMouseDown = False
+ deltax = event.x-self.startx
+ deltay = event.y-self.starty
+ tags = self.canvas.gettags(CURRENT)
+ obj = tags[0]
+ uid = int(tags[1])
+ self.nodeList[uid].shiftNode((deltax,-deltay))
+ self.startx,self.starty = event.x,event.y
+ self.applyChanges()
+ self.showControls()
+ self.shape.drawOnCanvas(self.canvas,self.parent.magnification)
+
+ def controlDrag(self,event):
+ self.parent.freeMouseDown = False
+ deltax = event.x-self.startx
+ deltay = event.y-self.starty
+ tags = self.canvas.gettags(CURRENT)
+ obj = tags[0]
+ uid = int(tags[1])
+ if obj == "precontrol":
+ self.nodeList[uid].shiftPrecontrol((deltax,-deltay))
+ elif obj == "postcontrol":
+ self.nodeList[uid].shiftPostcontrol((deltax,-deltay))
+ self.startx,self.starty = event.x,event.y
+ self.applyChanges()
+ self.showControls()
+ self.shape.drawOnCanvas(self.canvas,self.parent.magnification)
+
+ def applyChanges(self):
+ self.modified = True
+ self.shape.transform[0] = xasy2asy.asyTransform((0,0,1,0,0,1))
+ for i in range(len(self.nodeList)):
+ self.path.nodeSet[i] = self.nodeList[i].node
+ if self.nodeList[i].postcontrol != None:
+ self.path.controlSet[i][0] = self.nodeList[i].postcontrol
+ if self.nodeList[i].precontrol != None:
+ self.path.controlSet[i-1][1] = self.nodeList[i].precontrol
+
+ def endEdit(self):
+ self.unbindNodeEvents()
+ self.unbindControlEvents()
+ self.canvas.delete("node || precontrol || postcontrol || preline || postline")
diff --git a/Master/texmf-dist/asymptote/GUI/xasyCodeEditor.py b/Master/texmf-dist/asymptote/GUI/xasyCodeEditor.py
new file mode 100755
index 00000000000..93e06e447ee
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyCodeEditor.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyCodeEditor implements a simple text editor for Asymptote scripts in
+# xasy.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+############################################################################
+
+from subprocess import call
+from tempfile import mkstemp
+from os import remove
+from os import fdopen
+from string import split
+import xasyOptions
+
+def getText(text=""):
+ """Launch the external editor"""
+ temp = mkstemp()
+ tempf = fdopen(temp[0],"r+w")
+ tempf.write(text)
+ tempf.flush()
+ try:
+ call(split(xasyOptions.options['externalEditor'])+[temp[1]])
+ except:
+ raise Exception('Error launching external editor.')
+ tempf.seek(0)
+ text = tempf.read()
+ remove(temp[1])
+ return text
+
+if __name__ == '__main__':
+ #run a test
+ print getText("Here is some text to edit")
diff --git a/Master/texmf-dist/asymptote/GUI/xasyColorPicker.py b/Master/texmf-dist/asymptote/GUI/xasyColorPicker.py
new file mode 100755
index 00000000000..7415be205c4
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyColorPicker.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyColorPicker implements a dialog that allows a user to choose a color
+# from those already defined in Asymptote or a custom RGB color.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+############################################################################
+
+from Tkinter import *
+import tkColorChooser
+asyColors = { "black":(0,0,0),
+ "white":(1,1,1),
+ "gray":(0.5,0.5,0.5),
+ "red":(1,0,0),
+ "green":(0,1,0),
+ "blue":(0,0,1),
+ "cmyk":(1,1,1),
+ "Cyan":(0,1,1),
+ "Magenta":(1,0,1),
+ "Yellow":(1,1,0),
+ "Black":(0,0,0),
+ "cyan":(0,1,1),
+ "magenta":(1,0,1),
+ "yellow":(1,1,0),
+ "palered":(1,0.75,0.75),
+ "palegreen":(0.75,1,0.75),
+ "paleblue":(0.75,0.75,1),
+ "palecyan":(0.75,1,1),
+ "palemagenta":(1,0.75,1),
+ "paleyellow":(1,1,0.75),
+ "palegray":(0.95,0.95,0.95),
+ "lightred":(1,0.5,0.5),
+ "lightgreen":(0.5,1,0.5),
+ "lightblue":(0.5,0.5,1),
+ "lightcyan":(0.5,1,1),
+ "lightmagenta":(1,0.5,1),
+ "lightyellow":(1,1,0.5),
+ "lightgray":(0.9,0.9,0.9),
+ "mediumred":(1,0.25,0.25),
+ "mediumgreen":(0.25,1,0.25),
+ "mediumblue":(0.25,0.25,1),
+ "mediumcyan":(0.25,1,1),
+ "mediummagenta":(1,0.25,1),
+ "mediumyellow":(1,1,0.25),
+ "mediumgray":(0.75,0.75,0.75),
+ "heavyred":(0.75,0,0),
+ "heavygreen":(0,0.75,0),
+ "heavyblue":(0,0,0.75),
+ "heavycyan":(0,0.75,0.75),
+ "heavymagenta":(0.75,0,0.75),
+ "lightolive":(0.75,0.75,0),
+ "heavygray":(0.25,0.25,0.25),
+ "deepred":(0.5,0,0),
+ "deepgreen":(0,0.5,0),
+ "deepblue":(0,0,0.5),
+ "deepcyan":(0,0.5,0.5),
+ "deepmagenta":(0.5,0,0.5),
+ "olive":(0.5,0.5,0),
+ "deepgray":(0.1,0.1,0.1),
+ "darkred":(0.25,0,0),
+ "darkgreen":(0,0.25,0),
+ "darkblue":(0,0,0.25),
+ "darkcyan":(0,0.25,0.25),
+ "darkmagenta":(0.25,0,0.25),
+ "darkolive":(0.25,0.25,0),
+ "darkgray":(0.05,0.05,0.05),
+ "orange":(1,0.5,0),
+ "fuchsia":(1,0,0.5),
+ "chartreuse":(0.5,1,0),
+ "springgreen":(0,1,0.5),
+ "purple":(0.5,0,1),
+ "royalblue":(0,0.5,1)
+ }
+colorLayout = [['palered',
+ 'lightred',
+ 'mediumred',
+ 'red',
+ 'heavyred',
+ 'deepred',
+ 'darkred',
+ 'palegreen',
+ 'lightgreen',
+ 'mediumgreen',
+ 'green',
+ 'heavygreen',
+ 'deepgreen',
+ 'darkgreen',
+ 'paleblue',
+ 'lightblue',
+ 'mediumblue',
+ 'blue',
+ 'heavyblue',
+ 'deepblue',
+ 'darkblue'],
+ ['palecyan',
+ 'lightcyan',
+ 'heavycyan',
+ 'deepcyan',
+ 'darkcyan',
+ 'palemagenta',
+ 'lightmagenta',
+ 'mediummagenta',
+ 'magenta',
+ 'heavymagenta',
+ 'deepmagenta',
+ 'darkmagenta',
+ 'yellow',
+ 'lightyellow',
+ 'mediumyellow',
+ 'yellow',
+ 'lightolive',
+ 'olive',
+ 'darkolive',
+ 'palegray',
+ 'lightgray',
+ 'mediumgray',
+ 'gray',
+ 'heavygray',
+ 'deepgray',
+ 'darkgray'],
+ ['black',
+ 'white',
+ 'orange',
+ 'fuchsia',
+ 'chartreuse',
+ 'springgreen',
+ 'purple',
+ 'royalblue',
+ 'Cyan',
+ 'Magenta',
+ 'Yellow',
+ 'Black']]
+
+def makeRGBfromTkColor(tkColor):
+ """Convert a Tk color of the form #rrggbb to an asy rgb color"""
+ r = int('0x'+tkColor[1:3],16)
+ g = int('0x'+tkColor[3:5],16)
+ b = int('0x'+tkColor[5:7],16)
+ r /= 255.0
+ g /= 255.0
+ b /= 255.0
+ return (r,g,b)
+
+def RGBreal255((r,g,b)):
+ """Convert an RGB color from 0-1 to 0-255"""
+ a,b,c = (256*r,256*g,256*b)
+ if a == 256:
+ a = 255
+ if b == 256:
+ b = 255
+ if c == 256:
+ c = 255
+ return map(int,(a,b,c))
+
+def RGB255hex((r,g,b)):
+ """Make a color in the form #rrggbb in hex from r,g,b in 0-255"""
+ rs,gs,bs = map(hex,(r,g,b))
+ rs,gs,bs = rs[2:],gs[2:],bs[2:]
+ if len(rs) < 2:
+ rs += '0'
+ if len(gs) < 2:
+ gs += '0'
+ if len(bs) < 2:
+ bs += '0'
+ return '#'+rs+gs+bs
+
+class xasyColorDlg(Toplevel):
+ """A dialog for choosing an asymptote color. It displays the usual asy presets and allows custom rgb colors"""
+ def __init__(self,master=None,color=(0,0,0)):
+ Toplevel.__init__(self,master,width=500,height=500)
+ self.resizable(False,False)
+ self.parent = master
+ self.title("Color Picker")
+ self.transient(master)
+ self.focus_set()
+ self.wait_visibility()
+ self.grab_set()
+ self.color = self.oldColor = color
+ cwidth = 120
+ rheight = 20
+ self.pframe=Frame(self,bd=0)
+ self.pframe.rowconfigure(0,weight=1)
+ self.pframe.columnconfigure(0,weight=1)
+ Label(self.pframe,text="Color Presets").grid(row=0,column=0)
+ self.colScroll = Scrollbar(self.pframe,orient=VERTICAL)
+ self.colorList = Canvas(self.pframe, width=cwidth*len(colorLayout), scrollregion=(0,0,20+cwidth*len(colorLayout),20+rheight*max([len(i) for i in colorLayout])),yscrollcommand=self.colScroll.set,relief=FLAT)
+ self.colScroll.config(command=self.colorList.yview)
+ self.colScroll.grid(row=1,column=1,sticky=N+S)
+ self.colorList.grid(row=1,column=0,sticky=W)
+ ccount = 0
+ for column in colorLayout:
+ rcount = 0
+ for name in column:
+ self.colorList.create_rectangle(10+cwidth*ccount,10+rheight*rcount,cwidth*ccount+25,rheight*rcount+25,tags=(name,"preset"),fill=RGB255hex(RGBreal255(asyColors[name])))
+ self.colorList.create_text(cwidth*ccount+30,10+rheight*rcount,text=name,anchor=NW,tags=(name,"preset"),fill="black",activefill=RGB255hex(RGBreal255(asyColors[name])))
+ rcount += 1
+ ccount += 1
+ self.colorList.tag_bind("preset","<Button-1>",self.setColorEvt)
+ Button(self,text="Custom color...",command=self.getCustom).grid(row=2,column=0,sticky=W,padx=5,pady=5)
+ self.colDisp = Canvas(self,width=200,height=20,background=RGB255hex(RGBreal255(self.color)),relief=SUNKEN, bd=3)
+ self.colDisp.grid(row=2,column=1,columnspan=2)
+ self.rowconfigure(3,minsize=10)
+ self.columnconfigure(0,weight=1)
+ self.columnconfigure(1,weight=1)
+ self.columnconfigure(2,weight=1)
+ Button(self,text="OK",default=ACTIVE,command=self.destroy).grid(row=4,column=1,sticky=E+W,padx=5,pady=5)
+ Button(self,text="Cancel",command=self.cancel).grid(row=4,column=2,sticky=E+W,padx=5,pady=5)
+ self.pframe.grid(row=1,column=0,columnspan=3,padx=10,pady=10)
+ self.bind("<Return>",self.closeUp)
+ self.setColor(color)
+ def closeUp(self,event):
+ """Close the dialog forcibly"""
+ self.destroy()
+ def getCustom(self):
+ """Request a custom RGB color using a tkColorChooser"""
+ result=tkColorChooser.askcolor(initialcolor=RGB255hex(RGBreal255(self.color)),title="Custom Color",parent=self)
+ if result != (None,None):
+ self.setColor((result[0][0]/255.0,result[0][1]/255.0,result[0][2]/255.0))
+ def cancel(self):
+ """Respond to the user pressing cancel"""
+ self.color = self.oldColor
+ self.destroy()
+ def setColor(self,color):
+ """Save the color and update the color display"""
+ self.color = color
+ self.colDisp.configure(background=RGB255hex(RGBreal255(self.color)))
+ def setColorEvt(self,event):
+ """Respond to the user clicking a color from the palette"""
+ self.setColor(asyColors[self.colorList.gettags(CURRENT)[0]])
+ def getColor(self,initialColor=(0,0,0)):
+ """Use this method to prompt for a color. It returns the new color or the old color if the user cancelled the operation.
+
+ e.g:
+ print xasyColorDlg(Tk()).getColor((1,1,0))
+ """
+ self.setColor(initialColor)
+ self.oldColor = initialColor
+ self.wait_window(self)
+ return self.color
+
+if __name__ == '__main__':
+ root = Tk()
+ Button(root,text="Pick Color",command=lambda:xasyColorDlg(root).getColor()).pack()
+ root.mainloop()
diff --git a/Master/texmf-dist/asymptote/GUI/xasyFile.py b/Master/texmf-dist/asymptote/GUI/xasyFile.py
new file mode 100755
index 00000000000..890f1ad3eef
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyFile.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyFile implements the loading, parsing, and saving of an xasy file.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+############################################################################
+
+from string import *
+from xasy2asy import *
+import re
+
+class xasyParseError(Exception):
+ """A parsing error"""
+ pass
+
+class xasyFileError(Exception):
+ """An i/o error or other error not related to parsing"""
+ pass
+
+def parseFile(inFile):
+ """Parse a file returning a list of xasyItems"""
+ lines = inFile.read()
+ lines = lines.splitlines()
+ #lines = [line for line in lines.splitlines() if not line.startswith("//")]
+ result = []
+ if lines[0] != "initXasyMode();":
+ raise xasyFileError,"Invalid file format: First line must be \"initXasyMode();\""
+ lines.pop(0)
+ lineCount = 2
+ lineNum = len(lines)
+ while lineNum > 0:
+ line = lines[0]
+ lines.pop(0)
+ if not line.isspace() and len(line)>0:
+ try:
+ #print "Line %d: %s"%(lineCount,line),
+ lineResult = parseLine(line.strip(),lines)
+ except:
+ raise xasyParseError,"Parsing error: line %d in %s\n%s"%(lineCount,inFile.name,line)
+
+ if lineResult != None:
+ result.append(lineResult)
+ #print "\tproduced: %s"%str(lineResult)
+ lineCount += lineNum-len(lines)
+ lineNum = len(lines)
+ return result
+
+transformPrefix = "xformStack"
+scriptPrefix = "startScript(); {"
+scriptSuffix = "} endScript();"
+def extractScript(lines):
+ """Find the code belonging to a script item"""
+ theScript = ""
+ line = lines.pop(0)
+ level = 1
+ while level > 0:
+ check = line.lstrip()
+ while check.endswith(scriptSuffix):
+ level -= 1
+ line = line[:len(line)-len(scriptSuffix)]
+ check = line.lstrip()
+ if check.startswith(scriptPrefix):
+ level += 1
+ theScript += line + "\n"
+ if level > 0:
+ line = lines.pop(0)
+
+ global pendingTransformsD
+ ts = pendingTransformsD[:]
+ pendingTransformsD = []
+ return xasyScript(None,script=theScript,transforms=ts[:])
+
+pendingTransforms = []
+pendingTransformsD = []
+def addTransform(index,t,active=1):
+ """Place a transform in the list of transforms, expanding the list as needed"""
+ while len(pendingTransformsD) < index+1:
+ pendingTransformsD.append(identity())
+ deleted = int(active==0)
+ pendingTransformsD[index]=asyTransform(t,deleted)
+
+def parseIndexedTransforms(args):
+ """Parse a list of indexedTransforms, adding them to the current list of transforms"""
+ global pendingTransformsD
+ pendingTransformsD = []
+ args = args.replace("indexedTransform","")
+ false = 0
+ tList = [eval(a) for a in ")?(".join(args.split("),(")).split("?")]
+ for a in tList:
+ addTransform(*a)
+
+def parseTransformExpression(line):
+ """Parse statements related to the xformStack
+
+ Syntax:
+ xformStack.push(transform)
+ e.g.: xformStack.push((0,0,1,0,0,1)); //the identity
+ xformStack.add(indexedTransform(index,transform)[,...])
+ e.g.: xformStack.add(indexedTransform(1,(0,0,1,0,0,1));
+ """
+ global pendingTransforms
+ stackCmd = line[len(transformPrefix)+1:line.find("(")]
+ if line[-2:] != ");":
+ raise xasyParseError,"Invalid syntax"
+ args = line[line.find("(")+1:-2]
+ if stackCmd == "push":
+ t = asyTransform(eval(args))
+ pendingTransforms.append(t)
+ elif stackCmd == "add":
+ parseIndexedTransforms(args)
+ else:
+ raise xasyParseError,"Invalid transform stack command."
+ return None
+
+def parseLabel(line):
+ """Parse an asy Label statement, returning an xasyText item"""
+ if not (line.startswith("Label(") and line.endswith(",align=SE)")):
+ raise xasyParseError,"Invalid syntax"
+ args = line[6:-1]
+ loc2 = args.rfind(",align=SE")
+ loc1 = args.rfind(",",0,loc2-1)
+ loc = args.rfind(",(",0,loc1-1)
+ if loc < 2:
+ raise xasyParseError,"Invalid syntax"
+ text = args[1:loc-1]
+ location = eval(args[loc+1:args.find("),",loc)+1])
+ pen = args[loc:loc2]
+ pen = pen[pen.find(",")+1:]
+ pen = pen[pen.find(",")+1:]
+ pen = pen[pen.find(",")+1:]
+ global pendingTransforms
+ return xasyText(text,location,parsePen(pen),pendingTransforms.pop())
+
+def parseLabelCommand(line):
+ """Parse a label command returning an xasyText object
+
+ Syntax:
+ label(Label(text,location,pen,align=SE));
+ e.g.: label(Label("Hello world!",(0,0),rgb(0,0,0)+0.5,align=SE));
+ """
+ if line[-2:] != ");":
+ raise xasyParseError,"Invalid syntax"
+ arguments = line[6:-2]
+ return parseLabel(arguments)
+
+def parseDrawCommand(line):
+ """Parse a draw command returning an xasyShape object
+
+ Syntax:
+ draw(path,pen);
+ e.g.: draw((0,0)..controls(0.33,0.33)and(0.66,0.66)..(1,1),rgb(1,0,1)+1.5);
+ """
+ if line[-2:] != ");":
+ raise xasyParseError,"Invalid syntax"
+ args = line[5:-2]
+ loc = args.rfind(",rgb")
+ path = args[:loc]
+ pen = args[loc+1:]
+ global pendingTransforms
+ return xasyShape(parsePathExpression(path),parsePen(pen),pendingTransforms.pop())
+
+def parseFillCommand(line):
+ """Parse a fill command returning an xasyFilledShape object
+
+ Syntax:
+ fill(cyclic path,pen);
+ e.g.: fill((0,0)..controls(0.33,0.33)and(0.66,0.66)..(1,1)..controls(0.66,0)and(0.33,0)..cycle,rgb(1,0,1)+1.5);
+ """
+ if line[-2:] != ");":
+ raise xasyParseError,"Invalid syntax"
+ args = line[5:-2]
+ loc = args.rfind(",rgb")
+ path = args[:loc]
+ pen = args[loc+1:]
+ global pendingTransforms
+ return xasyFilledShape(parsePathExpression(path),parsePen(pen),pendingTransforms.pop())
+
+def parsePen(pen):
+ """Parse a pen expression returning an asyPen
+
+ Syntax:
+ color+width[+options]
+ e.g.: rgb(0,0,0)+1.5+evenodd
+ e.g.: rgb(0,1,0)+1.23
+ """
+ try:
+ tokens = pen.split("+")
+ color = eval(tokens[0][3:])
+ width = float(tokens[1])
+ if len(tokens)>2:
+ options = "+".join(tokens[2:])
+ else:
+ options = ""
+ return asyPen(color,width,options)
+ except:
+ raise xasyParseError,"Invalid pen"
+
+def parsePathExpression(expr):
+ """Parse an asy path returning an asyPath()"""
+ result = asyPath()
+ expr = "".join(expr.split())
+ #print expr
+ if expr.find("controls") != -1:
+ #parse a path with control points
+ tokens = expr.split("..")
+ nodes = [a for a in tokens if not a.startswith("controls")]
+ for a in range(len(nodes)):
+ if nodes[a] != "cycle":
+ nodes[a] = eval(nodes[a])
+ controls = [map(eval,a.replace("controls","").split("and")) for a in tokens if a.startswith("controls")]
+ result.initFromControls(nodes, controls)
+ else:
+ #parse a path without control points
+ tokens = re.split(r"(::|--|\.\.)",expr)
+ linkSet = re.findall("::|--|\.\.",expr)
+ nodeSet = [a for a in tokens if not re.match(r"::|--|\.\.",a)]
+ #print nodeSet
+ for a in range(len(nodeSet)):
+ if nodeSet[a] != "cycle":
+ nodeSet[a] = eval(nodeSet[a])
+ #print nodeSet
+ result.initFromNodeList(nodeSet, linkSet)
+ return result
+
+def takeUntilSemicolon(line,lines):
+ """Read and concatenate lines until the collected lines end with a semicolon"""
+ data = line
+ while not data.endswith(";"):
+ newline = lines.pop(0)
+ data += newline
+ return data
+
+def parseLine(line,lines):
+ """Parse a line of the file"""
+ if len(line)==0 or line.isspace() or line.startswith("//"):
+ return None
+ elif line.startswith(scriptPrefix):
+ return extractScript(lines)
+ elif line.startswith(transformPrefix):
+ return parseTransformExpression(takeUntilSemicolon(line,lines))
+ elif line.startswith("label("):
+ return parseLabelCommand(takeUntilSemicolon(line,lines))
+ elif line.startswith("draw("):
+ return parseDrawCommand(takeUntilSemicolon(line,lines))
+ elif line.startswith("fill("):
+ return parseFillCommand(takeUntilSemicolon(line,lines))
+ elif line.startswith("exitXasyMode();"):
+ return None
+ raise Exception,"Could not parse the line"
+
+fileHeader = """initXasyMode();
+// This file was generated by xasy. It may be edited manually, however, a strict
+// syntax must be followed. It is advised that manually scripted items be added
+// in the form of a script either by using xasy or by mimicking the format of an
+// xasy-generated script item.
+// Please consult the documentation or the examples provided for details.
+"""
+
+fileFooter = """// This is the end of the file
+exitXasyMode();
+
+"""
+
+def saveFile(file,xasyItems):
+ """Write a list of xasyItems to a file"""
+ file.write(fileHeader)
+ for item in xasyItems:
+ file.write(item.getCode()+"\n\n")
+ file.write(fileFooter)
+
+if __name__ == '__main__':
+ root = Tk()
+ try:
+ name = raw_input("enter file name (\"../../xasyTest.asy\"):")
+ if name == '':
+ name = "../../xasyTest.asy"
+ f = open(name,"rt")
+ except:
+ print "Could not open file."
+ asy.quit()
+ sys.exit(1)
+
+ fileItems = []
+ try:
+ fileItems = parseFile(f)
+ res = map(str,fileItems)
+ print "----------------------------------"
+ print "Objects in %s"%f.name
+ print "----------------------------------"
+ for a in res:
+ print a
+ print "----------------------------------"
+ print "successful parse"
+ f.close()
+ except:
+ f.close()
+ print "parse failed"
+ raise
+
+ print "making a file"
+ f = open("testfile.asy","wt")
+ saveFile(f,fileItems)
+ f.close()
+ root.configure(width=500,height=500)
+ root.title("Results")
+ canv = Canvas(root,width=500,height=500)
+ canv.pack()
+ for i in fileItems[1].imageList:
+ canv.create_image(250+i.bbox[0],250-i.bbox[3],anchor = NW, image=i.image)
+ Button(root,image=i.image).pack(side=LEFT)
+ root.mainloop()
diff --git a/Master/texmf-dist/asymptote/GUI/xasyGUIIcons.py b/Master/texmf-dist/asymptote/GUI/xasyGUIIcons.py
new file mode 100755
index 00000000000..3afecfd349c
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyGUIIcons.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+##################################################################
+# This file stores the icons used by the xasy GUI
+#
+# About images and base64
+#
+# Suppose you have image.gif and want to create a base64
+# string. This can be accomplished using:
+#
+# import base64
+# base64.encodestring(open("image.gif","rb").read())
+#
+# The resulting output, including the enclosing single quotes,
+# is the base64 encoding of the image and can be used in the
+# dictionary below.
+#
+#
+# Suppose you have a base64 string, b64str, and want to create
+# an image. This can be accomplished using:
+#
+# import base64
+# open("image.gif","w").write(base64.decodestring(b64str))
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+##################################################################
+import base64
+import os
+#toolbar icon image data in base64 eliminates need to worry about files
+#these are the base64 encodings of the content of the directory xasy3Imgs
+iconB64 = {
+'lower': 'R0lGODlhGAAYAPEBAAAAAP///8zMzAAAACH5BAEAAAIALAAAAAAYABgAAAItlI+py+0Po5yUgosz\nrrybK2giqADed6LHKCZm+p7xx2Zuqsqr95KcJpv9cJUCADs=\n',
+'rotate': 'R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAI7jI8JkO231mux1mkistL1zX0Q\ng2Fi6aGmurKp+8KKrJB0Zt+nzOQw6XPZgqjczuQ7eohKEDKoUYWIgQIAOw==\n',
+'raise': 'R0lGODlhGAAYAPEBAAAAAP///8zMzAAAACH5BAEAAAIALAAAAAAYABgAAAIwlI+pywgND3ixzVvZ\nNDSn3nlKKH7fhaZmObKtk8Yh6dKlLcfC5vZ1jvIJh8SikVUAADs=\n',
+'fillPoly': 'R0lGODlhGAAYAPEAAAAAAIOBgwAAAAAAACH5BAEAAAIALAAAAAAYABgAAAJGlI+py+0PEYgNBDCp\nDPxqY3UcRoViRzrmKWbLyqIMHI9vHbsbfuoHjfOBcrlbT0ATIo+gldKpMD1lL8vUo5oqS9vS5wsp\nAAA7\n',
+'move': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAI4jI+py+0I3gNUNhqtwlVD7m3h\nkoVdUJ4MaKTYysVymbDoYcM4Tmv9eAO2cp6YEKUavY5BpvMZKgAAOw==\n',
+'drawBezi': 'R0lGODlhGAAYAPEBAAAAAP///6usrQAAACH5BAEAAAIALAAAAAAYABgAAAI6lI+py+0AnYRUKhox\nsFvUFDXdM4LWUaKnEaorhqSX1noPmMquWJukzpr0YitRcfE5oobFpPIJjUoZBQA7\n',
+'vertiMove': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAIsjI+py+0I3gNUNhqtwlVD7m3h\nko2QmZRooKKt+Y5xOFtc7dwrtrLd3gsKTQUAOw==\n',
+'horizMove': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAIljI+py+0Po5y02oshAGu/7Skg\n143mSYpgGTYt8mbyTNf2jedWAQA7\n',
+'fillEllip': 'R0lGODlhGAAYAPECAAAAAIOBg////6usrSH5BAEAAAMALAAAAAAYABgAAAJAnI+py+0PowS0gkmD\n3qE6wIXctYDi2SkmepLGyrYHHIcuXW93Lr+86BrgakHfrzjjIRGVFgVjWUqm1Kr1ijUUAAA7\n',
+'text': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAI+jI+py+0Po5x0AgSu1SZvHnhS\nBnpio5Ukt2Idm3bysYrnddLwy+czH0rhFDkbTigj6UzKl68CjUqn1Ko1UAAAOw==\n',
+'drawPoly': 'R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAI4jI+py+0PEYhtgkmlzgFL/4DJ\nFULiVi4ns66smrUxrMj1fdqHR+60kfPdgCwLzbWTIU1LE+cJKQAAOw==\n',
+'drawLines': 'R0lGODlhGAAYAPEBAAAAAP///6usrQAAACH5BAEAAAIALAAAAAAYABgAAAI3lI+py+0AnYRAPmoZ\njvlwX3Vh8j2XUIIWNXoZS3ZoO8soSK+4fRuYnQPyFEHhcHecFV+ppDNRAAA7\n',
+'drawShape': 'R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAI5jI+pywffIjQzIrCwdXnTplmh\nMoKmKIHVeZXp5cFcPH+0HbjbqKN17OoxgrTeKiOkPHjH3fIGjS4KADs=\n',
+'drawEllip': 'R0lGODlhGAAYAPEBAAAAAP///6usrQAAACH5BAEAAAIALAAAAAAYABgAAAIylI+py+0PowS0gklX\ndRd29XmgdIQh+Z1TSSJpyxpqZMLqzOB4sgsbmKFZgrCi8YhMNgoAOw==\n',
+'select': 'R0lGODlhGAAYAPIDAAAAAICAgMDAwP///6usrQAAAAAAAAAAACH5BAEAAAQALAAAAAAYABgAAANH\nSLrc/mvA6YCkGIiLIQhb54Gh2HwkZxKo4KoiSpam7L6rfdNZ4M+C3I+0Ush8wSLKCFIyPsnisyld\nAD7VabR6DWSt37BYmgAAOw==\n',
+'fillShape': 'R0lGODlhGAAYAPEAAAAAAIOBgwAAAAAAACH5BAEAAAIALAAAAAAYABgAAAJHlI+pywff4gsUxgSo\nrhflzXXCB4YXWQIiCqpnubnLw8KyU8Omket77wvcgD4ZUTcMIj3KlOLYejY1N8/R0qChaCIrtgsO\nRwoAOw==\n',
+'asy': 'R0lGODlhGAAYAIABAP8AAAAAACH5BAEKAAEALAIAAwAUABIAAAImjI+py+0AHINy0ZouNjBurmGd\nt40fFT4j2aydGqaBq8jvxH46UwAAOw==\n'
+}
+
+def createGIF(key):
+ """Create a gif file from the data in the iconB64 list of icons"""
+ if key not in iconB64.keys():
+ print "Error: %s not found in icon list."%key
+ print "Available icons:",iconB64.keys()
+ else:
+ print "Generating %s.gif"%key
+ open("%s.gif"%key,"w").write(base64.decodestring(iconB64[key]))
+
+def createGIFs():
+ """Create the files for all the icons in iconB64"""
+ for name in iconB64.keys():
+ createGIF(name)
+
+def createStrFromGif(gifFile):
+ """Create the base64 representation of a file"""
+ return base64.encodestring(gifFile.read())
+
+if __name__=='__main__':
+ print "Testing the xasyGUIIcons module."
+ print "Generating all the GIFs:"
+ createGIFs()
+ print "Checking consistency of all icons in iconB64"
+ allpassed = True
+ for icon in iconB64.keys():
+ print ("Checking %s"%icon),
+ if createStrFromGif(open("%s.gif"%icon,"rb")) == iconB64[icon]:
+ print "\tPassed."
+ else:
+ print "\tFailed."
+ allpassed= False
+ if allpassed:
+ print "All files succeeded."
+ s = raw_input("Delete generated files? (y/n)")
+ if s == "y":
+ for name in iconB64.keys():
+ print "Deleting %s.gif"%name,
+ os.unlink(name+".gif")
+ print "\tdone"
+ print "Done"
diff --git a/Master/texmf-dist/asymptote/GUI/xasyMainWin.py b/Master/texmf-dist/asymptote/GUI/xasyMainWin.py
new file mode 100755
index 00000000000..0967107ac1a
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyMainWin.py
@@ -0,0 +1,1741 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyMainWin implements the functionality of the GUI. It depends on
+# xasy2asy for its interaction with Asymptote.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+###########################################################################
+
+import os
+from string import *
+import subprocess
+import math
+import copy
+
+from Tkinter import *
+import tkMessageBox
+import tkFileDialog
+import tkSimpleDialog
+import threading
+import time
+
+from xasyVersion import xasyVersion
+import xasyCodeEditor
+from xasy2asy import *
+import xasyFile
+import xasyOptions
+import xasyOptionsDialog
+import CubicBezier
+from xasyBezierEditor import xasyBezierEditor
+from xasyGUIIcons import iconB64
+from xasyColorPicker import *
+
+from UndoRedoStack import *
+from xasyActions import *
+
+import string
+
+try:
+ import ImageTk
+ import Image
+ PILAvailable = True
+except:
+ PILAvailable = False
+
+class xasyMainWin:
+ def __init__(self,master,file=None,magnification=1.0):
+ self.opLock = threading.Lock()
+ self.parent = master
+ self.magnification = magnification
+ self.previousZoom = self.magnification
+ self.magList = [0.1,0.25,1.0/3,0.5,1,2,3,4,5,10]
+ self.bindGlobalEvents()
+ self.createWidgets()
+ self.resetGUI()
+ if sys.platform[:3] == "win":
+ site="http://effbot.org/downloads/PIL-1.1.7.win32-py2.7.exe"
+ else:
+ site="http://effbot.org/downloads/Imaging-1.1.7.tar.gz"
+ if not PILAvailable:
+ tkMessageBox.showerror("Failed Dependencies","An error occurred loading the required PIL library. Please install "+site)
+ self.parent.destroy()
+ sys.exit(1)
+ if file != None:
+ self.loadFile(file)
+ self.parent.after(100,self.tickHandler)
+
+ def testOrAcquireLock(self):
+ val = self.opLock.acquire(False)
+ if val:
+ self.closeDisplayLock()
+ return val
+
+ def acquireLock(self):
+ self.closeDisplayLock()
+ self.opLock.acquire()
+
+ def releaseLock(self):
+ self.opLock.release()
+ self.openDisplayLock()
+
+ def tickHandler(self):
+ self.tickCount += 1
+ self.mainCanvas.itemconfigure("outlineBox",dashoffset=self.tickCount%9)
+ self.parent.after(100,self.tickHandler)
+
+ def closeDisplayLock(self):
+ self.status.config(text="Busy")
+ self.parent.update_idletasks()
+
+ def openDisplayLock(self):
+ self.status.config(text="Ready")
+
+ def bindGlobalEvents(self):
+ #global bindings
+ self.parent.bind_all("<Control-z>",lambda q:self.editUndoCmd())# z -> no shift
+ self.parent.bind_all("<Control-Z>",lambda q:self.editRedoCmd())# Z -> with shift
+ self.parent.bind_all("<Control-o>",lambda q:self.fileOpenCmd())
+ self.parent.bind_all("<Control-n>",lambda q:self.fileNewCmd())
+ self.parent.bind_all("<Control-s>",lambda q:self.fileSaveCmd())
+ self.parent.bind_all("<Control-q>",lambda q:self.fileExitCmd())
+ self.parent.bind_all("<F1>",lambda q:self.helpHelpCmd())
+
+ def unbindGlobalEvents(self):
+ #global bindings
+ self.parent.unbind("<Control-z>")
+ self.parent.unbind("<Control-Z>")
+ self.parent.unbind("<Control-o>")
+ self.parent.unbind("<Control-n>")
+ self.parent.unbind("<Control-s>")
+ self.parent.unbind("<Control-q>")
+ self.parent.unbind("<F1>")
+
+ def createWidgets(self):
+ #first some configuration
+ self.parent.geometry("800x600")
+ self.parent.title("Xasy")
+ self.parent.resizable(True,True)
+
+ #try to capture the closing of the window
+ #find a better way to do this since the widgets may
+ #already be destroyed when this is called
+ self.parent.protocol("WM_DELETE_WINDOW",self.canQuit)
+
+ #the main menu
+ self.mainMenu = Menu(self.parent)
+ self.parent.config(menu=self.mainMenu)
+
+ #the file menu
+ self.fileMenu = Menu(self.mainMenu,tearoff=0)
+ self.fileMenu.add_command(label="New",command=self.fileNewCmd,accelerator="Ctrl+N",underline=0)
+ self.fileMenu.add_command(label="Open",command=self.fileOpenCmd,accelerator="Ctrl+O",underline=0)
+ self.fileMenu.add_separator()
+ self.fileMenu.add_command(label="Save",command=self.fileSaveCmd,accelerator="Ctrl+S",underline=0)
+ self.fileMenu.add_command(label="Save As",command=self.fileSaveAsCmd,underline=5)
+ self.fileMenu.add_separator()
+
+ #an export menu
+ self.exportMenu = Menu(self.fileMenu,tearoff=0)
+ self.exportMenu.add_command(label="EPS...",command=self.exportEPS,underline=0)
+ self.exportMenu.add_command(label="PDF...",command=self.exportPDF,underline=0)
+ self.exportMenu.add_command(label="GIF...",command=self.exportGIF,underline=0)
+ self.exportMenu.add_command(label="PNG...",command=self.exportPNG,underline=1)
+ self.exportMenu.add_command(label="SVG...",command=self.exportSVG,underline=0)
+ self.fileMenu.add_cascade(label="Export",menu=self.exportMenu,underline=1)
+ self.fileMenu.add_separator()
+
+ self.fileMenu.add_command(label="Quit",command=self.fileExitCmd,accelerator="Ctrl+Q",underline=0)
+
+ self.mainMenu.add_cascade(label="File",menu=self.fileMenu,underline=0)
+
+ #the edit menu
+ self.editMenu = Menu(self.mainMenu,tearoff=0)
+ self.editMenu.add_command(label="Undo",command=self.editUndoCmd,accelerator="Ctrl+Z",underline=0)
+ self.editMenu.add_command(label="Redo",command=self.editRedoCmd,accelerator="Shift+Ctrl+Z",underline=0)
+ self.mainMenu.add_cascade(label="Edit",menu=self.editMenu,underline=0)
+
+ #the tools menu
+ self.toolsMenu = Menu(self.mainMenu,tearoff=0)
+ self.mainMenu.add_cascade(label="Tools",menu=self.toolsMenu,underline=0)
+
+ #the options menu
+ self.optionsMenu = Menu(self.toolsMenu,tearoff=0)
+ self.toolsMenu.add_cascade(label="Options",menu=self.optionsMenu,underline=0)
+ self.optionsMenu.add_command(label="Edit...",command=self.editOptions,underline=0)
+ self.optionsMenu.add_command(label="Reset defaults",command=self.resetOptions,underline=6)
+
+ #the help menu
+ self.helpMenu = Menu(self.mainMenu,tearoff=0)
+ self.helpMenu.add_command(label="Help",command=self.helpHelpCmd,state=DISABLED,accelerator="F1",underline=0)
+ self.helpMenu.add_command(label="Asymptote Documentation",command=self.helpAsyDocCmd,underline=10)
+ self.helpMenu.add_separator()
+ self.helpMenu.add_command(label="About xasy",command=self.helpAboutCmd,underline=0)
+ self.mainMenu.add_cascade(label="Help",menu=self.helpMenu,underline=0)
+
+ #status bar
+ self.statusBar = Frame(self.parent,relief=FLAT)
+
+ self.magVal = DoubleVar()
+ self.magVal.set(round(100*self.magnification,1))
+ self.magVal.trace('w',self.zoomViewCmd)
+ zoomList = self.magList
+ if self.magnification not in zoomList:
+ zoomList.append(self.magnification)
+ zoomList.sort()
+ zoomList = [round(100*i,1) for i in zoomList]
+ self.zoomMenu = OptionMenu(self.statusBar,self.magVal,*zoomList)
+ self.zoomMenu.pack(side=RIGHT)
+ Label(self.statusBar,text="Zoom:",anchor=E,width=7).pack(side=RIGHT)
+
+ self.coords = Label(self.statusBar,text="(0,0)",relief=SUNKEN,anchor=W)
+ self.coords.pack(side=RIGHT,anchor=S)
+ self.status = Label(self.statusBar,text="Ready",relief=SUNKEN,anchor=W)
+ self.status.pack(side=RIGHT,fill=X,expand=1,anchor=SW)
+ self.statusBar.pack(side=BOTTOM,fill=X)
+
+ #toolbar for transformation, drawing, and adjustment commands
+ self.toolBar = Frame(self.parent,relief=FLAT,borderwidth=3)
+
+ #let's load some images
+ self.toolIcons = {}
+ for x in iconB64.keys():
+ self.toolIcons[x] = PhotoImage(data=iconB64[x])
+
+ self.transformLbl = Label(self.toolBar,text="",anchor=W)
+ self.transformLbl.grid(row=0,column=0,columnspan=2,sticky=W)
+ self.toolSelectButton = Button(self.toolBar,command=self.toolSelectCmd,image=self.toolIcons["select"])
+ self.toolSelectButton.grid(row=1,column=0,sticky=N+S+E+W)
+ self.toolMoveButton = Button(self.toolBar,command=self.toolMoveCmd,image=self.toolIcons["move"])
+ self.toolMoveButton.grid(row=2,column=0,sticky=N+S+E+W)
+ self.toolRotateButton = Button(self.toolBar,command=self.toolRotateCmd,image=self.toolIcons["rotate"])
+ self.toolRotateButton.grid(row=2,column=1,sticky=N+S+E+W)
+ self.toolVertiMoveButton = Button(self.toolBar,command=self.toolVertiMoveCmd,image=self.toolIcons["vertiMove"])
+ self.toolVertiMoveButton.grid(row=3,column=0,sticky=N+S+E+W)
+ self.toolHorizMoveButton = Button(self.toolBar,command=self.toolHorizMoveCmd,image=self.toolIcons["horizMove"])
+ self.toolHorizMoveButton.grid(row=3,column=1,sticky=N+S+E+W)
+
+ self.drawLbl = Label(self.toolBar,text="",anchor=W)
+ self.drawLbl.grid(row=4,column=0,columnspan=2,sticky=W)
+ self.toolDrawLinesButton = Button(self.toolBar,command=self.toolDrawLinesCmd,image=self.toolIcons["drawLines"])
+ self.toolDrawLinesButton.grid(row=5,column=0,sticky=N+S+E+W)
+ self.toolDrawBeziButton = Button(self.toolBar,command=self.toolDrawBeziCmd,image=self.toolIcons["drawBezi"])
+ self.toolDrawBeziButton.grid(row=5,column=1,sticky=N+S+E+W)
+ self.toolDrawPolyButton = Button(self.toolBar,command=self.toolDrawPolyCmd,image=self.toolIcons["drawPoly"])
+ self.toolDrawPolyButton.grid(row=6,column=0,sticky=N+S+E+W)
+ self.toolFillPolyButton = Button(self.toolBar,command=self.toolFillPolyCmd,image=self.toolIcons["fillPoly"])
+ self.toolFillPolyButton.grid(row=6,column=1,sticky=N+S+E+W)
+ self.toolDrawEllipButton = Button(self.toolBar,command=self.toolDrawEllipCmd,image=self.toolIcons["drawEllip"],state=DISABLED,relief=FLAT)
+ #self.toolDrawEllipButton.grid(row=7,column=0,sticky=N+S+E+W)
+ self.toolFillEllipButton = Button(self.toolBar,command=self.toolFillEllipCmd,image=self.toolIcons["fillEllip"],state=DISABLED,relief=FLAT)
+ #self.toolFillEllipButton.grid(row=7,column=1,sticky=N+S+E+W)
+ self.toolDrawShapeButton = Button(self.toolBar,command=self.toolDrawShapeCmd,image=self.toolIcons["drawShape"])
+ self.toolDrawShapeButton.grid(row=8,column=0,sticky=N+S+E+W)
+ self.toolFillShapeButton = Button(self.toolBar,command=self.toolFillShapeCmd,image=self.toolIcons["fillShape"])
+ self.toolFillShapeButton.grid(row=8,column=1,sticky=N+S+E+W)
+ self.toolTextButton = Button(self.toolBar,command=self.toolTextCmd,image=self.toolIcons["text"])
+ self.toolTextButton.grid(row=9,column=0,sticky=N+S+E+W)
+ self.toolAsyButton = Button(self.toolBar,command=self.toolAsyCmd,image=self.toolIcons["asy"])
+ self.toolAsyButton.grid(row=9,column=1,sticky=N+S+E+W)
+
+ self.adjLbl = Label(self.toolBar,text="",anchor=W)
+ self.adjLbl.grid(row=10,column=0,columnspan=2,sticky=W)
+ self.toolRaiseButton = Button(self.toolBar,command=self.toolRaiseCmd,image=self.toolIcons["raise"])
+ self.toolRaiseButton.grid(row=11,column=0,sticky=N+S+E+W)
+ self.toolLowerButton = Button(self.toolBar,command=self.toolLowerCmd,image=self.toolIcons["lower"])
+ self.toolLowerButton.grid(row=11,column=1,sticky=N+S+E+W)
+
+ self.toolBar.pack(side=LEFT,anchor=NW)
+
+ #documentation for the tool bar buttons
+ self.toolDocs = {
+ self.toolSelectButton : "Click an item to select it. Control-Click will select/deselect additional items. Use mouse scroller (or Up/Down keys) to raise/lower highlighted items.",
+ self.toolMoveButton : "Drag a selected item.",
+ self.toolHorizMoveButton : "Drag a selected item. Only horizontal translation will be applied.",
+ self.toolVertiMoveButton : "Drag a selected item. Only vertical translation will be applied.",
+ self.toolRotateButton : "Drag a selected item to rotate it.",
+ self.toolDrawLinesButton : "Click to draw line segments. Double click to place last point.",
+ self.toolDrawBeziButton : "Click to place points. Double click to place last point.",
+ self.toolDrawPolyButton : "Click to place vertices. Double click to place last point.",
+ self.toolFillPolyButton : "Click to place vertices. Double click to place last point.",
+ self.toolDrawEllipButton : "(UNIMPLEMENTED)Click to place center. Move mouse to achieve correct shape and double click.",
+ self.toolFillEllipButton : "(UNIMPLEMENTED)Click to place center. Move mouse to achieve correct shape and double click.",
+ self.toolDrawShapeButton : "Click to place points. Double click to place last point.",
+ self.toolFillShapeButton : "Click to place points. Double click to place last point.",
+ self.toolTextButton : "Click location of top left label position and enter text in dialog.",
+ self.toolRaiseButton : "Raise selected items to top.",
+ self.toolLowerButton : "Lower selected items to bottom.",
+ self.toolAsyButton : "Insert/Edit Asymptote code."
+ }
+
+ #Current pen settings
+ self.optionsBar = Frame(self.parent,height=100,relief=FLAT,borderwidth=3)
+ self.penDisp = Canvas(self.optionsBar,width=100,height=25,bg="white",relief=SUNKEN,borderwidth=3)
+ self.penDisp.grid(row=0,column=0,padx=3,pady=3)
+ self.penDisp.create_line(10,25,30,10,60,20,80,10,smooth=True,tags="penDisp")
+ self.penDisp.create_text(100,30,text="x1",tags="penMag",anchor=SE,font=("times","8"))
+ self.penColButton = Button(self.optionsBar,text="Color...",width=5,command=self.setPenColCmd,relief=FLAT)
+ self.penColButton.grid(row=0,column=1,padx=3,pady=3)
+ Label(self.optionsBar,text="Width",anchor=E).grid(row=0,column=2)
+ self.penWidthEntry = Entry(self.optionsBar,width=5)
+ self.penWidthEntry.bind("<KeyRelease>",self.penWidthChanged)
+ self.penWidthEntry.bind("<FocusOut>",self.applyPenWidthEvt)
+ self.penWidthEntry.bind("<Return>",self.applyPenWidthEvt)
+ self.penWidthEntry.grid(row=0,column=3)
+ Label(self.optionsBar,text="Options",anchor=E).grid(row=0,column=4)
+ self.penOptEntry = Entry(self.optionsBar)
+ self.penOptEntry.bind("<FocusOut>",self.applyPenOptEvt)
+ self.penOptEntry.bind("<Return>",self.applyPenOptEvt)
+ self.penOptEntry.grid(row=0,column=5)
+ self.optionsBar.pack(side=BOTTOM,anchor=NW)
+
+ #a paned window for the canvas and propert explorer
+ self.windowPane = PanedWindow(self.parent)
+
+ #a property explorer
+ self.propFrame = Frame(self.parent)
+ self.propFrame.rowconfigure(1,weight=1)
+ self.propFrame.columnconfigure(0,weight=1)
+ Label(self.propFrame,text="Item List").grid(row=0,column=0,columnspan=2)
+ self.itemScroll = Scrollbar(self.propFrame,orient=VERTICAL)
+ self.propList = Listbox(self.propFrame, yscrollcommand=self.itemScroll.set)
+ self.itemScroll.config(command=self.propList.yview)
+ self.itemScroll.grid(row=1,column=1,sticky=N+S)
+ self.propList.grid(row=1,column=0,sticky=N+S+E+W)
+ self.propList.bind("<Double-Button-1>",self.propSelect)
+ self.propList.bind("<Button-3>",self.itemPropMenuPopup)
+
+ #the canvas's frame
+ self.canvFrame = Frame(self.parent,relief=FLAT,borderwidth=0)
+ self.canvFrame.rowconfigure(0,weight=1)
+ self.canvFrame.columnconfigure(0,weight=1)
+ self.canvVScroll = Scrollbar(self.canvFrame,orient=VERTICAL)
+ self.canvHScroll = Scrollbar(self.canvFrame,orient=HORIZONTAL)
+ self.canvHScroll.grid(row=1,column=0,sticky=E+W)
+ self.canvVScroll.grid(row=0,column=1,sticky=N+S)
+
+ #add the frames to the window pane
+ self.windowPane.pack(side=RIGHT,fill=BOTH,expand=True)
+ self.windowPane.add(self.canvFrame)
+ self.windowPane.add(self.propFrame)
+ self.windowPane.paneconfigure(self.propFrame,minsize=50,sticky=N+S+E+W)
+ self.windowPane.bind("<Double-Button-1>",self.togglePaneEvt)
+
+ #the highly important canvas!
+ self.mainCanvas = Canvas(self.canvFrame,relief=SUNKEN,background="white",borderwidth=3,
+ highlightthickness=0,closeenough=1.0,yscrollcommand=self.canvVScroll.set,
+ xscrollcommand=self.canvHScroll.set)
+ self.mainCanvas.grid(row=0,column=0,sticky=N+S+E+W)
+
+ self.canvVScroll.config(command=self.mainCanvas.yview)
+ self.canvHScroll.config(command=self.mainCanvas.xview)
+
+ self.mainCanvas.bind("<Motion>",self.canvMotion)
+ self.mainCanvas.bind("<Button-1>",self.canvLeftDown)
+ self.mainCanvas.bind("<Double-Button-1>",self.endDraw)
+ self.mainCanvas.bind("<ButtonRelease-1>",self.canvLeftUp)
+ self.mainCanvas.bind("<B1-Motion>",self.canvDrag)
+
+ self.mainCanvas.bind("<Enter>",self.canvEnter)
+ self.mainCanvas.bind("<Leave>",self.canvLeave)
+ self.mainCanvas.bind("<Delete>",self.itemDelete)
+ #self.mainCanvas.bind("<Button-3>",self.canvRightDown)
+ #self.mainCanvas.bind("<ButtonRelease-3>",self.canvRightUp)
+ self.mainCanvas.bind("<Button-4>",self.itemRaise)
+ self.mainCanvas.bind("<Button-5>",self.itemLower)
+ self.mainCanvas.bind("<Up>",self.itemRaise)
+ self.mainCanvas.bind("<Down>",self.itemLower)
+ self.mainCanvas.bind("<Configure>",self.configEvt)
+
+ def foregroundPenColor(self,hex):
+ hex = hex[1:]
+ rgb = max(hex[0:2], hex[2:4], hex[4:6])
+ if(rgb >= "80"):
+ return "black"
+ else:
+ return "white"
+
+ def resetGUI(self):
+ #set up the main window
+ self.filename = None
+ self.fileToOpen = None
+ self.retitle()
+
+ #set up the paned window
+ self.paneVisible = True
+
+ #setup the pen entries
+ self.pendingPenWidthChange = None
+ self.pendingPenOptChange = None
+
+ #load one-time configs
+ xasyOptions.load()
+ self.tkPenColor = xasyOptions.options['defPenColor']
+ self.penColor = makeRGBfromTkColor(self.tkPenColor)
+ self.penColButton.config(activebackground=self.tkPenColor,
+ activeforeground=self.foregroundPenColor(self.tkPenColor))
+ self.penWidth = xasyOptions.options['defPenWidth']
+ self.penWidthEntry.select_range(0,END)
+ self.penWidthEntry.delete(0,END)
+ self.penWidthEntry.insert(END,str(self.penWidth))
+ self.penOptions = xasyOptions.options['defPenOptions']
+ self.penOptEntry.select_range(0,END)
+ self.penOptEntry.delete(0,END)
+ self.penOptEntry.insert(END,str(self.penOptions))
+ self.showCurrentPen()
+
+ #load modifiable configs
+ self.applyOptions()
+
+ #set up editing
+ self.editor = None
+
+ #set up drawing
+ self.pathInProgress = asyPath()
+ self.currentIDTag = -1
+ self.inDrawingMode = False
+ self.freeMouseDown = True
+ self.dragSelecting = False
+ self.itemsBeingRotated = []
+ self.inRotatingMode = False
+
+ #set up the toolbar
+ try:
+ self.updateSelectedButton(self.toolSelectButton)
+ except:
+ self.selectedButton = self.toolSelectButton
+ self.updateSelectedButton(self.toolSelectButton)
+
+ #set up the canvas
+ self.mainCanvas.delete(ALL)
+ self.mainCanvas.create_rectangle(0,0,0,0,tags="outlineBox",width=0,outline="#801111",dash=(3,6))
+ self.backColor = "white" #in future, load this from an options file. Or, should this really be an option?
+ self.mainCanvas.configure(background=self.backColor)
+
+ #set up the xasy item list
+ self.fileItems = []
+ self.propList.delete(0,END)
+ self.updateCanvasSize()
+
+ #setup timer
+ self.tickCount = 0
+
+ #setup undo/redo!
+ self.undoRedoStack = actionStack()
+ self.amDragging = False
+
+ def retitle(self):
+ if self.filename == None:
+ self.parent.title("Xasy - New File")
+ else:
+ name = os.path.abspath(self.filename)
+ name = os.path.basename(name)
+ self.parent.title("Xasy - %s"%name)
+
+ def applyOptions(self):
+ self.gridcolor = xasyOptions.options['gridColor']
+ self.tickcolor = xasyOptions.options['tickColor']
+ self.axiscolor = xasyOptions.options['axesColor']
+ self.gridVisible = xasyOptions.options['showGrid']
+ self.gridxspace = xasyOptions.options['gridX']
+ self.gridyspace = xasyOptions.options['gridY']
+ self.axesVisible = xasyOptions.options['showAxes']
+ self.axisxspace = xasyOptions.options['axisX']
+ self.axisyspace = xasyOptions.options['axisY']
+ self.updateCanvasSize()
+ #test the asyProcess
+ startQuickAsy()
+ if not quickAsyRunning():
+ if tkMessageBox.askyesno("Xasy Error","Asymptote could not be executed.\r\nTry to find Asymptote automatically?"):
+ xasyOptions.setAsyPathFromWindowsRegistry()
+ xasyOptions.save()
+ startQuickAsy()
+ while not quickAsyRunning():
+ if tkMessageBox.askyesno("Xasy Error","Asymptote could not be executed.\r\nEdit settings?"):
+ xasyOptionsDialog.xasyOptionsDlg(self.parent)
+ xasyOptions.save()
+ startQuickAsy()
+ else:
+ self.parent.destroy()
+ sys.exit(1)
+
+ def drawGrid(self):
+ self.mainCanvas.delete("grid")
+ if not self.gridVisible:
+ return
+ left,top,right,bottom = map(int,self.mainCanvas.cget("scrollregion").split())
+ gridyspace = int(self.magnification*self.gridyspace)
+ gridxspace = int(self.magnification*self.gridxspace)
+ if gridxspace >= 3 and gridyspace >= 3:
+ for i in range(0,right,gridxspace):
+ self.mainCanvas.create_line(i,top,i,bottom,tags=("grid","vertical"),fill=self.gridcolor)
+ for i in range(-gridxspace,left,-gridxspace):
+ self.mainCanvas.create_line(i,top,i,bottom,tags=("grid","vertical"),fill=self.gridcolor)
+ for i in range(-gridyspace,top,-gridyspace):
+ self.mainCanvas.create_line(left,i,right,i,tags=("grid","horizontal"),fill=self.gridcolor)
+ for i in range(0,bottom,gridyspace):
+ self.mainCanvas.create_line(left,i,right,i,tags=("grid","horizontal"),fill=self.gridcolor)
+ self.mainCanvas.tag_lower("grid")
+
+ def drawAxes(self):
+ self.mainCanvas.delete("axes")
+ if not self.axesVisible:
+ return
+ left,top,right,bottom = map(int,self.mainCanvas.cget("scrollregion").split())
+ self.mainCanvas.create_line(0,top,0,bottom,tags=("axes","yaxis"),fill=self.axiscolor)
+ self.mainCanvas.create_line(left,0,right,0,tags=("axes","xaxis"),fill=self.axiscolor)
+ axisxspace = int(self.magnification*self.axisxspace)
+ axisyspace = int(self.magnification*self.axisyspace)
+ if axisxspace >= 3 and axisyspace >= 3:
+ for i in range(axisxspace,right,axisxspace):
+ self.mainCanvas.create_line(i,-5,i,5,tags=("axes","xaxis-ticks"),fill=self.tickcolor)
+ for i in range(-axisxspace,left,-axisxspace):
+ self.mainCanvas.create_line(i,-5,i,5,tags=("axes","xaxis-ticks"),fill=self.tickcolor)
+ for i in range(-axisyspace,top,-axisyspace):
+ self.mainCanvas.create_line(-5,i,5,i,tags=("axes","yaxis-ticks"),fill=self.tickcolor)
+ for i in range(axisyspace,bottom,axisyspace):
+ self.mainCanvas.create_line(-5,i,5,i,tags=("axes","yaxis-ticks"),fill=self.tickcolor)
+ self.mainCanvas.tag_lower("axes")
+
+ def updateCanvasSize(self,left=-200,top=-200,right=200,bottom=200):
+ self.parent.update_idletasks()
+ bbox = self.mainCanvas.bbox("drawn || image || node || precontrol || postcontrol")
+ if bbox == None:
+ bbox = (0,0,0,0)
+ #(topleft, bottomright)
+ left = min(bbox[0],left)
+ top = min(bbox[1],top)
+ right = max(bbox[2],right)
+ bottom = max(bbox[3],bottom)
+ w,h = self.mainCanvas.winfo_width(),self.mainCanvas.winfo_height()
+ if right-left < w:
+ extraw = w-(right-left)
+ right += extraw/2
+ left -= extraw/2
+ if bottom-top < h:
+ extrah = h-(bottom-top)
+ bottom += extrah/2
+ top -= extrah/2
+ self.mainCanvas.config(scrollregion=(left,top,right,bottom))
+ #self.mainCanvas.xview(MOVETO,float(split(self.mainCanvas["scrollregion"])[0]))
+ #self.mainCanvas.yview(MOVETO,float(split(self.mainCanvas["scrollregion"])[1]))
+ #self.mainCanvas.xview(MOVETO,(left+right)/2)
+ #self.mainCanvas.yview(MOVETO,(top+bottom)/2)
+ self.drawAxes()
+ self.drawGrid()
+
+ def bindEvents(self,tagorID):
+ if tagorID == None:
+ return
+ self.mainCanvas.tag_bind(tagorID,"<Control-Button-1>",self.itemToggleSelect)
+ self.mainCanvas.tag_bind(tagorID,"<Button-1>",self.itemSelect)
+ self.mainCanvas.tag_bind(tagorID,"<ButtonRelease-1>",self.itemMouseUp)
+ self.mainCanvas.tag_bind(tagorID,"<Double-Button-1>",self.itemEditEvt)
+ self.mainCanvas.tag_bind(tagorID,"<B1-Motion>",self.itemDrag)
+ self.mainCanvas.tag_bind(tagorID,"<Delete>",self.itemDelete)
+ self.mainCanvas.tag_bind(tagorID,"<Enter>",self.itemHighlight)
+ self.mainCanvas.tag_bind(tagorID,"<Button-3>",self.itemCanvasMenuPopup)
+
+ def bindItemEvents(self,item):
+ if item == None:
+ return
+ if isinstance(item,xasyScript) or isinstance(item,xasyText):
+ for image in item.imageList:
+ self.bindEvents(image.IDTag)
+ else:
+ self.bindEvents(item.IDTag)
+
+ def canQuit(self,force=False):
+ #print "Quitting"
+ if not force and not self.testOrAcquireLock():
+ return
+ try:
+ self.releaseLock()
+ except:
+ pass
+ if self.undoRedoStack.changesMade():
+ result = tkMessageBox._show("xasy","File has been modified.\nSave changes?",icon=tkMessageBox.QUESTION,type=tkMessageBox.YESNOCANCEL)
+ if str(result) == tkMessageBox.CANCEL:
+ return
+ elif result == tkMessageBox.YES:
+ self.fileSaveCmd()
+ try:
+ os.rmdir(getAsyTempDir())
+ except:
+ pass
+ self.parent.destroy()
+
+ def openFile(self,name):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock() #release the lock for loadFile
+ self.resetGUI()
+ self.loadFile(name)
+
+ def loadFile(self,name):
+ self.status.config(text="Loading "+name)
+ self.filename = os.path.abspath(name)
+ startQuickAsy()
+ self.retitle()
+ try:
+ try:
+ f = open(self.filename,'rt')
+ except:
+ if self.filename[-4:] == ".asy":
+ raise
+ else:
+ f = open(self.filename+".asy",'rt')
+ self.filename += ".asy"
+ self.retitle()
+ self.fileItems = xasyFile.parseFile(f)
+ f.close()
+ except IOError:
+ tkMessageBox.showerror("File Opening Failed.","File could not be opened.")
+ self.fileItems = []
+ except:
+ self.fileItems = []
+ self.autoMakeScript = True
+ if self.autoMakeScript or tkMessageBox.askyesno("Error Opening File", "File was not recognized as an xasy file.\nLoad as a script item?"):
+ try:
+ item = xasyScript(self.mainCanvas)
+ f.seek(0)
+ item.setScript(f.read())
+ self.addItemToFile(item)
+ except:
+ tkMessageBox.showerror("File Opening Failed.","Could not load as a script item.")
+ self.fileItems = []
+ self.populateCanvasWithItems()
+ self.populatePropertyList()
+ self.updateCanvasSize()
+
+ def populateCanvasWithItems(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.mainCanvas.delete("drawn || image")
+ self.itemCount = 0
+ for item in self.fileItems:
+ item.drawOnCanvas(self.mainCanvas,self.magnification,forceAddition=True)
+ self.bindItemEvents(item)
+ self.releaseLock()
+
+ def propListCountItem(self,item):
+ plist = self.propList.get(0,END)
+ count = 1
+ for text in plist:
+ if text.startswith(item):
+ count += 1
+ return count
+
+ def describeItem(self,item):
+ if isinstance(item,xasyScript):
+ return "Code Module "+str(self.propListCountItem("Code Module"))
+ elif isinstance(item,xasyText):
+ return "Text Label "+str(self.propListCountItem("Text Label"))
+ elif isinstance(item,xasyFilledShape):
+ return "Filled Shape "+str(self.propListCountItem("Filled Shape"))
+ elif isinstance(item,xasyShape):
+ return "Outline "+str(self.propListCountItem("Outline"))
+ else:
+ return "If this happened, the program is corrupt!"
+
+ def populatePropertyList(self):
+ self.propList.delete(0,END)
+ for item in self.fileItems:
+ self.propList.insert(0,self.describeItem(item))
+
+ def saveFile(self,name):
+ if(not self.testOrAcquireLock()):
+ return
+ f = open(name,"wt")
+ xasyFile.saveFile(f,self.fileItems)
+ f.close()
+ self.undoRedoStack.setCommitLevel()
+ self.retitle()
+ self.releaseLock()
+
+ #menu commands
+ def fileNewCmd(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ #print "Create New File"
+ if self.undoRedoStack.changesMade():
+ result = tkMessageBox._show("xasy","File has been modified.\nSave changes?",icon=tkMessageBox.QUESTION,type=tkMessageBox.YESNOCANCEL)
+ if str(result) == tkMessageBox.CANCEL:
+ return
+ elif result == tkMessageBox.YES:
+ self.fileSaveCmd()
+ self.resetGUI()
+
+ def fileOpenCmd(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ #print "Open a file"
+ if self.undoRedoStack.changesMade():
+ result = tkMessageBox._show("xasy","File has been modified.\nSave changes?",icon=tkMessageBox.QUESTION,type=tkMessageBox.YESNOCANCEL)
+ if str(result) == tkMessageBox.CANCEL:
+ return
+ elif result == tkMessageBox.YES:
+ self.fileSaveCmd()
+ filename=tkFileDialog.askopenfilename(filetypes=[("asy files","*.asy"),("All files","*")],title="Open File",parent=self.parent)
+ if type(filename) != type((0,)) and filename != None and filename != '':
+ self.filename = filename
+ self.openFile(self.filename)
+
+ def fileSaveCmd(self):
+ #print "Save current file"
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ if self.filename == None:
+ filename=tkFileDialog.asksaveasfilename(defaultextension=".asy",filetypes=[("asy files","*.asy")],initialfile="newDrawing.asy",parent=self.parent,title="Save File")
+ if type(filename) != type((0,)) and filename != None and filename != '':
+ self.filename = filename
+ if self.filename != None:
+ self.saveFile(self.filename)
+
+ def fileSaveAsCmd(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ #print "Save current file as"
+ filename=tkFileDialog.asksaveasfilename(defaultextension=".asy",filetypes=[("asy files","*.asy")],initialfile="newDrawing.asy",parent=self.parent,title="Save File")
+ if type(filename) != type((0,)) and filename != None and filename != '':
+ self.filename = filename
+ self.saveFile(self.filename)
+
+ #export the file
+ def exportEPS(self):
+ self.exportFile(self.filename,"eps")
+
+ def exportPDF(self):
+ self.exportFile(self.filename,"pdf")
+
+ def exportGIF(self):
+ self.exportFile(self.filename,"gif")
+
+ def exportPNG(self):
+ self.exportFile(self.filename,"png")
+
+ def exportSVG(self):
+ self.exportFile(self.filename,"svg")
+
+ def exportFile(self,inFile, outFormat):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ if inFile == None:
+ if tkMessageBox.askyesno("xasy","File has not been saved.\nSave?"):
+ self.fileSaveAsCmd()
+ inFile = self.filename
+ else:
+ return
+ elif self.undoRedoStack.changesMade():
+ choice = tkMessageBox._show("xasy","File has been modified.\nOnly saved changes can be exported.\nDo you want to save changes?",icon=tkMessageBox.QUESTION,type=tkMessageBox.YESNOCANCEL)
+ choice = str(choice)
+ if choice != tkMessageBox.YES:
+ return
+ else:
+ self.fileSaveCmd()
+ name = os.path.splitext(os.path.basename(self.filename))[0]+'.'+outFormat
+ outfilename = tkFileDialog.asksaveasfilename(defaultextension = '.'+outFormat,filetypes=[(outFormat+" files","*."+outFormat)],initialfile=name,parent=self.parent,title="Choose output file")
+ if type(outfilename)==type((0,)) or not outfilename or outfilename == '':
+ return
+ fullname = os.path.abspath(outfilename)
+ outName = os.path.basename(outfilename)
+ command=[xasyOptions.options['asyPath'],"-f"+outFormat,"-o"+fullname,inFile]
+ saver = subprocess.Popen(command,stdin=PIPE,stdout=PIPE,stderr=PIPE)
+ saver.wait()
+ if saver.returncode != 0:
+ tkMessageBox.showerror("Export Error","Export Error:\n"+saver.stdout.read()+saver.stderr.read())
+ self.status.config(text="Error exporting file")
+ else:
+ self.status.config(text="File exported successfully")
+
+ def fileExitCmd(self):
+ #print "Exit xasy"
+ self.canQuit()
+
+ def editUndoCmd(self):
+ if not self.editor == None:
+ return
+ if(not self.testOrAcquireLock()):
+ return
+ self.undoOperation()
+ self.releaseLock()
+
+ def editRedoCmd(self):
+ if not self.editor == None:
+ return
+ if(not self.testOrAcquireLock()):
+ return
+ self.redoOperation()
+ self.releaseLock()
+
+ def helpHelpCmd(self):
+ print "Get help on xasy"
+
+ def helpAsyDocCmd(self):
+ #print "Open documentation about Asymptote"
+ asyExecute("help;\n")
+
+ def helpAboutCmd(self):
+ tkMessageBox.showinfo("About xasy","A graphical interface for Asymptote "+xasyVersion)
+
+ def updateSelectedButton(self,newB):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ #disable switching modes during an incomplete drawing operation
+ if self.inDrawingMode:
+ return
+ self.selectedButton.config(relief = RAISED)
+ if newB == self.toolSelectButton or self.selectedButton == self.toolSelectButton:
+ self.mainCanvas.delete("highlightBox")
+ if self.editor != None:
+ self.editor.endEdit()
+ if self.editor.modified:
+ self.undoRedoStack.add(editDrawnItemAction(self,self.itemBeingEdited,copy.deepcopy(self.editor.shape),self.fileItems.index(self.editor.shape)))
+ if newB not in (self.toolSelectButton,self.toolMoveButton,self.toolHorizMoveButton,self.toolVertiMoveButton,self.toolRotateButton):
+ self.clearSelection()
+ self.selectedButton = newB
+ self.selectedButton.config(relief = SUNKEN)
+ self.status.config(text=self.toolDocs[newB])
+
+ #toolbar commands
+ def toolSelectCmd(self):
+ self.updateSelectedButton(self.toolSelectButton)
+ def toolMoveCmd(self):
+ self.updateSelectedButton(self.toolMoveButton)
+ def toolRotateCmd(self):
+ self.updateSelectedButton(self.toolRotateButton)
+ def toolVertiMoveCmd(self):
+ self.updateSelectedButton(self.toolVertiMoveButton)
+ def toolHorizMoveCmd(self):
+ self.updateSelectedButton(self.toolHorizMoveButton)
+ def toolDrawLinesCmd(self):
+ self.updateSelectedButton(self.toolDrawLinesButton)
+ def toolDrawBeziCmd(self):
+ self.updateSelectedButton(self.toolDrawBeziButton)
+ def toolDrawPolyCmd(self):
+ self.updateSelectedButton(self.toolDrawPolyButton)
+ def toolFillPolyCmd(self):
+ self.updateSelectedButton(self.toolFillPolyButton)
+ def toolDrawEllipCmd(self):
+ self.updateSelectedButton(self.toolDrawEllipButton)
+ def toolFillEllipCmd(self):
+ self.updateSelectedButton(self.toolFillEllipButton)
+ def toolDrawShapeCmd(self):
+ self.updateSelectedButton(self.toolDrawShapeButton)
+ def toolFillShapeCmd(self):
+ self.updateSelectedButton(self.toolFillShapeButton)
+ def toolTextCmd(self):
+ self.updateSelectedButton(self.toolTextButton)
+ def toolAsyCmd(self):
+ # ignore the command if we are too busy to process it
+ if not self.testOrAcquireLock():
+ return
+ self.updateSelectedButton(self.toolSelectButton)
+ self.clearSelection()
+ self.clearHighlight()
+ self.unbindGlobalEvents()
+ try:
+ self.getNewText("// enter your code here")
+ except Exception, e:
+ tkMessageBox.showerror('xasy Error',e.message)
+ else:
+ self.addItemToFile(xasyScript(self.mainCanvas))
+ text = self.newText
+ self.undoRedoStack.add(addScriptAction(self,self.fileItems[-1]))
+ self.fileItems[-1].setScript(text)
+ self.fileItems[-1].drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(self.fileItems[-1])
+ self.bindGlobalEvents()
+ self.releaseLock()
+ def toolRaiseCmd(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ if not self.inDrawingMode and self.editor == None:
+ itemList = []
+ indexList = []
+ for ID in self.mainCanvas.find_withtag("selectedItem"):
+ item = self.findItem(ID)
+ if item not in itemList:
+ itemList.append(item)
+ indexList.append(self.fileItems.index(item))
+ self.raiseSomething(item)
+ self.undoRedoStack.add(itemRaiseAction(self,itemList,indexList))
+ def toolLowerCmd(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ if not self.inDrawingMode and self.editor == None:
+ itemList = []
+ indexList = []
+ for ID in self.mainCanvas.find_withtag("selectedItem"):
+ item = self.findItem(ID)
+ if item not in itemList:
+ itemList.append(item)
+ indexList.append(self.fileItems.index(item))
+ self.lowerSomething(item)
+ self.undoRedoStack.add(itemLowerAction(self,itemList,indexList))
+ def itemRaise(self,event):
+ self.mainCanvas.tag_raise(CURRENT)
+ def itemLower(self,event):
+ self.mainCanvas.tag_lower(CURRENT)
+
+ #options bar commands
+ def setPenColCmd(self):
+ if not self.testOrAcquireLock():
+ return
+ old = self.penColor
+ self.penColor = xasyColorDlg(self.parent).getColor(self.penColor)
+ if self.penColor != old:
+ self.tkPenColor = RGB255hex(RGBreal255(self.penColor))
+ self.penColButton.config(activebackground=self.tkPenColor,
+ activeforeground=self.foregroundPenColor(self.tkPenColor))
+ self.showCurrentPen()
+ self.releaseLock()
+
+ def clearSelection(self):
+ self.hideSelectionBox()
+ self.mainCanvas.dtag("selectedItem","selectedItem")
+
+ def hideSelectionBox(self):
+ self.mainCanvas.itemconfigure("outlineBox",width=1,outline=self.backColor)
+ self.mainCanvas.tag_lower("outlineBox")
+ self.mainCanvas.coords("outlineBox",self.mainCanvas.bbox(ALL))
+
+ def showSelectionBox(self):
+ self.mainCanvas.itemconfigure("outlineBox",width=2,outline="#801111")
+ self.mainCanvas.tag_raise("outlineBox")
+
+ def setSelection(self,what):
+ self.mainCanvas.addtag_withtag("selectedItem",what)
+ self.updateSelection()
+ if self.selectedButton == self.toolSelectButton and len(self.mainCanvas.find_withtag("selectedItem")) > 0:
+ self.updateSelectedButton(self.toolMoveButton)
+
+ def unSelect(self,what):
+ self.mainCanvas.dtag(what,"selectedItem")
+ self.updateSelection()
+
+ def updateSelection(self):
+ self.clearHighlight()
+ theBbox = self.mainCanvas.bbox("selectedItem")
+ if theBbox != None:
+ theBbox = (theBbox[0]-2,theBbox[1]-2,theBbox[2]+2,theBbox[3]+2)
+ self.mainCanvas.coords("outlineBox",theBbox)
+ self.showSelectionBox()
+ else:
+ self.clearSelection()
+
+ #event handlers
+ def updateZoom(self):
+ self.zoomMenu.config(state=DISABLED)
+ self.magnification = self.magVal.get()/100.0
+ if self.magnification != self.previousZoom:
+ self.populateCanvasWithItems()
+ self.updateCanvasSize()
+ self.updateSelection()
+ self.drawAxes()
+ self.drawGrid()
+ self.previousZoom = self.magnification
+ self.zoomMenu.config(state=NORMAL)
+
+ def zoomViewCmd(self,*args):
+ magnification = self.magVal.get()/100.0
+ self.updateZoom();
+
+ def selectItem(self,item):
+ self.clearSelection()
+ if isinstance(item,xasyScript) or isinstance(item,xasyText):
+ for image in item.imageList:
+ self.setSelection(image.IDTag)
+ else:
+ self.setSelection(item.IDTag)
+
+ def propSelect(self,event):
+ items = map(int, self.propList.curselection())
+ if len(items)>0:
+ try:
+ self.selectItem(self.fileItems[len(self.fileItems)-items[0]-1])
+ except:
+ raise
+
+ def findItem(self,ID):
+ for item in self.fileItems:
+ if isinstance(item,xasyScript) or isinstance(item,xasyText):
+ for image in item.imageList:
+ if image.IDTag == ID:
+ return item
+ else:
+ if item.IDTag == ID:
+ return item
+ raise Exception,"Illegal operation: Item with matching ID could not be found."
+
+ def findItemImageIndex(self,item,ID):
+ count = 0
+ for image in item.imageList:
+ if image.IDTag == ID:
+ return count
+ else:
+ count += 1
+ raise Exception,"Illegal operation: Image with matching ID could not be found."
+ return None
+
+ def raiseSomething(self,item,force=False):
+ if self.fileItems[-1] != item or force:
+ index = len(self.fileItems)-self.fileItems.index(item)-1
+ text = self.propList.get(index)
+ self.propList.delete(index)
+ self.propList.insert(0,text)
+ for i in range(self.fileItems.index(item),len(self.fileItems)-1):
+ self.fileItems[i] = self.fileItems[i+1]
+ self.fileItems[-1] = item
+ if isinstance(item,xasyScript) or isinstance(item,xasyText):
+ for im in item.imageList:
+ if im.IDTag != None:
+ self.mainCanvas.tag_raise(im.IDTag)
+ else:
+ if item.IDTag != None:
+ self.mainCanvas.tag_raise(item.IDTag)
+
+ def lowerSomething(self,item):
+ if self.fileItems[0] != item:
+ index = len(self.fileItems)-self.fileItems.index(item)-1
+ text = self.propList.get(index)
+ self.propList.delete(index)
+ self.propList.insert(END,text)
+ indices = range(self.fileItems.index(item))
+ indices.reverse()
+ for i in indices:
+ self.fileItems[i+1] = self.fileItems[i]
+ self.fileItems[0] = item
+ if isinstance(item,xasyScript) or isinstance(item,xasyText):
+ item.imageList.reverse()
+ for im in item.imageList:
+ if im.IDTag != None:
+ self.mainCanvas.tag_lower(im.IDTag)
+ item.imageList.reverse()
+ else:
+ if item.IDTag != None:
+ self.mainCanvas.tag_lower(item.IDTag)
+ self.mainCanvas.tag_lower("axes || grid")
+
+ def translateSomething(self,ID,translation,specificItem=None,specificIndex=None):
+ transform = asyTransform((translation[0],translation[1],1,0,0,1))
+ if ID == -1:
+ item = specificItem
+ else:
+ item = self.findItem(ID)
+ if isinstance(item,xasyText) or isinstance(item,xasyScript):
+ if ID == -1:
+ index = specificIndex
+ else:
+ index = self.findItemImageIndex(item,ID)
+ try:
+ original = item.transform[index]
+ except:
+ original = identity()
+ item.transform[index] = transform*original
+ bbox = item.imageList[index].originalImage.bbox
+ item.imageList[index].originalImage.bbox = bbox[0]+translation[0],bbox[1]+translation[1],bbox[2]+translation[0],bbox[3]+translation[1]
+ else:
+ item.transform = [transform*item.transform[0]]
+
+ def makeRotationMatrix(self,theta,origin):
+ rotMat = (math.cos(theta),-math.sin(theta),math.sin(theta),math.cos(theta))
+ shift = asyTransform((0,0,1-rotMat[0],-rotMat[1],-rotMat[2],1-rotMat[3]))*origin
+ return asyTransform((shift[0],shift[1],rotMat[0],rotMat[1],rotMat[2],rotMat[3]))
+
+ def rotateSomething(self,ID,theta,origin,specificItem=None,specificIndex=None):
+ #print "Rotating by",theta*180.0/math.pi,"around",origin
+ rotMat = self.makeRotationMatrix(theta,(origin[0]/self.magnification,origin[1]/self.magnification))
+ #print rotMat
+ if ID == -1:
+ item = specificItem
+ else:
+ item = self.findItem(ID)
+ if isinstance(item,xasyText) or isinstance(item,xasyScript):
+ #transform the image
+ if ID == -1:
+ index = specificIndex
+ else:
+ index = self.findItemImageIndex(item,ID)
+ try:
+ original = item.transform[index]
+ except:
+ original = identity()
+ oldBbox = item.imageList[index].originalImage.bbox
+ oldBbox = (oldBbox[0],-oldBbox[1],oldBbox[2],-oldBbox[3])
+ item.transform[index] = rotMat*item.transform[index]
+ item.transform[index] = rotMat*original
+ item.imageList[index].originalImage.theta += theta
+ item.imageList[index].image = item.imageList[index].originalImage.rotate(item.imageList[index].originalImage.theta*180.0/math.pi,expand=True,resample=Image.BICUBIC)
+ item.imageList[index].itk = ImageTk.PhotoImage(item.imageList[index].image)
+ self.mainCanvas.itemconfigure(ID,image=item.imageList[index].itk)
+ #the image has been rotated in place
+ #now, compensate for any resizing and shift to the correct location
+ #
+ # p0 --- p1 p1
+ # | | ---> / \
+ # p2 --- p3 p0 p3
+ # \ /
+ # p2
+ #
+ rotMat2 = self.makeRotationMatrix(item.imageList[index].originalImage.theta,origin)
+ p0 = rotMat2*(oldBbox[0],-oldBbox[3])#switch to usual coordinates
+ p1 = rotMat2*(oldBbox[2],-oldBbox[3])
+ p2 = rotMat2*(oldBbox[0],-oldBbox[1])
+ p3 = rotMat2*(oldBbox[2],-oldBbox[1])
+ newTopLeft = (min(p0[0],p1[0],p2[0],p3[0]),-max(p0[1],p1[1],p2[1],p3[1]))#switch back to screen coords
+ shift = (newTopLeft[0]-oldBbox[0],newTopLeft[1]-oldBbox[3])
+ #print theta*180.0/math.pi,origin,oldBbox,newTopLeft,shift
+ #print item.imageList[index].originalImage.size
+ #print item.imageList[index].image.size
+ #print
+ self.mainCanvas.coords(ID,oldBbox[0]+shift[0],oldBbox[3]+shift[1])
+ else:
+ #transform each point of the object
+ xform = rotMat*item.transform[0]
+ item.transform = [identity()]
+ for i in range(len(item.path.nodeSet)):
+ if item.path.nodeSet[i] != 'cycle':
+ item.path.nodeSet[i] = xform*item.path.nodeSet[i]
+ for i in range(len(item.path.controlSet)):
+ item.path.controlSet[i][0] = xform*item.path.controlSet[i][0]
+ item.path.controlSet[i][1] = xform*item.path.controlSet[i][1]
+ item.drawOnCanvas(self.mainCanvas,self.magnification)
+
+ def deleteItem(self,item):
+ if isinstance(item,xasyScript) or isinstance(item,xasyText):
+ if isinstance(item,xasyScript):
+ self.undoRedoStack.add(deleteScriptAction(self,item,self.fileItems.index(item)))
+ else:
+ self.undoRedoStack.add(deleteLabelAction(self,item,self.fileItems.index(item)))
+ for image in item.imageList:
+ self.mainCanvas.delete(image.IDTag)
+ else:
+ if isinstance(item,xasyDrawnItem):
+ self.undoRedoStack.add(deleteDrawnItemAction(self,item,self.fileItems.index(item)))
+ self.mainCanvas.delete(item.IDTag)
+ self.fileItems.remove(item)
+ self.populatePropertyList()
+ self.clearSelection()
+
+ def deleteSomething(self,ID):
+ self.clearSelection()
+ self.clearHighlight()
+ if self.editor != None:
+ self.editor.endEdit()
+ if self.editor.modified:
+ self.undoRedoStack.add(editDrawnItemAction(self,self.itemBeingEdited,copy.deepcopy(self.editor.shape),self.fileItems.index(self.editor.shape)))
+ item = self.findItem(ID)
+ #save an event on the undoredo stack
+ if isinstance(item,xasyScript):
+ index = self.findItemImageIndex(item,ID)
+ item.transform[index].deleted = True
+ else:
+ if isinstance(item,xasyText):
+ self.undoRedoStack.add(deleteLabelAction(self,item,self.fileItems.index(item)))
+ elif isinstance(item,xasyDrawnItem):
+ self.undoRedoStack.add(deleteDrawnItemAction(self,item,self.fileItems.index(item)))
+ self.fileItems.remove(item)
+ self.mainCanvas.delete(ID)
+ self.populatePropertyList()
+
+ def scriptEditThread(self,oldText):
+ try:
+ self.newText = xasyCodeEditor.getText(oldText)
+ except:
+ self.newText = -1
+
+ def getNewText(self,oldText):
+ editThread = threading.Thread(target=self.scriptEditThread,args=(oldText,))
+ editThread.start()
+ while editThread.isAlive():
+ time.sleep(0.05)
+ self.parent.update()
+ editThread.join()
+ if type(self.newText)==type(-1):
+ self.newText = ''
+ raise Exception('Error launching external editor. Please check xasy options.')
+
+ def itemEdit(self,item):
+ # are we too busy?
+ if not self.testOrAcquireLock():
+ return
+ self.updateSelectedButton(self.toolSelectButton)
+ if isinstance(item,xasyScript):
+ self.unbindGlobalEvents()
+ oldText = item.script
+ try:
+ self.getNewText(oldText)
+ except Exception,e:
+ tkMessageBox.showerror('xasy Error',e.message)
+ else:
+ if self.newText != oldText:
+ self.undoRedoStack.add(editScriptAction(self,item,self.newText,oldText))
+ item.setScript(self.newText)
+ item.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(item)
+ self.bindGlobalEvents()
+ elif isinstance(item,xasyText):
+ theText = tkSimpleDialog.askstring(title="Xasy - Text",prompt="Enter text to display:",initialvalue=item.label.text,parent=self.parent)
+ if theText != None and theText != "":
+ self.undoRedoStack.add(editLabelTextAction(self,item,theText,item.label.text))
+ item.label.text = theText
+ item.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(item)
+ elif isinstance(item,xasyShape):
+ self.clearSelection()
+ self.clearHighlight()
+ self.itemBeingEdited = copy.deepcopy(item)
+ self.editor = xasyBezierEditor(self,item,self.mainCanvas)
+ self.updateSelection()
+ self.releaseLock()
+
+ def itemEditEvt(self,event):
+ if not self.inDrawingMode:
+ ID = self.mainCanvas.find_withtag(CURRENT)[0]
+ item = self.findItem(ID)
+ self.itemEdit(item)
+
+ def itemDrag(self,event):
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ if self.selectedButton not in [self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton]:
+ return
+ if "selectedItem" in self.mainCanvas.gettags(CURRENT):
+ self.amDragging = True
+ for ID in self.mainCanvas.find_withtag("selectedItem"):
+ transform = identity()
+ if self.selectedButton == self.toolMoveButton:
+ translation = (x0-self.dragStartx,-(y0-self.dragStarty))
+ elif self.selectedButton == self.toolVertiMoveButton:
+ translation = (0,-(y0-self.dragStarty))
+ elif self.selectedButton == self.toolHorizMoveButton:
+ translation = (x0-self.dragStartx,0)
+ self.translateSomething(ID,(translation[0]/self.magnification,translation[1]/self.magnification))
+ self.mainCanvas.move(ID,translation[0],-translation[1])
+ self.updateSelection()
+ self.updateCanvasSize()
+ self.distanceDragged = (self.distanceDragged[0]+translation[0],self.distanceDragged[1]-translation[1])
+ self.dragStartx,self.dragStarty = x0,y0
+
+ def itemMouseUp(self,event):
+ self.freeMouseDown = True
+ if self.amDragging:
+ IDList = self.mainCanvas.find_withtag("selectedItem")
+ itemList = []
+ indexList = []
+ for ID in IDList:
+ item = self.findItem(ID)
+ if item not in itemList:
+ itemList.append(item)
+ try:
+ indexList.append([self.findItemImageIndex(item,ID)])
+ except:
+ indexList.append([None])
+ else:
+ indexList[itemList.index(item)].append(self.findItemImageIndex(item,ID))
+ self.undoRedoStack.add(translationAction(self,itemList,indexList,(self.distanceDragged[0],-self.distanceDragged[1])))
+ self.amDragging = False
+
+ def itemSelect(self,event):
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ self.dragStartx,self.dragStarty = x0,y0
+ self.distanceDragged = (0,0)
+ if self.selectedButton in [self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton]:
+ self.freeMouseDown = False
+ if self.selectedButton == self.toolSelectButton or (len(self.mainCanvas.find_withtag("selectedItem"))<=1 and self.selectedButton in [self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton]):
+ self.clearSelection()
+ self.setSelection(CURRENT)
+
+ def itemToggleSelect(self,event):
+ #print "control click"
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ if self.selectedButton in [self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton]:
+ self.freeMouseDown = False
+ self.dragStartx,self.dragStarty = x0,y0
+ if "selectedItem" in self.mainCanvas.gettags(CURRENT):
+ self.unSelect(CURRENT)
+ else:
+ self.setSelection(CURRENT)
+
+ def itemDelete(self,event):
+ if(not self.testOrAcquireLock()):
+ return
+ itemList = []
+ self.undoRedoStack.add(endActionGroup)
+ for ID in self.mainCanvas.find_withtag("selectedItem"):
+ item = self.findItem(ID)
+ if isinstance(item,xasyScript):
+ index = self.findItemImageIndex(item,ID)
+ if item not in itemList:
+ itemList.append([item,[index],[item.transform[index]]])
+ else:
+ x = None
+ for i in itemList:
+ if i[0] == item:
+ x = i
+ x[1].append(index)
+ x[2].append(item.transform[index])
+ self.deleteSomething(ID)
+ for entry in itemList:
+ self.undoRedoStack.add(deleteScriptItemAction(self,entry[0],entry[1],entry[2]))
+ self.undoRedoStack.add(beginActionGroup)
+ self.clearSelection()
+ self.releaseLock()
+
+ def itemMotion(self,event):
+ pass
+
+ def itemHighlight(self,event):
+ if self.selectedButton in [self.toolSelectButton] and self.editor == None:
+ box = self.mainCanvas.bbox(CURRENT)
+ box = (box[0]-2,box[1]-2,box[2]+2,box[3]+2)
+ if len(self.mainCanvas.find_withtag("highlightBox"))==0:
+ self.mainCanvas.create_rectangle(box,tags="highlightBox",width=2,outline="red")
+ else:
+ self.mainCanvas.tag_raise("highlightBox")
+ self.mainCanvas.coords("highlightBox",*box)
+ self.mainCanvas.tag_bind("highlightBox","<Leave>",self.itemUnHighlight)
+
+ def itemUnHighlight(self,event):
+ self.clearHighlight()
+
+ def clearHighlight(self):
+ self.mainCanvas.delete("highlightBox")
+
+ def itemLeftDown(self,event):
+ pass
+
+ def itemLeftUp(self,event):
+ pass
+
+ def itemRightDown(self,event):
+ pass
+
+ def itemRightUp(self,event):
+ pass
+
+ def canvMotion(self,event):
+ self.coords.config(
+ text="(%.3f,%.3f)"%(self.mainCanvas.canvasx(event.x)/self.magnification,-self.mainCanvas.canvasy(event.y)/self.magnification)
+ )
+
+ def addItemToFile(self,item):
+ self.fileItems.append(item)
+ self.propList.insert(0,self.describeItem(item))
+ self.updateCanvasSize()
+
+ def startDraw(self,event):
+ # don't start if we can't finish
+ if not self.testOrAcquireLock() and not self.inDrawingMode:
+ return
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ #self.mainCanvas.create_oval(x,y,x,y,width=5)
+ if self.selectedButton == self.toolDrawEllipButton:
+ pass
+ elif self.selectedButton == self.toolFillEllipButton:
+ pass
+ elif self.selectedButton == self.toolTextButton:
+ theText = tkSimpleDialog.askstring(title="Xasy - Text",prompt="Enter text to display:",initialvalue="",parent=self.parent)
+ if theText != None and theText != "":
+ theItem = xasyText(theText,(x,-y),asyPen(self.penColor,self.penWidth,self.penOptions))
+ theItem.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(theItem)
+ self.addItemToFile(theItem)
+ self.undoRedoStack.add(addLabelAction(self,theItem))
+ self.releaseLock()
+ self.updateSelectedButton(self.toolSelectButton)
+ elif self.selectedButton in [self.toolDrawLinesButton,self.toolDrawBeziButton,self.toolDrawPolyButton,self.toolDrawShapeButton,self.toolFillPolyButton,self.toolFillShapeButton]:
+ self.inDrawingMode = True
+ try:
+ if len(self.itemBeingDrawn.path.nodeSet) == 0:
+ raise Exception
+ else:
+ if self.selectedButton in [self.toolDrawLinesButton,self.toolDrawPolyButton,self.toolFillPolyButton]:
+ self.itemBeingDrawn.appendPoint((x,-y),'--')
+ else:#drawBezi,drawShape,fillShape
+ self.itemBeingDrawn.appendPoint((x,-y),'..')
+ except:
+ path = asyPath()
+ if self.selectedButton == self.toolDrawLinesButton:
+ path.initFromNodeList([(x,-y),(x,-y)],['--'])
+ elif self.selectedButton == self.toolDrawBeziButton:
+ path.initFromNodeList([(x,-y),(x,-y)],['..'])
+ elif self.selectedButton == self.toolDrawPolyButton or self.selectedButton == self.toolFillPolyButton:
+ path.initFromNodeList([(x,-y),(x,-y),'cycle'],['--','--'])
+ elif self.selectedButton == self.toolDrawShapeButton or self.selectedButton == self.toolFillShapeButton:
+ path.initFromNodeList([(x,-y),(x,-y),'cycle'],['..','..'])
+ if self.selectedButton in [self.toolDrawLinesButton,self.toolDrawBeziButton,self.toolDrawPolyButton,self.toolDrawShapeButton]:
+ self.itemBeingDrawn = xasyShape(path,pen=asyPen(self.penColor,self.penWidth,self.penOptions))
+ else:
+ if self.penOptions.find("fillrule") != -1 or self.penOptions.find("evenodd") != -1 or self.penOptions.find("zerowinding") != -1:
+ options = self.penOptions
+ else:
+ options = "evenodd"
+ self.itemBeingDrawn = xasyFilledShape(path,pen=asyPen(self.penColor,self.penWidth,options))
+ self.itemBeingDrawn.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(self.itemBeingDrawn)
+ self.mainCanvas.bind("<Motion>",self.extendDraw)
+
+ def extendDraw(self,event):
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ tags = self.mainCanvas.gettags("itemBeingDrawn")
+ self.itemBeingDrawn.setLastPoint((x,-y))
+ self.itemBeingDrawn.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.canvMotion(event)
+
+ def endDraw(self,event):
+ if not self.inDrawingMode or self.itemBeingDrawn == None:
+ return
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ #if self.selectedButton in [self.toolDrawLinesButton,self.toolDrawPolyButton,self.toolFillPolyButton]:
+ #self.itemBeingDrawn.appendPoint((x,-y),'--')
+ #else:
+ #self.itemBeingDrawn.appendPoint((x,-y),'..')
+
+ #only needed for certain key bindings when startDraw is triggered right before an endDraw
+ #e.g.: single click: startDraw, double click: endDraw
+ self.itemBeingDrawn.removeLastPoint()
+ self.itemBeingDrawn.setLastPoint((x,-y))
+ self.itemBeingDrawn.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.addItemToFile(self.itemBeingDrawn)
+ self.undoRedoStack.add(addDrawnItemAction(self,self.itemBeingDrawn))
+ self.itemBeingDrawn = None
+ self.mainCanvas.dtag("itemBeingDrawn","itemBeingDrawn")
+ self.mainCanvas.bind("<Motion>",self.canvMotion)
+ self.inDrawingMode = False
+ self.releaseLock()
+
+ def canvLeftDown(self,event):
+ #print "Left Mouse Down"
+ self.selectDragStart = (self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y))
+ theBbox = self.mainCanvas.bbox("selectedItem")
+ if theBbox != None:
+ self.selectBboxMidpoint = (theBbox[0]+theBbox[2])/2.0,-(theBbox[1]+theBbox[3])/2.0
+ if self.freeMouseDown and self.editor != None:
+ self.editor.endEdit()
+ if self.editor.modified:
+ self.undoRedoStack.add(editDrawnItemAction(self,self.itemBeingEdited,copy.deepcopy(self.editor.shape),self.fileItems.index(self.editor.shape)))
+ self.editor = None
+ elif self.selectedButton in (self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton):
+ if self.freeMouseDown:
+ self.clearSelection()
+ self.dragSelecting = False
+ else:
+ self.startDraw(event)
+
+ def canvLeftUp(self,event):
+ #print "Left Mouse Up"
+ # if we're busy, ignore it
+ if not self.testOrAcquireLock():
+ return
+ self.freeMouseDown = True
+ if self.inRotatingMode:
+ for item in self.itemsBeingRotated:
+ item.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(item)
+ self.updateSelection()
+ self.itemsBeingRotated = []
+ self.inRotatingMode = False
+ if self.dragSelecting:
+ self.hideSelectionBox()
+ self.dragSelecting = False
+ self.mainCanvas.addtag_enclosed("enclosed",self.selectDragStart[0],self.selectDragStart[1],self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y))
+ for item in self.mainCanvas.find_withtag("enclosed"):
+ tags = self.mainCanvas.gettags(item)
+ if "drawn" not in tags and "image" not in tags:
+ self.mainCanvas.dtag(item,"enclosed")
+ self.mainCanvas.addtag_withtag("selectedItem","enclosed")
+ self.mainCanvas.dtag("enclosed","enclosed")
+ if self.selectedButton == self.toolSelectButton and len(self.mainCanvas.find_withtag("selectedItem")) > 0:
+ self.updateSelectedButton(self.toolMoveButton)
+ self.updateSelection()
+ self.releaseLock()
+
+ def canvDrag(self,event):
+ x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)
+ x = x0/self.magnification
+ y = y0/self.magnification
+ if self.selectedButton == self.toolSelectButton and self.editor == None:
+ self.mainCanvas.coords("outlineBox",self.selectDragStart[0],self.selectDragStart[1],x0,y0)
+ self.showSelectionBox()
+ self.dragSelecting = True
+ elif self.selectedButton == self.toolRotateButton and self.editor == None:
+ bbox = self.mainCanvas.bbox("selectedItem")
+ if bbox != None:
+ p1 = self.selectDragStart[0]-self.selectBboxMidpoint[0],-self.selectDragStart[1]-self.selectBboxMidpoint[1]
+ mp1 = math.sqrt(p1[0]**2+p1[1]**2)
+ p2 = x0-self.selectBboxMidpoint[0],-y0-self.selectBboxMidpoint[1]
+ mp2 = math.sqrt(p2[0]**2+p2[1]**2)
+ if mp1 != 0:
+ t1 = math.acos(p1[0]/mp1)
+ if p1[1] < 0:
+ t1 *= -1
+ else:
+ t1 = 0
+ if mp2 != 0:
+ t2 = math.acos(p2[0]/mp2)
+ if p2[1] < 0:
+ t2 *= -1
+ else:
+ t2 = 0
+ theta = t2-t1
+ self.selectDragStart = x0,y0
+ self.itemsBeingRotated = []
+ for ID in self.mainCanvas.find_withtag("selectedItem"):
+ self.rotateSomething(ID,theta,self.selectBboxMidpoint)
+ item = self.findItem(ID)
+ if not item in self.itemsBeingRotated:
+ self.itemsBeingRotated.append(item)
+ self.updateSelection()
+ self.updateCanvasSize()
+ if not self.inRotatingMode:
+ self.currentRotationAngle = theta
+ IDList = self.mainCanvas.find_withtag("selectedItem")
+ itemList = []
+ indexList = []
+ for ID in IDList:
+ item = self.findItem(ID)
+ if item not in itemList:
+ itemList.append(item)
+ try:
+ indexList.append([self.findItemImageIndex(item,ID)])
+ except:
+ indexList.append([None])
+ else:
+ indexList[itemList.index(item)].append(self.findItemImageIndex(item,ID))
+ self.undoRedoStack.add(rotationAction(self,itemList,indexList,self.currentRotationAngle,self.selectBboxMidpoint))
+ self.inRotatingMode = True
+ else:
+ self.currentRotationAngle += theta
+ self.undoRedoStack.undoStack[-1].angle = self.currentRotationAngle
+
+ def canvEnter(self,event):
+ self.freeMouseDown = True
+ event.widget.focus_set()
+
+ def canvLeave(self,event):
+ self.freeMouseDown = False
+
+ def canvRightDown(self,event):
+ pass
+ #print "Right Mouse Down"
+
+ def canvRightUp(self,event):
+ pass
+ #print "Right Mouse Up"
+
+ def configEvt(self,event):
+ self.updateCanvasSize()
+ self.sizePane()
+
+ def sizePane(self):
+ width = self.windowPane.winfo_width()-10
+ cwidth = min(int(0.87*self.windowPane.winfo_width()),width-75)
+ if self.paneVisible:
+ self.windowPane.paneconfigure(self.canvFrame,minsize=cwidth)
+ else:
+ self.windowPane.paneconfigure(self.canvFrame,minsize=width)
+ self.windowPane.paneconfigure(self.propFrame,minsize=75)
+
+ def togglePaneEvt(self,event):
+ self.paneVisible = not self.paneVisible
+ self.sizePane()
+
+ def popupDelete(self):
+ self.deleteItem(self.itemPopupMenu.item)
+
+ def popupEdit(self):
+ self.itemEdit(self.itemPopupMenu.item)
+
+ def popupViewCode(self):
+ tkMessageBox.showinfo("Item Code",self.itemPopupMenu.item.getCode())
+
+ def popupClearTransform(self):
+ self.undoRedoStack.add(clearItemTransformsAction(self,self.itemPopupMenu.item,copy.deepcopy(self.itemPopupMenu.item.transform)))
+ if isinstance(self.itemPopupMenu.item,xasyScript) or isinstance(self.itemPopupMenu.item,xasyText):
+ for i in range(len(self.itemPopupMenu.item.transform)):
+ self.itemPopupMenu.item.transform[i] = identity()
+ else:
+ self.itemPopupMenu.item.transform = [identity()]
+ self.popupRedrawItem()
+
+ def popupRedrawItem(self):
+ if not self.testOrAcquireLock():
+ return
+ self.clearSelection()
+ self.clearHighlight()
+ self.itemPopupMenu.item.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(self.itemPopupMenu.item)
+ self.updateCanvasSize()
+ self.releaseLock()
+
+ def hidePopupMenu(self):
+ try:
+ self.itemPopupMenu.unpost()
+ except:
+ pass
+
+ def itemMenuPopup(self,parent,item,x,y):
+ self.hidePopupMenu()
+ self.itemPopupMenu = Menu(parent,tearoff=False)
+ self.itemPopupMenu.add_command(label="Edit",command=self.popupEdit)
+ self.itemPopupMenu.add_command(label="Clear Transforms",command=self.popupClearTransform)
+ self.itemPopupMenu.add_command(label="Redraw",command=self.popupRedrawItem)
+ self.itemPopupMenu.add_command(label="View code",command=self.popupViewCode)
+ self.itemPopupMenu.add_separator()
+ self.itemPopupMenu.add_command(label="Delete",command=self.popupDelete)
+ self.itemPopupMenu.item = item
+ #self.itemPopupMenu.bind("<Leave>",lambda a:self.itemPopupMenu.unpost())
+ #self.itemPopupMenu.bind("<FocusOut>",lambda a:self.itemPopupMenu.unpost())
+ self.itemPopupMenu.post(x,y)
+
+ def itemPropMenuPopup(self,event):
+ try:
+ item = self.fileItems[len(self.fileItems)-int(self.propList.curselection()[0])-1]
+ self.itemMenuPopup(self.propList,item,event.x_root,event.y_root)
+ except:
+ pass
+
+ def itemCanvasMenuPopup(self,event):
+ if self.selectedButton in (self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton):
+ try:
+ item = self.findItem(self.mainCanvas.find_withtag(CURRENT)[0])
+ except:
+ item = None
+ if item != None:
+ self.itemMenuPopup(self.mainCanvas,item,event.x_root,event.y_root)
+
+ def editOptions(self):
+ if(not self.testOrAcquireLock()):
+ return
+ self.releaseLock()
+ xasyOptionsDialog.xasyOptionsDlg(self.parent)
+ self.applyOptions()
+
+ def resetOptions(self):
+ xasyOptions.setDefaults()
+ self.applyOptions()
+
+ def applyPenWidth(self):
+ self.pendingPenWidthChange = None
+ if self.validatePenWidth():
+ old = self.penWidth
+ self.penWidth = float(self.penWidthEntry.get())
+ if old != self.penWidth:
+ self.showCurrentPen()
+
+ def validatePenWidth(self):
+ text = self.penWidthEntry.get()
+ try:
+ width = float(text)
+ if width <= 0:
+ return False
+ else:
+ return True
+ except:
+ return False
+
+ def showCurrentPen(self):
+ mag = 1
+ width = self.penWidth
+ while width > 10:
+ width /= 2
+ mag *= 2
+ self.penDisp.itemconfigure("penDisp",width=width,fill=self.tkPenColor)
+ self.penDisp.itemconfigure("penMag",text="x%d"%mag)
+ #apply the new pen to any selected items
+ IDs = self.mainCanvas.find_withtag("selectedItem")
+ madeAChange = False
+ for ID in IDs:
+ item = self.findItem(ID)
+ if not isinstance(item,xasyScript):
+ if not madeAChange:
+ self.undoRedoStack.add(endActionGroup)
+ madeAChange = True
+ if isinstance(item,xasyText):
+ temp = item.label.pen
+ item.label.pen = asyPen(self.penColor,self.penWidth,self.penOptions)
+ item.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.bindItemEvents(item)
+ self.setSelection(item.imageList[0].IDTag)
+ self.undoRedoStack.add(editLabelPenAction(self,temp,asyPen(self.penColor,self.penWidth,self.penOptions),self.fileItems.index(item)))
+ else:
+ temp = copy.deepcopy(item)
+ item.pen = asyPen(self.penColor,self.penWidth,self.penOptions)
+ item.drawOnCanvas(self.mainCanvas,self.magnification)
+ self.undoRedoStack.add(editDrawnItemAction(self,temp,copy.deepcopy(item),self.fileItems.index(item)))
+ if madeAChange:
+ self.undoRedoStack.add(beginActionGroup)
+
+ def applyPenWidthEvt(self,event):
+ if not self.testOrAcquireLock():
+ return
+ self.applyPenWidth()
+ self.releaseLock()
+
+ def penWidthChanged(self,event):
+ if self.pendingPenWidthChange is not None:
+ self.penWidthEntry.after_cancel(self.pendingPenWidthChange)
+ self.pendingPenWidthChange = self.penWidthEntry.after(1000,self.applyPenWidth)
+
+ def applyPenOptEvt(self,event):
+ if not self.testOrAcquireLock():
+ return
+ self.applyPenOpt()
+ self.releaseLock()
+
+ def validatePenOpt(self):
+ try:
+ penTest = asyPen(self.penColor,self.penWidth,self.penOptEntry.get())
+ return True
+ except:
+ self.penOptEntry.select_range(0,END)
+ self.penOptEntry.delete(0,END)
+ self.penOptEntry.insert(END,"Invalid Pen Options")
+ self.penOptEntry.after(5000,self.clearInvalidOptEntry)
+ self.penOptions = ""
+ return False
+
+ def clearInvalidOptEntry(self):
+ self.penOptEntry.select_range(0,END)
+ self.penOptEntry.delete(0,END)
+
+ def applyPenOpt(self):
+ if self.validatePenOpt():
+ old = self.penOptions
+ self.penOptions = self.penOptEntry.get()
+ if old != self.penOptions:
+ self.showCurrentPen()
+
+ def undoOperation(self):
+ self.undoRedoStack.undo()
+
+ def redoOperation(self):
+ self.undoRedoStack.redo()
+
+ def resetStacking(self):
+ for item in self.fileItems:
+ self.raiseSomething(item,force=True)
diff --git a/Master/texmf-dist/asymptote/GUI/xasyOptions.py b/Master/texmf-dist/asymptote/GUI/xasyOptions.py
new file mode 100755
index 00000000000..4406687cbf4
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyOptions.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyOptions provides a mechanism for storing and restoring a user's
+# preferences.
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+###########################################################################
+
+import pickle
+import sys,os
+import errno
+
+defaultOptions = {
+ 'asyPath':'asy',
+ 'showDebug':False,
+ 'showGrid':False,
+ 'gridX':10,
+ 'gridY':10,
+ 'gridColor':'#eeeeee',
+ 'showAxes':True,
+ 'axisX':10,
+ 'axisY':10,
+ 'axesColor':'#cccccc',
+ 'tickColor':'#eeeeee',
+ 'defPenOptions':'',
+ 'defPenColor':'#000000',
+ 'defPenWidth':1.0,
+ 'externalEditor':''
+ }
+
+if sys.platform[:3] == "win":
+ defaultOptions['externalEditor'] = "notepad.exe"
+else:
+ defaultOptions['externalEditor'] = "emacs"
+
+
+options = defaultOptions.copy()
+
+def settingsFileLocation():
+ folder = ""
+ try:
+ folder = os.path.expanduser("~/.asy/")
+ except:
+ pass
+ return os.path.normcase(os.path.join(folder,"xasy.conf"))
+
+def setAsyPathFromWindowsRegistry():
+ try:
+ import _winreg as registry
+ #test both registry locations
+ try:
+ key = registry.OpenKey(registry.HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Asymptote")
+ options['asyPath'] = registry.QueryValueEx(key,"Path")[0]+"\\asy.exe"
+ registry.CloseKey(key)
+ except:
+ key = registry.OpenKey(registry.HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Asymptote")
+ options['asyPath'] = registry.QueryValueEx(key,"InstallLocation")[0]+"\\asy.exe"
+ registry.CloseKey(key)
+ except:
+ #looks like asy is not installed or this isn't Windows
+ pass
+
+def setDefaults():
+ global options
+ options = defaultOptions.copy()
+ if sys.platform[:3] == 'win': #for windows, wince, win32, etc
+ setAsyPathFromWindowsRegistry()
+ save()
+
+def load():
+ global options
+ fileName = settingsFileLocation()
+ if not os.path.exists(fileName):
+ #make folder
+ thedir = os.path.dirname(fileName)
+ if not os.path.exists(thedir):
+ try:
+ os.makedirs(thedir)
+ except:
+ raise Exception,"Could not create configuration folder"
+ if not os.path.isdir(thedir):
+ raise Exception,"Configuration folder path does not point to a folder"
+ setDefaults()
+ try:
+ f = open(fileName,"rb")
+ newOptions = pickle.load(f)
+ for key in options.keys():
+ if type(newOptions[key]) != type(options[key]):
+ raise Exception,"Bad type for entry in xasy settings"
+ options = newOptions
+ except:
+ setDefaults()
+
+def save():
+ global options
+ fileName = settingsFileLocation()
+ try:
+ f = open(fileName,"wb")
+ pickle.dump(options,f)
+ f.close()
+ except:
+ raise Exception,"Error saving preferences"
+
+load()
+
+if __name__=='__main__':
+ print settingsFileLocation()
+ print "Current content"
+ load()
+ print "Setting defaults"
+ setDefaults()
+ save()
+ load()
+ options['showAxes'] = options['showGrid'] = False
+ save()
+ print "Set to False"
+ load()
+ options['showAxes'] = options['showGrid'] = True
+ save()
+ print "Set to True"
+ load()
+ print options
diff --git a/Master/texmf-dist/asymptote/GUI/xasyOptionsDialog.py b/Master/texmf-dist/asymptote/GUI/xasyOptionsDialog.py
new file mode 100755
index 00000000000..c3162e8673b
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyOptionsDialog.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+###########################################################################
+#
+# xasyOptionsDialog implements a dialog window to allow users to edit
+# their preferences and specify program options
+#
+#
+# Author: Orest Shardt
+# Created: June 29, 2007
+#
+###########################################################################
+
+from Tkinter import *
+import xasyOptions
+import tkSimpleDialog
+import xasyColorPicker
+import tkMessageBox
+import tkFileDialog
+import tkColorChooser
+import os
+import sys
+
+class xasyOptionsDlg(tkSimpleDialog.Dialog):
+ """A dialog to interact with users about their preferred settings"""
+ def __init__(self,master=None):
+ tkSimpleDialog.Dialog.__init__(self,master,"xasy Options")
+
+ def body(self,master):
+ optFrame = Frame(master)
+ optFrame.grid(row=0,column=0,sticky=N+S+E+W)
+
+ asyGrp = LabelFrame(optFrame,text="Asymptote",padx=5,pady=5)
+ asyGrp.grid(row=0,column=0,sticky=E+W)
+ asyGrp.rowconfigure(0,weight=1)
+ asyGrp.rowconfigure(1,weight=1)
+ asyGrp.columnconfigure(0,weight=1)
+ asyGrp.columnconfigure(0,weight=2)
+ Label(asyGrp,text="Command").grid(row=0,column=0,sticky=W)
+ self.ap = Entry(asyGrp)
+ self.ap.insert(END,xasyOptions.options['asyPath'])
+ self.ap.grid(row=0,column=1,sticky=E+W)
+ Button(asyGrp,text="...",command=self.findAsyPath).grid(row=0,column=2,sticky=E+W)
+ self.showDebug = BooleanVar()
+ self.showDebug.set(xasyOptions.options['showDebug'])
+ self.sd = Checkbutton(asyGrp,text="Show debugging info in console",var=self.showDebug)
+ self.sd.grid(row=1,column=0,columnspan=2,sticky=W)
+
+ editGrp = LabelFrame(optFrame,text="External Editor",padx=5,pady=5)
+ editGrp.grid(row=1,column=0,sticky=E+W)
+ editGrp.rowconfigure(0,weight=1)
+ editGrp.rowconfigure(1,weight=1)
+ editGrp.columnconfigure(0,weight=1)
+ editGrp.columnconfigure(0,weight=2)
+ Label(editGrp,text="Program").grid(row=0,column=0,sticky=W)
+ self.ee = Entry(editGrp)
+ self.ee.insert(END,xasyOptions.options['externalEditor'])
+ self.ee.grid(row=0,column=1,sticky=E+W)
+ Button(editGrp,text="...",command=self.findEEPath).grid(row=0,column=2,sticky=E+W)
+
+ penGrp = LabelFrame(optFrame,text="Default Pen",padx=5,pady=5)
+ penGrp.grid(row=2,column=0,sticky=E+W)
+ penGrp.rowconfigure(0,weight=1)
+ penGrp.rowconfigure(1,weight=1)
+ penGrp.rowconfigure(2,weight=1)
+ penGrp.columnconfigure(1,weight=1)
+ Label(penGrp,text="Color").grid(row=0,column=0,sticky=E)
+ self.pc = xasyOptions.options['defPenColor']
+ Button(penGrp,text="Change",command=self.changePenColor).grid(row=0,column=1,sticky=W)
+ Label(penGrp,text="Width").grid(row=1,column=0,sticky=E)
+ self.pw = Entry(penGrp)
+ self.pw.insert(END,str(xasyOptions.options['defPenWidth']))
+ self.pw.grid(row=1,column=1,sticky=E+W)
+ Label(penGrp,text="Options").grid(row=2,column=0,sticky=E)
+ self.po = Entry(penGrp)
+ self.po.insert(END,xasyOptions.options['defPenOptions'])
+ self.po.grid(row=2,column=1,sticky=E+W)
+
+ dispGrp = LabelFrame(optFrame,text="Display Options",padx=5,pady=5)
+ dispGrp.grid(row=3,column=0,sticky=E+W)
+ dispGrp.rowconfigure(0,weight=1)
+ dispGrp.rowconfigure(1,weight=1)
+ dispGrp.rowconfigure(2,weight=1)
+ dispGrp.rowconfigure(3,weight=1)
+ dispGrp.columnconfigure(0,weight=1)
+ dispGrp.columnconfigure(1,weight=1)
+ dispGrp.columnconfigure(2,weight=1)
+ self.showAxes = BooleanVar()
+ self.showAxes.set(xasyOptions.options['showAxes'])
+ self.sa = Checkbutton(dispGrp,text="Show Axes",var=self.showAxes)
+ self.sa.grid(row=0,column=0,sticky=W)
+ self.ac = xasyOptions.options['axesColor']
+ Button(dispGrp,text="Color...",command=self.changeAxesColor).grid(row=1,column=0)
+ Label(dispGrp,text="x").grid(row=0,column=1,padx=5,sticky=E)
+ self.axs = Entry(dispGrp,width=6)
+ self.axs.insert(END,xasyOptions.options['axisX'])
+ self.axs.grid(row=0,column=2,sticky=W+E)
+ Label(dispGrp,text="y").grid(row=1,column=1,padx=5,sticky=E)
+ self.ays = Entry(dispGrp,width=6)
+ self.ays.insert(END,xasyOptions.options['axisY'])
+ self.ays.grid(row=1,column=2,sticky=W+E)
+
+ self.showGrid = BooleanVar()
+ self.showGrid.set(xasyOptions.options['showGrid'])
+ self.sg = Checkbutton(dispGrp,text="Show Grid",var=self.showGrid)
+ self.sg.grid(row=4,column=0,sticky=W)
+ self.gc = xasyOptions.options['gridColor']
+ Button(dispGrp,text="Color...",command=self.changeGridColor).grid(row=3,column=0)
+ Label(dispGrp,text="x").grid(row=2,column=1,padx=5,sticky=E)
+ self.gxs = Entry(dispGrp,width=6)
+ self.gxs.insert(END,xasyOptions.options['gridX'])
+ self.gxs.grid(row=2,column=2,sticky=W+E)
+ Label(dispGrp,text="y").grid(row=3,column=1,padx=5,sticky=E)
+ self.gys = Entry(dispGrp,width=6)
+ self.gys.insert(END,xasyOptions.options['gridY'])
+ self.gys.grid(row=3,column=2,sticky=W+E)
+
+ def findEEPath(self):
+ if sys.platform[:3] == 'win': #for windows, wince, win32, etc
+ file=tkFileDialog.askopenfile(filetypes=[("Programs","*.exe"),("All files","*")],title="Choose External Editor",parent=self)
+ else:
+ file=tkFileDialog.askopenfile(filetypes=[("All files","*")],title="Choose External Editor",parent=self)
+ if file != None:
+ name = os.path.abspath(file.name)
+ file.close()
+ self.ee.delete(0,END)
+ self.ee.insert(END,name)
+ self.validate()
+
+ def findAsyPath(self):
+ if sys.platform[:3] == 'win': #for windows, wince, win32, etc
+ file=tkFileDialog.askopenfile(filetypes=[("Programs","*.exe"),("All files","*")],title="Find Asymptote Executable",parent=self)
+ else:
+ file=tkFileDialog.askopenfile(filetypes=[("All files","*")],title="Find Asymptote Executable",parent=self)
+ if file != None:
+ name = os.path.abspath(file.name)
+ file.close()
+ self.ap.delete(0,END)
+ self.ap.insert(END,name)
+ self.validate()
+
+ def getAColor(self,color):
+ result = xasyColorPicker.xasyColorDlg(self).getColor(xasyColorPicker.makeRGBfromTkColor(color))
+ return xasyColorPicker.RGB255hex(xasyColorPicker.RGBreal255(result))
+
+ def changeAxesColor(self):
+ self.ac = self.getAColor(self.ac)
+
+ def changeGridColor(self):
+ self.gc = self.getAColor(self.gc)
+
+ def changePenColor(self):
+ self.pc = self.getAColor(self.pc)
+
+ def apply(self):
+ xasyOptions.options['externalEditor'] = self.ee.get()
+ xasyOptions.options['asyPath'] = self.ap.get()
+ xasyOptions.options['showDebug'] = bool(self.showDebug.get())
+
+ xasyOptions.options['defPenColor'] = self.pc
+ xasyOptions.options['defPenWidth'] = float(self.pw.get())
+ xasyOptions.options['defPenOptions'] = self.po.get()
+
+ xasyOptions.options['showAxes'] = bool(self.showAxes.get())
+ xasyOptions.options['axesColor'] = self.ac
+ xasyOptions.options['tickColor'] = self.ac
+ xasyOptions.options['axisX'] = int(self.axs.get())
+ xasyOptions.options['axisY'] = int(self.ays.get())
+ xasyOptions.options['showGrid'] = bool(self.showGrid.get())
+ xasyOptions.options['gridColor'] = self.gc
+ xasyOptions.options['gridX'] = int(self.gxs.get())
+ xasyOptions.options['gridY'] = int(self.gys.get())
+ xasyOptions.save()
+
+ def validateAColor(self,color):
+ hexdigits = '0123456789abcdef'
+ if len(self.pc) != 7 or self.pc[0] != '#' or sum([1 for a in self.pc[1:] if a in hexdigits]) != 6:
+ return False
+ else:
+ return True
+
+ def validate(self):
+ """Validate the data entered into the dialog"""
+ #validate the color
+ hexdigits = '0123456789abcdef'
+ if not self.validateAColor(self.pc):
+ tkMessageBox.showerror("xasy Options","Invalid pen color.\r\n"+self.pc,parent=self)
+ return False
+ #validate the width
+ try:
+ test = float(self.pw.get())
+ except:
+ tkMessageBox.showerror("xasy Options","Pen width must be a number.",parent=self)
+ return False
+
+ #validate the options
+ #nothing to do
+
+ #validate the axis spacing
+ try:
+ test = int(self.axs.get())
+ test = int(self.ays.get())
+ except:
+ tkMessageBox.showerror("xasy Options","Axes' x- and y-spacing must be numbers.",parent=self)
+ return False
+
+ #validate the grid spacing
+ try:
+ test = int(self.gxs.get())
+ test = int(self.gys.get())
+ except:
+ tkMessageBox.showerror("xasy Options","Grid's x- and y-spacing must be numbers.",parent=self)
+ return False
+
+ if not self.validateAColor(self.ac):
+ tkMessageBox.showerror("xasy Options","Invalid axis color.\r\n"+self.ac,parent=self)
+ return False
+
+ if not self.validateAColor(self.gc):
+ tkMessageBox.showerror("xasy Options","Invalid grid color.\r\n"+self.gc,parent=self)
+ return False
+
+ return True
+
+if __name__ == '__main__':
+ root = Tk()
+ xasyOptions.load()
+ d = xasyOptionsDlg(root)
+ print d.result
diff --git a/Master/texmf-dist/asymptote/GUI/xasyVersion.py b/Master/texmf-dist/asymptote/GUI/xasyVersion.py
new file mode 100755
index 00000000000..7349b08d046
--- /dev/null
+++ b/Master/texmf-dist/asymptote/GUI/xasyVersion.py
@@ -0,0 +1,2 @@
+#!/usr/bin/env python
+xasyVersion = "2.16"
diff --git a/Master/texmf-dist/asymptote/animate.asy b/Master/texmf-dist/asymptote/animate.asy
new file mode 100644
index 00000000000..586e71041b6
--- /dev/null
+++ b/Master/texmf-dist/asymptote/animate.asy
@@ -0,0 +1,3 @@
+usepackage("animate");
+import animation;
+
diff --git a/Master/texmf-dist/asymptote/animation.asy b/Master/texmf-dist/asymptote/animation.asy
new file mode 100644
index 00000000000..57e13ab6e03
--- /dev/null
+++ b/Master/texmf-dist/asymptote/animation.asy
@@ -0,0 +1,191 @@
+/*****
+ * animation.asy
+ * Andy Hammerlindl and John Bowman 2005/11/06
+ *
+ * Produce GIF, inline PDF, or other animations.
+ *****/
+
+// animation delay is in milliseconds
+real animationdelay=50;
+
+typedef frame enclosure(frame);
+
+frame NoBox(frame f) {
+ return f;
+}
+
+enclosure BBox(real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill) {
+ return new frame(frame f) {
+ box(f,xmargin,ymargin,p,filltype,above=false);
+ return f;
+ };
+}
+
+struct animation {
+ picture[] pictures;
+ string[] files;
+ int index;
+
+ string prefix;
+ bool global; // If true, use a global scaling for all frames; this requires
+ // extra memory since the actual shipout is deferred until all frames have
+ // been generated.
+
+ void operator init(string prefix="", bool global=true) {
+ prefix=replace(stripdirectory(outprefix(prefix))," ","_");
+ this.prefix=prefix;
+ this.global=global;
+ }
+
+ string basename(string prefix=stripextension(prefix)) {
+ return "_"+prefix;
+ }
+
+ string name(string prefix, int index) {
+ return stripextension(prefix)+"+"+string(index);
+ }
+
+ private string nextname() {
+ string name=basename(name(prefix,index));
+ ++index;
+ return name;
+ }
+
+ void shipout(string name=nextname(), frame f) {
+ string format=nativeformat();
+ plain.shipout(name,f,format=format,view=false);
+ files.push(name+"."+format);
+ shipped=false;
+ }
+
+ void add(picture pic=currentpicture, enclosure enclosure=NoBox) {
+ if(global) {
+ ++index;
+ pictures.push(pic.copy());
+ } else this.shipout(enclosure(pic.fit()));
+ }
+
+ void purge(bool keep=settings.keep) {
+ if(!keep) {
+ for(int i=0; i < files.length; ++i)
+ delete(files[i]);
+ }
+ }
+
+ int merge(int loops=0, real delay=animationdelay, string format="gif",
+ string options="", bool keep=settings.keep) {
+ string args="-loop " +(string) loops+" -delay "+(string)(delay/10)+
+ " -alpha Off -dispose Background "+options;
+ for(int i=0; i < files.length; ++i)
+ args += " " +files[i];
+ int rc=convert(args,prefix+"."+format,format=format);
+ this.purge(keep);
+ if(rc == 0) animate(file=prefix+"."+format,format=format);
+ else abort("merge failed");
+ return rc;
+ }
+
+ void glmovie(string prefix=prefix, projection P=currentprojection) {
+ if(!view() || settings.render == 0) return;
+ fit(prefix,pictures,view=true,P);
+ }
+
+ // Export all frames with the same scaling.
+ void export(string prefix=prefix, enclosure enclosure=NoBox,
+ bool multipage=false, bool view=false,
+ projection P=currentprojection) {
+ if(pictures.length == 0) return;
+ if(!global) multipage=false;
+ bool inlinetex=settings.inlinetex;
+ if(multipage)
+ settings.inlinetex=false;
+ frame multi;
+ frame[] fits=fit(prefix,pictures,view=false,P);
+ for(int i=0; i < fits.length; ++i) {
+ string s=name(prefix,i);
+ if(multipage) {
+ add(multi,enclosure(fits[i]));
+ newpage(multi);
+ files.push(s+"."+nativeformat());
+ } else {
+ if(pictures[i].empty3() || settings.render <= 0)
+ this.shipout(s,enclosure(fits[i]));
+ else // 3D frames
+ files.push(s+"."+nativeformat());
+ }
+ }
+ if(multipage) {
+ plain.shipout(prefix,multi,view=view);
+ settings.inlinetex=inlinetex;
+ }
+ shipped=true;
+ }
+
+ string load(int frames, real delay=animationdelay, string options="",
+ bool multipage=false) {
+ if(!global) multipage=false;
+ string s="\animategraphics["+options+"]{"+format("%.18f",1000/delay,"C")+
+ "}{"+basename();
+ if(!multipage) s += "+";
+ s += "}{0}{"+string(frames-1)+"}";
+ return s;
+ }
+
+ bool pdflatex()
+ {
+ return latex() && pdf();
+ }
+
+ string pdf(enclosure enclosure=NoBox, real delay=animationdelay,
+ string options="", bool keep=settings.keep, bool multipage=true) {
+ if(settings.inlinetex) multipage=true;
+ if(!global) multipage=false;
+ if(!pdflatex())
+ abort("inline pdf animations require -tex pdflatex or -tex xelatex");
+ if(settings.outformat != "") settings.outformat="pdf";
+
+ string filename=basename();
+ string pdfname=filename+".pdf";
+
+ if(global)
+ export(filename,enclosure,multipage=multipage);
+
+ shipped=false;
+
+ if(!keep) {
+ exitfcn currentexitfunction=atexit();
+ void exitfunction() {
+ if(currentexitfunction != null) currentexitfunction();
+ if(multipage || !settings.inlinetex)
+ this.purge();
+ if(multipage && !settings.inlinetex)
+ delete(pdfname);
+ }
+ atexit(exitfunction);
+ }
+
+ if(!multipage)
+ delete(pdfname);
+
+ return load(index,delay,options,multipage);
+ }
+
+ int movie(enclosure enclosure=NoBox, int loops=0, real delay=animationdelay,
+ string format=settings.outformat == "" ? "gif" : settings.outformat,
+ string options="", bool keep=settings.keep) {
+ if(global) {
+ if(format == "pdf") {
+ export(enclosure,multipage=true,view=true);
+ return 0;
+ }
+ export(enclosure);
+ }
+ return merge(loops,delay,format,options,keep);
+ }
+}
+
+animation operator init() {
+ animation a=animation();
+ return a;
+}
diff --git a/Master/texmf-dist/asymptote/annotate.asy b/Master/texmf-dist/asymptote/annotate.asy
new file mode 100644
index 00000000000..aba9309cb65
--- /dev/null
+++ b/Master/texmf-dist/asymptote/annotate.asy
@@ -0,0 +1,16 @@
+void annotate(picture pic=currentpicture, string title, string text,
+ pair position)
+{
+ pic.add(new void(frame f, transform t) {
+ position=t*position;
+ label(f,"\special{!/pdfmark where
+ {pop} {userdict /pdfmark /cleartomark load put} ifelse
+ [/Rect["+(string) position.x+" 0 0 "+(string) position.y+"]
+ /Subtype /Text
+ /Name /Comment
+ /Title ("+title+")
+ /Contents ("+text+")
+ /ANN pdfmark}");
+ },true);
+ draw(pic,position,invisible);
+}
diff --git a/Master/texmf-dist/asymptote/asy-init.el b/Master/texmf-dist/asymptote/asy-init.el
new file mode 100644
index 00000000000..0e3f178a228
--- /dev/null
+++ b/Master/texmf-dist/asymptote/asy-init.el
@@ -0,0 +1,4 @@
+(autoload 'asy-mode "asy-mode.el" "Asymptote major mode." t)
+(autoload 'lasy-mode "asy-mode.el" "hybrid Asymptote/Latex major mode." t)
+(autoload 'asy-insinuate-latex "asy-mode.el" "Asymptote insinuate LaTeX." t)
+(add-to-list 'auto-mode-alist '("\\.asy$" . asy-mode))
diff --git a/Master/texmf-dist/asymptote/asy-kate.sh b/Master/texmf-dist/asymptote/asy-kate.sh
new file mode 100644
index 00000000000..5f482b376d9
--- /dev/null
+++ b/Master/texmf-dist/asymptote/asy-kate.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+echo '<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<!-- based on asy-keywords.el and Highlighting file asymptote.xml by Christoph Hormann-->
+<language version="1.0" kateversion="3.2.2" name="asymptote" section="Sources" extensions="*.asy" mimetype="text/x-asymptote" licence="LGPL" author="Carsten Brenner">
+
+<highlighting>
+' > asymptote.xml
+
+# 1. Change Name of lists in <\list> <list name="...">
+# 2. tail to get rid of the first lines
+# 3. building the right line ending
+# 4-5. kill linebreaks
+# 6. change spaces into <\item><item>
+# 7. Undo change (7.) in 'list name'
+# 8. do some formatting
+
+cat asy-keywords.el | sed 's/^(.*\-\([^\-]*\)\-.*/\n<list name="\1"><item>/' | tail -14 | sed 's/ ))/<\/item><\/list>/' | tr '\n' '@' | sed 's/@//g' | sed 's/ /<\/item><item>/g' | sed 's/list<\/item><item>name/list name/g' | sed 's/></>\n</g' >> asymptote.xml
+
+echo '
+<contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <DetectSpaces />
+ <RegExpr attribute="Preprocessor" context="Outscoped" String="#\s*if\s+0" beginRegion="Outscoped" firstNonSpace="true" />
+ <DetectChar attribute="Preprocessor" context="Preprocessor" char="#" firstNonSpace="true" />
+ <StringDetect attribute="Region Marker" context="Region Marker" String="//BEGIN" beginRegion="Region1" firstNonSpace="true" />
+ <StringDetect attribute="Region Marker" context="Region Marker" String="//END" endRegion="Region1" firstNonSpace="true" />
+ <keyword attribute="Keyword" context="#stay" String="keyword" />
+ <keyword attribute="Extensions" context="#stay" String="extensions" />
+ <keyword attribute="Function" context="#stay" String="function" />
+ <keyword attribute="Data Type" context="#stay" String="type" />
+ <keyword attribute="Constants" context="#stay" String="constants" />
+ <keyword attribute="Variable" context="#stay" String="variable" />
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <DetectIdentifier />
+ <Float attribute="Float" context="#stay">
+ <AnyChar String="fF" attribute="Float" context="#stay"/>
+ </Float>
+ <HlCOct attribute="Octal" context="#stay"/>
+ <HlCHex attribute="Hex" context="#stay"/>
+ <Int attribute="Decimal" context="#stay">
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ <IncludeRules context="##Doxygen" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
+ <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]{|}~^&#59;"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#stay"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Region Marker" lineEndContext="#pop" name="Region Marker">
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+ <DetectSpaces />
+ <IncludeRules context="##Alerts" />
+ <DetectIdentifier />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
+ <IncludeRules context="##Alerts" />
+ <DetectIdentifier />
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ <RegExpr attribute="Preprocessor" context="Define" String="define.*((?=\\))"/>
+ <RegExpr attribute="Preprocessor" context="#stay" String="define.*"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&quot;" char1="&quot;"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
+ <IncludeRules context="##Doxygen" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*"/>
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Define">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" />
+ <DetectIdentifier />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped" >
+ <DetectSpaces />
+ <IncludeRules context="##Alerts" />
+ <DetectIdentifier />
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <IncludeRules context="##Doxygen" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="Outscoped" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#\s*(endif|else|elif)" endRegion="Outscoped" firstNonSpace="true" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern">
+ <DetectSpaces />
+ <IncludeRules context="##Alerts" />
+ <DetectIdentifier />
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <IncludeRules context="##Doxygen" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="Outscoped" firstNonSpace="true"/>
+ <RegExpr attribute="Comment" context="#pop" String="#\s*endif" endRegion="Outscoped" firstNonSpace="true"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Extensions" defStyleNum="dsKeyword" color="#0095ff" selColor="#ffffff" bold="1" italic="0"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Function" defStyleNum="dsFunction" />
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Prep. Lib" defStyleNum="dsOthers"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name="Region Marker" defStyleNum="dsRegionMarker" />
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Variable" defStyleNum="dsOthers" />
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" region="Comment"/>
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+ </language>' >> asymptote.xml
diff --git a/Master/texmf-dist/asymptote/asy-keywords.el b/Master/texmf-dist/asymptote/asy-keywords.el
new file mode 100644
index 00000000000..0bb5b66e7ab
--- /dev/null
+++ b/Master/texmf-dist/asymptote/asy-keywords.el
@@ -0,0 +1,17 @@
+;;
+;; This file is automatically generated by asy-list.pl.
+;; Changes will be overwritten.
+;;
+(defvar asy-keywords-version "2.16")
+
+(defvar asy-keyword-name '(
+and controls tension atleast curl if else while for do return break continue struct typedef new access import unravel from include quote static public private restricted this explicit true false null cycle newframe operator ))
+
+(defvar asy-type-name '(
+Braid FitResult Label Legend Segment Solution TreeNode abscissa arc arrowhead binarytree binarytreeNode block bool bool3 bounds bqe circle conic coord coordsys cputime ellipse file filltype frame grid3 guide horner hsv hyperbola indexedTransform int inversion key light line linefit marginT marker mass object pair parabola path path3 pen picture point position projection real revolution scaleT scientific segment side slice solution splitface string surface tensionSpecifier ticklocate ticksgridT tickvalues transform transformation tree triangle trilinear triple vector vertex void ))
+
+(defvar asy-function-name '(
+AND Arc ArcArrow ArcArrows Arrow Arrows Automatic AvantGarde BBox BWRainbow BWRainbow2 Bar Bars BeginArcArrow BeginArrow BeginBar BeginDotMargin BeginMargin BeginPenMargin Blank Bookman Bottom BottomTop Bounds Break Broken BrokenLog CLZ CTZ Ceil Circle CircleBarIntervalMarker Cos Courier CrossIntervalMarker DOSendl DOSnewl DefaultFormat DefaultLogFormat Degrees Dir DotMargin DotMargins Dotted Draw Drawline Embed EndArcArrow EndArrow EndBar EndDotMargin EndMargin EndPenMargin Fill FillDraw Floor Format Full Gaussian Gaussrand Gaussrandpair Gradient Grayscale Helvetica Hermite HookHead InOutTicks InTicks Jn Label Landscape Left LeftRight LeftTicks Legend Linear Link Log LogFormat Margin Margins Mark MidArcArrow MidArrow NOT NewCenturySchoolBook NoBox NoMargin NoModifier NoTicks NoTicks3 NoZero NoZeroFormat None OR OmitFormat OmitTick OmitTickInterval OmitTickIntervals OutTicks Ox Oy Palatino PaletteTicks Pen PenMargin PenMargins Pentype Portrait RadialShade RadialShadeDraw Rainbow Range Relative Right RightTicks Rotate Round SQR Scale ScaleX ScaleY ScaleZ Seascape Segment Shift Sin Slant Spline StickIntervalMarker Straight Symbol Tan TeXify Ticks Ticks3 TildeIntervalMarker TimesRoman Top TrueMargin UnFill UpsideDown Wheel X XEquals XOR XY XYEquals XYZero XYgrid XZEquals XZZero XZero XZgrid Y YEquals YXgrid YZ YZEquals YZZero YZero YZgrid Yn Z ZX ZXgrid ZYgrid ZapfChancery ZapfDingbats _begingroup3 _cputime _draw _eval _image _labelpath _projection _strokepath _texpath aCos aSin aTan abort abs accel acos acosh acot acsc activatequote add addArrow addMargins addSaveFunction addpenarc addpenline adjust alias align all altitude angabscissa angle angpoint animate annotate anticomplementary antipedal apply approximate arc arcarrowsize arccircle arcdir arcfromcenter arcfromfocus arclength arcnodesnumber arcpoint arcsubtended arcsubtendedcenter arctime arctopath array arrow arrow2 arrowbase arrowbasepoints arrowsize ascii asec asin asinh ask assert asy asycode asydir asyfigure asyfilecode asyinclude asywrite atan atan2 atanh atbreakpoint atexit attach attract atupdate autoformat autoscale autoscale3 axes axes3 axialshade axis axiscoverage azimuth babel background bangles bar barmarksize barsize basealign baseline bbox beep begin beginclip begingroup beginpoint between bevel bezier bezierP bezierPP bezierPPP bezulate bibliography bibliographystyle binarytree binarytreeNode binomial bins bisector bisectorpoint bispline blend blockconnector box bqe breakpoint breakpoints brick buildRestoreDefaults buildRestoreThunk buildcycle bulletcolor byte calculateScaling canonical canonicalcartesiansystem cartesiansystem case1 case2 case3 case4 cbrt cd ceil center centerToFocus centroid cevian change2 changecoordsys checkSegment checkconditionlength checker checkincreasing checklengths checkposition checktriangle choose circle circlebarframe circlemarkradius circlenodesnumber circumcenter circumcircle clamped clear clip clipdraw close cmyk code colatitude collect collinear color colorless colors colorspace comma compassmark complement complementary concat concurrent cone conic conicnodesnumber conictype conj connect connected connectedindex containmentTree contains contour contour3 contouredges controlSpecifier convert coordinates coordsys copy copyPairOrTriple cos cosh cot countIntersections cputime crop cropcode cross crossframe crosshatch crossmarksize csc cubicroots curabscissa curlSpecifier curpoint currentarrow currentexitfunction currentmomarrow currentpolarconicroutine curve cut cutafter cutbefore cyclic cylinder deactivatequote debugger deconstruct defaultdir defaultformat defaultpen defined degenerate degrees delete deletepreamble determinant diagonal diamond diffdiv dir dirSpecifier dirtime display distance divisors do_overpaint dot dotframe dotsize downcase draw drawAll drawDoubleLine drawFermion drawGhost drawGluon drawMomArrow drawPRCcylinder drawPRCdisk drawPRCsphere drawPRCtube drawPhoton drawScalar drawVertex drawVertexBox drawVertexBoxO drawVertexBoxX drawVertexO drawVertexOX drawVertexTriangle drawVertexTriangleO drawVertexX drawarrow drawarrow2 drawline drawpixel drawtick duplicate elle ellipse ellipsenodesnumber embed embed3 empty enclose end endScript endclip endgroup endgroup3 endl endpoint endpoints eof eol equation equations erase erasestep erf erfc error errorbar errorbars eval excenter excircle exit exitXasyMode exitfunction exp expfactors expi expm1 exradius extend extension extouch fabs factorial fermat fft fhorner figure file filecode fill filldraw filloutside fillrule filltype find finite finiteDifferenceJacobian firstcut firstframe fit fit2 fixedscaling floor flush fmdefaults fmod focusToCenter font fontcommand fontsize foot format frac frequency fromCenter fromFocus fspline functionshade gamma generate_random_backtrace generateticks gergonne getc getint getpair getreal getstring gettriple gluon gouraudshade graph graphic gray grestore grid grid3 gsave halfbox hatch hdiffdiv hermite hex histogram history hline hprojection hsv hyperbola hyperbolanodesnumber hyperlink hypot identity image incenter incentral incircle increasing incrementposition indexedTransform indexedfigure initXasyMode initdefaults input inradius insert inside integrate interactive interior interp interpolate intersect intersection intersectionpoint intersectionpoints intersections intouch inverse inversion invisible is3D isCCW isDuplicate isnan isogonal isogonalconjugate isotomic isotomicconjugate isparabola italic item jobname key kurtosis kurtosisexcess label labelaxis labelmargin labelpath labels labeltick labelx labelx3 labely labely3 labelz labelz3 lastcut latex latitude latticeshade layer layout ldexp leastsquares legend legenditem length lexorder lift light limits line linear linecap lineinversion linejoin linemargin lineskip linetype linewidth link list lm_enorm lm_evaluate_default lm_lmdif lm_lmpar lm_minimize lm_print_default lm_print_quiet lm_qrfac lm_qrsolv locale locate locatefile location log log10 log1p logaxiscoverage longitude lookup makeNode makedraw makepen map margin markangle markangleradius markanglespace markarc marker markinterval marknodes markrightangle markthin markuniform mass masscenter massformat math max max3 maxAfterTransform maxbezier maxbound maxcoords maxlength maxratio maxtimes mean medial median midpoint min min3 minAfterTransform minbezier minbound minipage minratio mintimes miterlimit mktemp momArrowPath momarrowsize monotonic multifigure nativeformat natural needshipout newl newpage newslide newton newtree nextframe nextnormal nextpage nib nodabscissa none norm normalvideo notaknot nowarn numberpage nurb object offset onpath opacity opposite orientation origin orthic orthocentercenter outformat outline outname outprefix output overloadedMessage overwrite pack pad pairs palette parabola parabolanodesnumber parallel parallelogram partialsum path path3 pattern pause pdf pedal periodic perp perpendicular perpendicularmark phantom phi1 phi2 phi3 photon piecewisestraight point polar polarconicroutine polargraph polygon postcontrol postscript pow10 ppoint prc prc0 precision precontrol prepend printBytecode print_random_addresses project projection purge pwhermite quadrant quadraticroots quantize quarticroots quotient radialshade radians radicalcenter radicalline radius rand randompath rd readline realmult realquarticroots rectangle rectangular rectify reflect relabscissa relative relativedistance reldir relpoint reltime remainder remark removeDuplicates rename replace report resetdefaultpen restore restoredefaults reverse reversevideo rf rfind rgb rgba rgbint rms rotate rotateO rotation round roundbox roundedpath roundrectangle same samecoordsys sameside sample save savedefaults saveline scale scale3 scaleO scaleT scaleless scientific search searchindex searchtree sec secondaryX secondaryY seconds section sector seek seekeof segment sequence setcontour setpens sgn sgnd sharpangle sharpdegrees shift shiftless shipout shipout3 show side simeq simpson sin sinh size size3 skewness skip slant sleep slope slopefield solve solveBVP sort sourceline sphere split sqrt square srand standardizecoordsys startScript stdev step stickframe stickmarksize stickmarkspace stop straight straightness string stripdirectory stripextension stripfile stripsuffix strokepath subdivide subitem subpath substr sum surface symmedial symmedian system tab tableau tan tangent tangential tangents tanh tell tensionSpecifier tensorshade tex texcolor texify texpath texpreamble texreset texshipout texsize textpath thick thin tick tickMax tickMax3 tickMin tickMin3 ticklabelshift ticklocate tildeframe tildemarksize tile tiling time times title titlepage topbox transform transformation transpose trembleFuzz triangle triangleAbc triangleabc triangulate tricoef tridiagonal trilinear trim truepoint tube uncycle unfill uniform unique unit unitrand unitsize unityroot unstraighten upcase updatefunction uperiodic upscale uptodate usepackage usersetting usetypescript usleep value variance variancebiased vbox vector vectorfield verbatim view vline vperiodic vprojection warn warning windingnumber write xaxis xaxis3 xaxis3At xaxisAt xequals xlimits xpart xscale xscaleO xtick xtick3 xtrans yaxis yaxis3 yaxis3At yaxisAt yequals ylimits ypart yscale yscaleO ytick ytick3 ytrans zaxis3 zaxis3At zero zero3 zlimits zpart ztick ztick3 ztrans ))
+
+(defvar asy-variable-name '(
+AliceBlue Align Allow AntiqueWhite Apricot Aqua Aquamarine Aspect Azure BeginPoint Beige Bisque Bittersweet Black BlanchedAlmond Blue BlueGreen BlueViolet Both Break BrickRed Brown BurlyWood BurntOrange CCW CW CadetBlue CarnationPink Center Centered Cerulean Chartreuse Chocolate Coeff Coral CornflowerBlue Cornsilk Crimson Crop Cyan Dandelion DarkBlue DarkCyan DarkGoldenrod DarkGray DarkGreen DarkKhaki DarkMagenta DarkOliveGreen DarkOrange DarkOrchid DarkRed DarkSalmon DarkSeaGreen DarkSlateBlue DarkSlateGray DarkTurquoise DarkViolet DeepPink DeepSkyBlue DefaultHead DimGray DodgerBlue Dotted Down Draw E ENE EPS ESE E_Euler E_PC E_RK2 E_RK3BS Emerald EndPoint Euler Fill FillDraw FireBrick FloralWhite ForestGreen Fuchsia Gainsboro GhostWhite Gold Goldenrod Gray Green GreenYellow Honeydew HookHead Horizontal HotPink I IgnoreAspect IndianRed Indigo Ivory JOIN_IN JOIN_OUT JungleGreen Khaki LM_DWARF LM_MACHEP LM_SQRT_DWARF LM_SQRT_GIANT LM_USERTOL Label Lavender LavenderBlush LawnGreen Left LeftJustified LeftSide LemonChiffon LightBlue LightCoral LightCyan LightGoldenrodYellow LightGreen LightGrey LightPink LightSalmon LightSeaGreen LightSkyBlue LightSlateGray LightSteelBlue LightYellow Lime LimeGreen Linear Linen Log Logarithmic Magenta Mahogany Mark MarkFill MarkPath Maroon Max MediumAquamarine MediumBlue MediumOrchid MediumPurple MediumSeaGreen MediumSlateBlue MediumSpringGreen MediumTurquoise MediumVioletRed Melon MidPoint MidnightBlue Min MintCream MistyRose Moccasin Move MoveQuiet Mulberry N NE NNE NNW NW NavajoWhite Navy NavyBlue NoAlign NoCrop NoFill NoSide OldLace Olive OliveDrab OliveGreen Orange OrangeRed Orchid Ox Oy PC PaleGoldenrod PaleGreen PaleTurquoise PaleVioletRed PapayaWhip Peach PeachPuff Periwinkle Peru PineGreen Pink Plum PowderBlue ProcessBlue Purple RK2 RK3 RK3BS RK4 RK5 RK5DP RK5F RawSienna Red RedOrange RedViolet Rhodamine Right RightJustified RightSide RosyBrown RoyalBlue RoyalPurple RubineRed S SE SSE SSW SW SaddleBrown Salmon SandyBrown SeaGreen Seashell Sepia Sienna Silver SimpleHead SkyBlue SlateBlue SlateGray Snow SpringGreen SteelBlue Suppress SuppressQuiet Tan TeXHead Teal TealBlue Thistle Ticksize Tomato Turquoise UnFill Up VERSION Value Vertical Violet VioletRed W WNW WSW Wheat White WhiteSmoke WildStrawberry XYAlign YAlign Yellow YellowGreen YellowOrange addpenarc addpenline align allowstepping angularsystem animationdelay appendsuffix arcarrowangle arcarrowfactor arrow2sizelimit arrowangle arrowbarb arrowdir arrowfactor arrowhookfactor arrowlength arrowsizelimit arrowtexfactor authorpen axis axiscoverage axislabelfactor background backgroundcolor backgroundpen barfactor barmarksizefactor basealign baselinetemplate beveljoin bigvertexpen bigvertexsize black blue bm bottom bp brown bullet byfoci byvertices camerafactor chartreuse circlemarkradiusfactor circlenodesnumberfactor circleprecision circlescale cm codefile codepen codeskip colorPen coloredNodes coloredSegments conditionlength conicnodesfactor count cputimeformat crossmarksizefactor currentcoordsys currentlight currentpatterns currentpen currentpicture currentposition currentprojection curvilinearsystem cuttings cyan darkblue darkbrown darkcyan darkgray darkgreen darkgrey darkmagenta darkolive darkred dashdotted dashed datepen dateskip debuggerlines debugging deepblue deepcyan deepgray deepgreen deepgrey deepmagenta deepred default defaultControl defaultS defaultbackpen defaultcoordsys defaultexcursion defaultfilename defaultformat defaultmassformat defaultpen defaultseparator diagnostics differentlengths dot dotfactor dotframe dotted doublelinepen doublelinespacing down duplicateFuzz edge ellipsenodesnumberfactor eps epsgeo epsilon evenodd expansionfactor extendcap exterior fermionpen figureborder figuremattpen file3 firstnode firststep foregroundcolor fuchsia fuzz gapfactor ghostpen gluonamplitude gluonpen gluonratio gray green grey hatchepsilon havepagenumber heavyblue heavycyan heavygray heavygreen heavygrey heavymagenta heavyred hline hwratio hyperbola hyperbolanodesnumberfactor identity4 ignore inXasyMode inch inches includegraphicscommand inf infinity institutionpen intMax intMin interior invert invisible itempen itemskip itemstep labelmargin landscape lastnode left legendhskip legendlinelength legendmargin legendmarkersize legendmaxrelativewidth legendvskip lightblue lightcyan lightgray lightgreen lightgrey lightmagenta lightolive lightred lightyellow line linemargin lm_infmsg lm_shortmsg longdashdotted longdashed magenta magneticRadius mantissaBits markangleradius markangleradiusfactor markanglespace markanglespacefactor mediumblue mediumcyan mediumgray mediumgreen mediumgrey mediummagenta mediumred mediumyellow middle minDistDefault minblockheight minblockwidth mincirclediameter minipagemargin minipagewidth minvertexangle miterjoin mm momarrowfactor momarrowlength momarrowmargin momarrowoffset momarrowpen monoPen morepoints nCircle newbulletcolor ngraph nil nmesh nobasealign nodeMarginDefault nodesystem nomarker nopoint noprimary nullpath nullpen numarray ocgindex oldbulletcolor olive orange origin overpaint page pageheight pagemargin pagenumberalign pagenumberpen pagenumberposition pagewidth paleblue palecyan palegray palegreen palegrey palemagenta palered paleyellow parabolanodesnumberfactor perpfactor phi photonamplitude photonpen photonratio pi pink plain plain_bounds plain_scaling plus preamblenodes pt purple r3 r4a r4b randMax realDigits realEpsilon realMax realMin red relativesystem reverse right roundcap roundjoin royalblue salmon saveFunctions scalarpen sequencereal settings shipped signedtrailingzero solid springgreen sqrtEpsilon squarecap squarepen startposition stdin stdout stepfactor stepfraction steppagenumberpen stepping stickframe stickmarksizefactor stickmarkspacefactor swap textpen ticksize tildeframe tildemarksizefactor tinv titlealign titlepagepen titlepageposition titlepen titleskip top trailingzero treeLevelStep treeMinNodeWidth treeNodeStep trembleAngle trembleFrequency trembleRandom undefined unitcircle unitsquare up urlpen urlskip version vertexpen vertexsize viewportmargin viewportsize vline white wye xformStack yellow ylabelwidth zerotickfuzz zerowinding ))
diff --git a/Master/texmf-dist/asymptote/asy-mode.el b/Master/texmf-dist/asymptote/asy-mode.el
new file mode 100644
index 00000000000..7ccdfdf1197
--- /dev/null
+++ b/Master/texmf-dist/asymptote/asy-mode.el
@@ -0,0 +1,1590 @@
+;;; asy-mode.el
+
+;; Copyright (C) 2006-8
+;; Author: Philippe IVALDI 20 August 2006
+;; http://www.piprime.fr/
+;; Modified by: John Bowman
+;;
+;; This program is free software ; you can redistribute it and/or modify
+;; it under the terms of the GNU Lesser General Public License as published by
+;; the Free Software Foundation ; either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY ; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; Lesser General Public License for more details.
+;;
+;; You should have received a copy of the GNU Lesser General Public License
+;; along with this program ; if not, write to the Free Software
+;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+;; Emacs mode for editing Asymptote source code.
+
+;; INSTALLATION:
+;; Place this file (asy-mode.el) and asy-keywords.el in your Emacs load path.
+;; Then choose ONE of the following installation methods:
+
+;; * Method 1:
+;; Copy and uncomment the following lines to your .emacs initialization file:
+;;(autoload 'asy-mode "asy-mode.el" "Asymptote major mode." t)
+;;(autoload 'lasy-mode "asy-mode.el" "hybrid Asymptote/Latex major mode." t)
+;;(autoload 'asy-insinuate-latex "asy-mode.el" "Asymptote insinuate LaTeX." t)
+;;(add-to-list 'auto-mode-alist '("\\.asy$" . asy-mode))
+
+;; * Method 2:
+;; Copy and uncomment the following line to your .emacs initialization file:
+;;(require 'asy-mode)
+
+;; Notes:
+;;
+;; For full functionality the two-mode-mode package should also be installed
+;; (http://www.dedasys.com/freesoftware/files/two-mode-mode.el).
+;;
+;; See also paragraph II of the documentation below to automate asy-insinuate-latex.
+
+(defvar asy-mode-version "1.6")
+
+;;;###autoload
+(define-derived-mode asy-mode objc-mode "Asymptote"
+ "Emacs mode for editing Asymptote source code.
+For full functionality the `two-mode-mode' package should also be installed
+(http://www.dedasys.com/freesoftware/files/two-mode-mode.el).
+
+I. This package provides two modes:
+1- asy-mode:
+All the files with the extension '.asy' are edited in this mode, which provides the following features:
+* Syntax color highlighting;
+* Compiling and viewing current buffer with the key binding C-c C-c;
+* Moving cursor to the error by pressing the key F4.
+* Showing the available function prototypes for the command at the cursor with the key binding C-c ?
+* Compiling and viewing a TeX document linked with the current buffer (usually a document that includes the output picture).
+To link a Tex document try 'M-x asy-set-master-tex' follow by C-Return (see descriptions further of the key binding C-Return, C-S-Return, M-Return, M-S-Return etc within 2- lasy-mode)
+
+2- lasy-mode
+Editing a TeX file that contains Asymptote code is facilitated with the hybrid mode 'lasy-mode'.
+Toggle lasy-mode with M-x lasy-mode.
+In this hybrid mode the major mode is LaTeX when the cursor is in LaTeX code and becomes asy-mode when the cursor is between '\\begin{asy}' and '\\end{asy}'.
+All the features of asy-mode are provided and the key binding C-c C-c of asy-mode compiles and views only the code of the picture where the cursor is.
+Note that some keys binding are added to the LaTeX-mode-map in lasy-mode if the value of the variable lasy-extra-key is t (the default)
+.
+* C-return: compile (if the buffer/file is modified) and view the PostScript output with sequence [latex->[asy->latex]->dvips]->PSviewer
+* M-return: same with pdf output and with the sequence [pdflatex->[asy->pdflatex]]->PDFviewer
+* C-M-return: same with pdf output and with the sequence [latex->[asy->latex]->dvips->ps2pdf]->PSviewer
+* Add the Shift key to the sequence of keys to compile even if the file is not modified.
+
+II. To add a menu bar in current 'latex-mode' buffer and activate hot keys, use 'M-x asy-insinuate-latex <RET>'.
+You can automate this feature for all the 'latex-mode' buffers by inserting the five following lines in your .emacs initialization file:
+(eval-after-load \"latex\"
+ '(progn
+ ;; Add here your personal features for 'latex-mode':
+ (asy-insinuate-latex t) ;; Asymptote globally insinuates Latex.
+ ))
+
+You can access this help within Emacs by the key binding C-h f asy-mode <RET>
+
+BUGS:
+This package has been tested in:
+* Linux Debian Etch
+- GNU Emacs 22.0.50.1
+- GNU Emacs 21.4.1 (only basic errors management and basic font-lock features within lasy-mode are supported)
+* WindowsXP
+- GNU Emacs 22.0.990.1 (i386-mingw-nt5.1.2600)
+
+This package seems to work with XEmacs 21.4 but not all the features are available (in particular syntax highlighting).
+
+Report bugs to http://asymptote.sourceforge.net
+
+Some variables can be customized: M-x customize-group <RET> asymptote <RET>."
+
+ (setq c++-font-lock-extra-types (cons "true" c++-font-lock-extra-types)))
+
+(require 'font-lock)
+(require 'cc-mode)
+(require 'cl) ;; Common Lisp extensions for Emacs
+(require 'compile)
+(require 'wid-edit)
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.asy$" . asy-mode))
+
+(defvar running-xemacs-p (featurep 'xemacs))
+(defvar running-unix-p (not (string-match "windows-nt\\|ms-dos" (symbol-name system-type))))
+
+(when running-xemacs-p
+ (defalias 'turn-on-font-lock-if-enabled 'ignore)
+ (defalias 'line-number-at-pos 'line-number)
+ (defvar temporary-file-directory (temp-directory))
+ (defun replace-regexp-in-string (regexp rep string)
+ (replace-in-string string regexp rep))
+ )
+
+(when (or (< emacs-major-version 22) running-xemacs-p)
+ ;; Add regexp for parsing the compilation errors of asy
+ (add-to-list 'compilation-error-regexp-alist
+ '("\\(.*?.asy\\): \\(.*?\\)\\.\\(.*?\\):" 1 2 3)))
+
+(when (< emacs-major-version 22)
+ (defun line-number-at-pos (&optional pos)
+ "Return (narrowed) buffer line number at position POS.
+If POS is nil, use current buffer location.
+Counting starts at (point-min), so the value refers
+to the contents of the accessible portion of the buffer."
+ (let ((opoint (or pos (point))) start)
+ (save-excursion
+ (goto-char (point-min))
+ (setq start (point))
+ (goto-char opoint)
+ (forward-line 0)
+ (1+ (count-lines start (point)))))))
+
+(defcustom lasy-extra-key t
+ "* If on, the folowing binding keys are added in lasy-mode :
+ (define-key lasy-mode-map (kbd \"<C-return>\") 'lasy-view-ps)
+ (define-key lasy-mode-map (kbd \"<C-S-return>\") 'asy-master-tex-view-ps-f)
+ (define-key lasy-mode-map (kbd \"<M-return>\") 'lasy-view-pdf-via-pdflatex)
+ (define-key lasy-mode-map (kbd \"<M-S-return>\") 'asy-master-tex-view-pdflatex-f)
+ (define-key lasy-mode-map (kbd \"<C-M-return>\") 'lasy-view-pdf-via-ps2pdf)
+ (define-key lasy-mode-map (kbd \"<C-M-S-return>\") 'asy-master-tex-view-ps2pdf-f)
+
+If you also want this feature in pure latex-mode, you can set this variable to `nil' and add these lines in your .emacs:
+
+(require 'asy-mode)
+(eval-after-load \"latex\"
+ '(progn
+ (define-key LaTeX-mode-map (kbd \"<C-return>\") 'lasy-view-ps)
+ (define-key LaTeX-mode-map (kbd \"<C-S-return>\") 'asy-master-tex-view-ps-f)
+ (define-key LaTeX-mode-map (kbd \"<M-return>\") 'lasy-view-pdf-via-pdflatex)
+ (define-key LaTeX-mode-map (kbd \"<M-S-return>\") 'asy-master-tex-view-pdflatex-f)
+ (define-key LaTeX-mode-map (kbd \"<C-M-return>\") 'lasy-view-pdf-via-ps2pdf)
+ (define-key LaTeX-mode-map (kbd \"<C-M-S-return>\") 'asy-master-tex-view-ps2pdf-f)))"
+ :type 'boolean
+ :group 'asymptote)
+
+(defcustom asy-compilation-buffer 'none
+ " 'visible means keep compilation buffer visible ;
+ 'available means keep compilation buffer available in other buffer but not visible;
+ 'none means delete compilation buffer automatically after a *successful* compilation.
+ 'never means don't open any window or buffer attached to the compilation process.
+If the value is 'never':
+* Emacs is suspended until the child program returns;
+* the management of errors is poorer than with other value;
+* the compilation doesn't modify your current window configuration."
+ :type '(choice (const visible) (const available) (const none) (const never))
+ :group 'asymptote)
+
+(defcustom lasy-ask-about-temp-compilation-buffer t
+ "* If t, ask before visiting a temporary buffer of compilation."
+ :type 'boolean
+ :group 'asymptote)
+
+(defcustom lasy-compilation-inline-auto-detection nil
+ "* If t, lasy-mode detects automatically if the option 'inline' is passed to asymptote.sty.
+In case of 'inline' option, the compilation of a figure separately of the document is processed by rebuilding the preamble and compiling it as a file '.tex' containing only this picture.
+ If nil (the default), the compilation of a figure separately of the document is processed by building a file '.asy', without the features of the LaTeX preamble."
+ :type 'boolean
+ :group 'asymptote)
+
+(defcustom asy-command-location ""
+ "* If not in the path, you can put here the name of the directory containing Asy's binary files.
+this variable must end in /."
+ :type 'directory
+ :group 'asymptote)
+
+(defcustom asy-command "asy -V"
+ "* Command invoked to compile a Asymptote file.
+You can define the location of this command with the variable `asy-command-location'."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom lasy-command "asy"
+ "* Command invoked to compile a Asymptote file generated compiling a .tex file.
+You can define the location of this command with the variable `asy-command-location'."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom lasy-latex-command "latex -halt-on-error"
+ "* Command invoked to compile a .tex file with LaTeX."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom lasy-pdflatex-command "pdflatex -halt-on-error"
+ "* Command invoked to compile a .tex file with pdflaTex."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom lasy-dvips-pre-pdf-command "dvips -Ppdf"
+ "* Command invoked to convert a .dvi file to a temporary .ps file in order to
+generate a final .pdf file."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom lasy-dvips-command "dvips -q"
+ "* Command invoked to convert a .dvi file to a final .ps file."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom lasy-ps2pdf-command "ps2pdf14"
+ "* Command invoked to convert a .dvi file to .ps file."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom asy-temp-dir temporary-file-directory
+ "*The name of a directory for Asy's temporary files.
+Such files are generated by functions like
+`asy-compile' when lasy-mode is enable."
+ :type 'directory
+ :group 'asymptote)
+
+(defcustom ps-view-command (if running-unix-p "gv" "")
+ "Command to view a PostScript file generated by compiling a tex file within lasy-mode.
+This variable is not used when running the Windows OS.
+See `asy-open-file'."
+ :type 'string
+ :group 'asymptote)
+
+(defcustom pdf-view-command
+ (if running-unix-p
+ "xpdf" "")
+ "Command to view a pdf file generated by compiling a tex file within lasy-mode.
+This variable is not used when running the Windows OS.
+See `asy-open-file'."
+ :type 'string
+ :group 'asymptote)
+
+(defvar asy-TeX-master-file nil
+ "TeX file associate with current asymptote code.
+This variable must be modified only using the function 'asy-set-master-tex by M-x asy-set-master-tex <RET>.")
+(make-variable-buffer-local 'asy-TeX-master-file)
+
+(defvar lasy-compile-tex nil
+ "* Internal use. t if LaTeX compilation come from latex-mode.")
+
+(when (fboundp 'font-lock-add-keywords)
+ (if (< max-specpdl-size 2000) (setq max-specpdl-size 2000))
+ (defun asy-add-function-keywords (function-keywords face-name)
+ (let* ((keyword-list (mapcar #'(lambda (x)
+ (symbol-name x))
+ function-keywords))
+ (keyword-regexp (concat "\\<\\("
+ (regexp-opt keyword-list)
+ "\\)(")))
+ (font-lock-add-keywords 'asy-mode
+ `((,keyword-regexp 1 ',face-name)))))
+
+ (defun asy-add-variable-keywords (function-keywords face-name)
+ (let* ((keyword-list (mapcar #'(lambda (x)
+ (symbol-name x))
+ function-keywords))
+ (keyword-regexp (concat "\\<[0-9]*\\("
+ (regexp-opt keyword-list)
+ "\\)\\(?:[^(a-zA-Z]\\|\\'\\)")))
+ (font-lock-add-keywords 'asy-mode
+ `((,keyword-regexp 1 ',face-name)))))
+
+ ;; External definitions of keywords:
+ ;; asy-function-name and asy-variable-name
+ (if (locate-library "asy-keywords.el")
+ (load "asy-keywords.el")
+ (progn
+ ;; Use dummy keyword definitions if asy-keywords.el is not found:
+ (defvar asy-keyword-name nil)
+ (defvar asy-type-name nil)
+ (defvar asy-function-name nil)
+ (defvar asy-variable-name nil)))
+
+ (defcustom asy-extra-type-name '()
+ "Extra user type names highlighted with 'font-lock-type-face"
+ :type '(repeat symbol)
+ :group 'asymptote)
+
+ (defcustom asy-extra-function-name
+ '()
+ "Extra user function names highlighted with 'font-lock-function-name-face"
+ :type '(repeat symbol)
+ :group 'asymptote)
+
+ (defcustom asy-extra-variable-name '()
+ "Extra user variable names highlighted with 'font-lock-constant-face"
+ :type '(repeat symbol)
+ :group 'asymptote)
+
+ (asy-add-variable-keywords
+ asy-keyword-name
+ 'font-lock-builtin-face)
+
+ (asy-add-variable-keywords
+ (nconc asy-type-name asy-extra-type-name)
+ 'font-lock-type-face)
+
+ (asy-add-function-keywords
+ (nconc asy-function-name asy-extra-function-name)
+ 'font-lock-function-name-face)
+
+ (asy-add-variable-keywords
+ (nconc asy-variable-name asy-extra-variable-name)
+ 'font-lock-constant-face)
+
+ (defface asy-environment-face
+ `((t
+ (:underline t :inverse-video t)))
+ "Face used to highlighting the keywords '\\begin{asy}' and '\\end{asy}' within lasy-mode."
+ :group 'asymptote)
+
+ (font-lock-add-keywords
+ 'asy-mode
+ '(("\\\\begin{asy}.*" . 'asy-environment-face)
+ ("\\\\end{asy}" . 'asy-environment-face)))
+
+ (defface asy-link-face ;; widget-field-face
+ `((t
+ (:underline t)))
+ "Face used to highlighting the links."
+ :group 'asymptote)
+
+ (font-lock-add-keywords
+ 'asy-mode
+ '(("\\[.*?\\.asy\\]" . 'asy-link-face)))
+ )
+
+(setq buffers-menu-max-size nil)
+(setq mode-name "Asymptote")
+
+(if running-xemacs-p
+ (defvar asy-menu
+ '("Asy"
+ ["Toggle lasy-mode" lasy-mode :active (and (featurep 'two-mode-mode) two-mode-bool)]
+ ["Compile/View" asy-compile t]
+ ["Go to error" asy-goto-error t]
+ ["Describe command" asy-show-function-at-point t]"--"
+ ("Master TeX file"
+ ["Set/Change value" (asy-set-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool))]
+ ["Erase value" (asy-unset-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool))]
+ ("Compile OR View"
+ ["PS" asy-master-tex-view-ps :active t]
+ ["PDF (pdflatex)" asy-master-tex-view-pdflatex :active t]
+ ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf :active t])
+ ("Compile AND View"
+ ["PS" asy-master-tex-view-ps-f :active t]
+ ["PDF (pdflatex)" asy-master-tex-view-pdflatex-f :active t]
+ ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf-f :active t]))
+ ["Asymptote insinuates globally LaTeX" asy-insinuate-latex-globally :active (not asy-insinuate-latex-globally-p)]"--"
+ ("Debugger Buffer"
+ ["Visible" (setq asy-compilation-buffer 'visible) :style radio :selected (eq asy-compilation-buffer 'visible) :active t]
+ ["Available" (setq asy-compilation-buffer 'available) :style radio :selected (eq asy-compilation-buffer 'available) :active t]
+ ["None" (setq asy-compilation-buffer 'none) :style radio :selected (eq asy-compilation-buffer 'none) :active t]
+ ["Never" (setq asy-compilation-buffer 'never) :style radio :selected (eq asy-compilation-buffer 'never) :active t])
+ ("Compilation Options" :included (and (featurep 'two-mode-mode) two-mode-bool)
+ ["Enable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection t) :style radio :selected lasy-compilation-inline-auto-detection :active t]
+ ["Disable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection nil) :style radio :selected (not lasy-compilation-inline-auto-detection) :active t])
+ ["Customize" (customize-group "asymptote") :active t]
+ ["Help" (describe-function 'asy-mode) :active t]
+ ))
+ (defvar asy-menu
+ '("Asy"
+ ["Toggle Lasy-Mode" lasy-mode :visible (and (featurep 'two-mode-mode) two-mode-bool)]
+ ["Compile/View" asy-compile t]
+ ["Go to Error" asy-goto-error t]
+ ["Describe Command" asy-show-function-at-point t]"--"
+ ("Master TeX File"
+ ["Set/Change Value" (asy-set-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool)) :key-sequence nil]
+ ["Erase Value" (asy-unset-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool)) :key-sequence nil]
+ ("Compile or View"
+ ["PS" asy-master-tex-view-ps :active t]
+ ["PDF (pdflatex)" asy-master-tex-view-pdflatex :active t]
+ ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf :active t])
+ ("Compile and View"
+ ["PS" asy-master-tex-view-ps-f :active t]
+ ["PDF (pdflatex)" asy-master-tex-view-pdflatex-f :active t]
+ ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf-f :active t]))
+ ["Asymptote Insinuates Globally LaTeX" asy-insinuate-latex-globally :active (not asy-insinuate-latex-globally-p)]"--"
+ ("Debugger Buffer"
+ ["Visible" (setq asy-compilation-buffer 'visible) :style radio :selected (eq asy-compilation-buffer 'visible) :active t :key-sequence nil]
+ ["Available" (setq asy-compilation-buffer 'available) :style radio :selected (eq asy-compilation-buffer 'available) :active t :key-sequence nil]
+ ["None" (setq asy-compilation-buffer 'none) :style radio :selected (eq asy-compilation-buffer 'none) :active t :key-sequence nil]
+ ["Never" (setq asy-compilation-buffer 'never) :style radio :selected (eq asy-compilation-buffer 'never) :active t :key-sequence nil])
+ ("Compilation Options" :visible (and (featurep 'two-mode-mode) two-mode-bool)
+ ["Enable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection t) :style radio :selected lasy-compilation-inline-auto-detection :active t :key-sequence nil]
+ ["Disable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection nil) :style radio :selected (not lasy-compilation-inline-auto-detection) :active t :key-sequence nil])
+ ["Customize" (customize-group "asymptote") :active t :key-sequence nil]
+ ["Help" (describe-function 'asy-mode) :active t :key-sequence nil]
+ )))
+(easy-menu-define asy-mode-menu asy-mode-map "Asymptote Mode Commands" asy-menu)
+;; On the hook for XEmacs only.
+(if running-xemacs-p
+ (add-hook 'asy-mode-hook
+ (lambda ()
+ (and (eq major-mode 'asy-mode)
+ (easy-menu-add asy-mode-menu asy-mode-map)))))
+
+(defun asy-protect-file-name(Filename)
+ (concat "\"" Filename "\""))
+
+(defun asy-get-temp-file-name(&optional noext)
+ "Get a temp file name for printing."
+ (if running-xemacs-p
+ (concat (make-temp-name asy-temp-dir) (if noext "" ".asy"))
+ (concat (make-temp-file
+ (expand-file-name "asy" asy-temp-dir)) (if noext "" ".asy"))))
+
+(defun asy-log-filename()
+ (concat buffer-file-name ".log"))
+
+(defun asy-compile()
+ "Compile Asymptote code."
+ (interactive)
+ (if (and (boundp two-mode-bool) two-mode-bool)
+ (lasy-compile) ;; compile asy code in a TeX file.
+ (progn ;; compile asy code in a asy file.
+ (let*
+ ((buffer-base-name (file-name-sans-extension (file-name-nondirectory buffer-file-name)))
+ (asy-compile-command
+ (concat asy-command-location asy-command
+ (if (eq asy-compilation-buffer 'never)
+ " " " -wait ")
+ (asy-protect-file-name buffer-base-name))))
+ (if (buffer-modified-p) (save-buffer))
+ (message "%s" asy-compile-command)
+ (asy-internal-compile asy-compile-command t t)))))
+
+(defun asy-error-message(&optional P)
+ (let ((asy-last-error
+ (asy-log-field-string
+ (asy-log-filename) 0)))
+ (if (and asy-last-error (not (string= asy-last-error "")))
+ (message (concat asy-last-error (if P "\nPress F4 to go to error" "")))
+ (when (and (boundp two-mode-bool) two-mode-bool lasy-run-tex (not (zerop asy-last-compilation-code)))
+ (message "The LaTeX code may be incorrect.")))))
+
+(defun asy-log-field-string(Filename Field)
+ "Return field of first line of file filename.
+Fields are defined as 'field1: field2.field3:field4' . Field=0 <-> all fields"
+ (let ((view-inhibit-help-message t))
+ (with-temp-buffer
+ (progn
+ (insert-file Filename)
+ (beginning-of-buffer)
+ (if (re-search-forward "^\\(.*?\\): \\(.*?\\)\\.\\(.*?\\):\\(.*\\)$" (point-max) t)
+ (match-string Field) nil)))))
+
+(defun asy-next-error(arg reset)
+ (if (> emacs-major-version 21)
+ (next-error arg reset)
+ (next-error arg)))
+
+(defun lasy-ask-visit-tem-compilation-buffer()
+ "* Ask before visiting a temporary compilation buffer depending the value of `lasy-ask-about-temp-compilation-buffer'."
+ (if lasy-ask-about-temp-compilation-buffer
+ (y-or-n-p "Visit temporary buffer of compilation ? ") t))
+
+(defun lasy-place-cursor-to-error(Filename li co)
+ (save-excursion
+ (with-temp-buffer
+ (insert-file-contents
+ (if running-unix-p Filename
+ (replace-regexp-in-string
+ "//" ":/"
+ (replace-regexp-in-string "/cygdrive/" "" Filename)))) ;; Not right,
+;;;maybe take a look at the code of compilation-find-file
+ (beginning-of-buffer)
+ (next-line (1- (string-to-number li)))
+ (setq line-err
+ (buffer-substring-no-properties
+ (progn (beginning-of-line) (point))
+ (progn (end-of-line) (point))))))
+ (beginning-of-buffer)
+ (search-forward line-err)
+ (beginning-of-line)
+ (forward-char (1- (string-to-number co))))
+
+(defun asy-goto-error(&optional arg reset)
+ "Go to point of last error within asy/lasy-mode."
+ (interactive "P")
+ (if (or (eq asy-compilation-buffer 'never)
+ (and (boundp two-mode-bool) two-mode-bool))
+ (let* ((log-file (asy-log-filename))
+ (li_ (asy-log-field-string log-file 2))
+ (co_ (asy-log-field-string log-file 3)))
+ (if (and (boundp two-mode-bool) two-mode-bool) ;; Within Lasy-mode
+ (progn ;; lasy-mode need the compilation of file.tex
+ ;; the error can be in Tex commands or in Asymptote commands
+ (if (eq asy-compilation-buffer 'never) ;; Find error in the log file.
+ (if li_ ;; Asy error found in the log-file
+ (progn
+ (lasy-place-cursor-to-error
+ (asy-log-field-string log-file 1) li_ co_)
+ (asy-error-message))
+ (message "There is an error in your LaTeX code..."))
+ (if (or running-xemacs-p (< emacs-major-version 22))
+ (when (lasy-ask-visit-tem-compilation-buffer)
+ (next-error arg))
+ (let ((msg)) ;; Find error in the compilation buffer
+ (save-excursion
+ (set-buffer (next-error-find-buffer))
+ (when reset
+ (setq compilation-current-error nil))
+ (let* ((columns compilation-error-screen-columns)
+ (last 1)
+ (loc (compilation-next-error (or arg 1) nil
+ (or compilation-current-error
+ compilation-messages-start
+ (point-min))))
+ (end-loc (nth 2 loc))
+ (marker (point-marker)))
+ (setq compilation-current-error (point-marker)
+ overlay-arrow-position
+ (if (bolp)
+ compilation-current-error
+ (copy-marker (line-beginning-position)))
+ loc (car loc)))
+ (if (re-search-forward "^\\(.*?\\): \\(.*?\\)\\.\\(.*?\\):\\(.*\\)$" (point-max) t)
+ (progn
+ (setq msg (match-string 0)
+ log-file (match-string 1)
+ li_ (match-string 2)
+ co_ (match-string 3)))
+ (error "Not other errors.")))
+ (lasy-place-cursor-to-error log-file li_ co_)
+ (message msg)))))
+ (if li_ ;;Pure asy-mode and compilation with shell-command
+ (progn
+ (goto-line (string-to-number li_))
+ (forward-char (1- (string-to-number co_)))
+ (asy-error-message))
+ (progn (message "No error.")))))
+ (asy-next-error arg reset)))
+
+(defun asy-grep (Regexp)
+ "Internal function used by asymptote."
+ (let ((Strout "")
+ (case-fold-search-asy case-fold-search))
+ (progn
+ (beginning-of-buffer)
+ (setq case-fold-search nil)
+ (while (re-search-forward Regexp (point-max) t)
+ (setq Strout (concat Strout (match-string 0) "\n\n")))
+ (setq case-fold-search case-fold-search-asy)
+ (if (string= Strout "") "No match.\n" Strout))))
+
+(defun asy-widget-open-file-at-pos (widget &optional event)
+ ""
+ (kill-buffer (current-buffer))
+ (find-file (widget-get widget :follow-link))
+ (goto-line (string-to-number (widget-get widget :value))))
+
+(defun asy-show-function-at-point()
+ "Show the Asymptote definitions of the command at point."
+ (interactive)
+ (save-excursion
+ (let ((cWord (current-word))
+ (cWindow (selected-window)))
+ (switch-to-buffer-other-window "*asy-help*")
+ (fundamental-mode)
+ (setq default-directory "/")
+ (if (> emacs-major-version 21)
+ (call-process-shell-command
+ (concat asy-command-location "asy -l --where") nil t nil)
+ (insert (shell-command-to-string "asy -l --where")))
+ (let ((rHelp (asy-grep (concat "^.*\\b" cWord "(\\(.\\)*?$")))
+ (tag)(file)(line))
+ (erase-buffer)
+ (insert rHelp)
+ (beginning-of-buffer)
+ (while (re-search-forward "\\(.*\\): \\([0-9]*\\)\\.\\([0-9]*\\)" (point-max) t)
+ (setq file (match-string 1)
+ line (match-string 2)
+ tag (file-name-nondirectory file))
+ (widget-create `(file-link
+ :tag ,tag
+ :follow-link ,file
+ :value ,line
+ :action asy-widget-open-file-at-pos
+ ))))
+ (beginning-of-buffer)
+ (while (re-search-forward "\\(.*: [0-9]*\\.[0-9]*\\)" (point-max) t)
+ (replace-match ""))
+ (asy-mode)
+ (use-local-map widget-keymap)
+ (widget-setup)
+ (goto-char (point-min))
+ (select-window cWindow))))
+
+(add-hook 'asy-mode-hook
+ (lambda ()
+ (c-set-style "gnu");
+ (c-set-offset (quote topmost-intro-cont) 0 nil)
+ (make-local-variable 'c-label-minimum-indentation)
+ (setq c-label-minimum-indentation 0)
+ (when (fboundp 'flyspell-mode) (flyspell-mode -1))
+ (turn-on-font-lock)
+ (column-number-mode t)
+ ))
+
+
+;;;###autoload (defun lasy-mode ())
+;;; ************************************
+;;; asy-mode mixed with LaTeX-mode: lasy
+;;; ************************************
+(if (locate-library "two-mode-mode")
+ (progn
+
+ (defvar lasy-fontify-asy-p nil
+ "Variable to communicate with `font-lock-unfontify-region'.
+Internal use, don't set in any fashion.")
+ (setq lasy-fontify-asy-p nil)
+
+ (eval-after-load "two-mode-mode"
+ '(progn
+ ;; Redefine `two-mode-mode-update-mode' to use regexp.
+ (defun two-mode-mode-update-mode ()
+ "Redefined in `asy-mode.el' to use regexp"
+ (when (and two-mode-bool two-mode-update)
+ (setq two-mode-update 0)
+ (let ((mode-list second-modes)
+ (flag 0))
+ (while mode-list
+ (let ((mode (car mode-list))
+ (lm -1)
+ (rm -1))
+ (save-excursion
+ (if (search-backward-regexp (cadr mode) nil t)
+ (setq lm (point))
+ (setq lm -1)))
+ (save-excursion
+ (if (search-backward-regexp (car (cddr mode)) nil t)
+ (setq rm (point))
+ (setq rm -1)))
+ (if (and (not (and (= lm -1) (= rm -1))) (>= lm rm))
+ (progn
+ (setq flag 1)
+ (setq mode-list '())
+ (two-mode-change-mode (car mode) (car (cdr (cddr mode)))))))
+ (setq mode-list (cdr mode-list)))
+ (if (= flag 0)
+ (two-mode-change-mode (car default-mode) (cadr default-mode))))))
+
+ (defun two-mode-change-mode (to-mode func)
+ "Redefined in asy-mode.
+Change the variable `lasy-fontify-asy-p' according to the value of func and
+the current mode."
+ (if (string= to-mode mode-name)
+ t
+ (progn
+ (setq lasy-fontify-asy-p (eq func 'asy-mode))
+ (funcall func)
+ (hack-local-variables)
+ (two-mode-mode-setup)
+ (if two-mode-switch-hook
+ (run-hooks 'two-mode-switch-hook))
+ (if (eq font-lock-mode t)
+ (font-lock-fontify-buffer))
+ (turn-on-font-lock-if-enabled))))
+ ))
+
+
+ (require 'two-mode-mode)
+
+ (defun lasy-mode ()
+ "Treat, in some cases, the current buffer as a literal Asymptote program."
+ (interactive)
+ (save-excursion
+ (let ((prefix
+ (progn
+ (goto-char (point-max))
+ (re-search-backward "^\\([^\n]+\\)Local Variables:"
+ (- (point-max) 3000) t)
+ (match-string 1)))
+ (pos-b (point)))
+ (when
+ (and prefix
+ (progn
+ (re-search-forward (regexp-quote
+ (concat prefix
+ "End:")) (point-max) t)
+ (re-search-backward (concat "\\(" prefix "mode: .*\\)") pos-b t))
+ )
+ (error (concat "lasy-mode can not work if a mode is specified as local file variable.
+You should remove the line " (int-to-string (line-number-at-pos)))))))
+ (set (make-local-variable 'asy-insinuate-latex-p) asy-insinuate-latex-p)
+ (make-local-variable 'lasy-fontify-asy-p)
+ (when (< emacs-major-version 22)
+ (make-local-variable 'font-lock-keywords-only))
+ (setq default-mode '("LaTeX" latex-mode)
+ second-modes '(("Asymptote"
+ "^\\\\begin{asy}.*$"
+ "^\\\\end{asy}"
+ asy-mode)))
+ (if two-mode-bool
+ (progn
+ (latex-mode)
+ (asy-insinuate-latex))
+ (progn
+ (two-mode-mode)
+ )))
+
+ (when (not running-xemacs-p)
+ (defadvice TeX-command-master (around asy-choose-compile act)
+ "Hack to circumvent the preempt of 'C-c C-c' by AucTeX within `lasy-mode'."
+ (if (string-match "asymptote" (downcase mode-name))
+ (asy-compile)
+ ad-do-it)))
+
+ (add-hook 'two-mode-switch-hook
+ (lambda ()
+ (if (eq major-mode 'latex-mode)
+ (progn ;; Switch to latex-mode
+ ;; Disable LaTeX-math-Mode within lasy-mode (because of incompatibility)
+ (when LaTeX-math-mode (LaTeX-math-mode -1))
+ (asy-insinuate-latex)
+ (when (< emacs-major-version 22)
+ (setq font-lock-keywords-only nil)))
+ (progn ;; Switch to asy-mode
+ (when (< emacs-major-version 22)
+ (setq font-lock-keywords-only t))
+ ))))
+ ;; (setq two-mode-switch-hook nil)
+
+ ;; Solve a problem restoring a TeX file via desktop.el previously in lasy-mode.
+ (if (boundp 'desktop-buffer-mode-handlers)
+ (progn
+ (defun asy-restore-desktop-buffer (desktop-b-f-name d-b-n d-b-m)
+ (find-file desktop-b-f-name))
+ (add-to-list 'desktop-buffer-mode-handlers
+ '(asy-mode . asy-restore-desktop-buffer))))
+
+ ;; Functions and 'advises' to restrict 'font-lock-unfontify-region'
+ ;; and 'font-lock-fontify-syntactically-region' within lasy-mode
+ ;; Special thanks to Olivier Ramaré for his help.
+ (when (and (fboundp 'font-lock-add-keywords) (> emacs-major-version 21))
+ (defun lasy-mode-at-pos (pos &optional interior strictly)
+ "If point at POS is in an asy environment return the list (start end)."
+ (save-excursion
+ (save-match-data
+ (goto-char pos)
+ (let* ((basy
+ (progn
+ (unless strictly (end-of-line))
+ (when (re-search-backward "^\\\\begin{asy}" (point-min) t)
+ (when interior (next-line))
+ (point))))
+ (easy
+ (and basy
+ (progn
+ (when (re-search-forward "^\\\\end{asy}" (point-max) t)
+ (when interior (previous-line)(beginning-of-line))
+ (point))))))
+ (and basy easy
+ (> pos (- basy (if interior 12 0)))
+ (< pos (+ easy (if interior 10 0)))
+ (list basy easy))))))
+
+ (defun lasy-region (start end &optional interior)
+ "If the region 'start to end' contains the beginning or
+the end of an asy environment return the list of points where
+the asy environment starts and ends."
+ (let* ((beg (min start end))
+ (lim (max start end)))
+ (or (lasy-mode-at-pos beg interior)
+ (save-match-data
+ (save-excursion
+ (goto-char beg)
+ (and (re-search-forward "^\\\\begin{asy}" lim t)
+ (lasy-mode-at-pos (point) interior)))))))
+
+ (defun lasy-tags (start end)
+ "Return associated list of points where the tags starts and ends
+restricted to the region (start end).
+\"b\" associated with (start-beginTag end-beginTag),
+\"e\" associated with (start-endTag end-endTag)."
+ (let*
+ ((beg (min start end))
+ (lim (max start end))
+ out)
+ (save-excursion
+ (goto-char beg)(beginning-of-line)
+ (while
+ (when (re-search-forward "^\\\\begin{asy}.*" lim t)
+ (push (list
+ (progn (beginning-of-line)(point))
+ (progn (end-of-line)(point))) out)))
+ (goto-char beg)(beginning-of-line)
+ (while
+ (when (re-search-forward "^\\\\end{asy}" lim t)
+ (push (list
+ (progn (beginning-of-line)(point))
+ (progn (end-of-line)(point))) out)))
+ out)))
+
+ (defun lasy-restrict-region (start end &optional interior)
+ "If the region 'start to end' contains the beginning or
+the end of an asy environment, returns the list of points wich
+restricts the region to the asy environment.
+Else, return (start end)."
+ (let*
+ ((beg (min start end))
+ (lim (max start end))
+ (be (if (lasy-mode-at-pos beg)
+ beg
+ (or (save-excursion
+ (goto-char beg)
+ (when (re-search-forward "^\\\\begin{asy}.*" lim t)
+ (unless interior (beginning-of-line))
+ (point)))
+ beg)))
+ (en (or (save-excursion
+ (goto-char be)
+ (when (re-search-forward "^\\\\end{asy}" lim t)
+ (when interior (beginning-of-line))
+ (point)))
+ lim)))
+ (list be en)))
+
+ (defun lasy-parse-region (start end)
+ "Return a list ((a (start1 end1)) (b (start2 end2)) [...]).
+where a, b, ... are nil or t; t means the region from 'startX' through 'endX' (are points)
+is in a asy environnement."
+ (let (regasy out rr brr err tags)
+ (save-excursion
+ (goto-char start)
+ (while (< (point) end)
+ (setq regasy (lasy-region (point) end))
+ (if regasy
+ (progn
+ (setq rr (lasy-mode-at-pos (point)))
+ (setq brr (and rr (nth 0 rr))
+ err (and rr (nth 1 rr)))
+ (if rr
+ (progn
+ (push (list t (list (max 1 (1- (point))) (min end err))) out)
+ (goto-char (min end err)))
+ (progn
+ (push (list nil (list (point) (nth 0 regasy))) out)
+ (goto-char (1+ (nth 0 regasy))))))
+ (progn
+ (push (list nil (list (min (1+ (point)) end) end)) out)
+ (goto-char end)))
+ ))
+ ;; Put start and end of tag in latex fontification.
+ (setq tags (lasy-tags start end))
+ (dolist (tag tags) (push (list nil tag) out))
+ (reverse out)))
+
+ (defadvice font-lock-unfontify-region
+ (around asy-font-lock-unfontify-region (beg end))
+ (if two-mode-bool
+ (let ((rstate (lasy-parse-region beg end))
+ curr reg asy-fontify latex-fontify)
+ (while (setq curr (pop rstate))
+ (setq reg (nth 1 curr))
+ (setq asy-fontify (and (nth 0 curr) lasy-fontify-asy-p)
+ latex-fontify (and (not (nth 0 curr))
+ (not lasy-fontify-asy-p)))
+ (when (or asy-fontify latex-fontify)
+ (setq beg (nth 0 reg)
+ end (nth 1 reg))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region beg end)
+ ad-do-it
+ (widen))))))
+ ad-do-it))
+
+ (ad-activate 'font-lock-unfontify-region)
+ ;; (ad-deactivate 'font-lock-unfontify-region)
+
+ (defadvice font-lock-fontify-syntactically-region
+ (around asy-font-lock-fontify-syntactically-region
+ (start end &optional loudly))
+ (if (and two-mode-bool (eq major-mode 'asy-mode))
+ (let*((reg (lasy-restrict-region start end)))
+ (save-restriction
+ (setq start (nth 0 reg) end (nth 1 reg))
+ (narrow-to-region start end)
+ (condition-case nil
+ ad-do-it
+ (error nil))
+ (widen)
+ ))
+ ad-do-it))
+
+ (ad-activate 'font-lock-fontify-syntactically-region)
+ ;; (ad-deactivate 'font-lock-fontify-syntactically-region)
+
+ (defadvice font-lock-default-fontify-region
+ (around asy-font-lock-default-fontify-region
+ (beg end loudly))
+ (if two-mode-bool
+ (let ((rstate (lasy-parse-region beg end))
+ asy-fontify latex-fontify curr reg)
+ (while (setq curr (pop rstate))
+ (setq reg (nth 1 curr))
+ (setq asy-fontify (and (nth 0 curr) lasy-fontify-asy-p)
+ latex-fontify (and (not (nth 0 curr))
+ (not lasy-fontify-asy-p)))
+ (when (or asy-fontify latex-fontify)
+ (setq beg (nth 0 reg)
+ end (nth 1 reg))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region beg end)
+ (condition-case nil
+ ad-do-it
+ (error nil))
+ (widen)
+ )))))
+ ad-do-it))
+
+ (ad-activate 'font-lock-default-fontify-region)
+ ;; (ad-deactivate 'font-lock-default-fontify-region)
+
+ ))
+ (progn
+ (defvar two-mode-bool nil)
+ (defun lasy-mode ()
+ (message "You must install the package two-mode-mode.el."))))
+
+(setq asy-latex-menu-item
+ '(["Toggle lasy-mode" lasy-mode :active (featurep 'two-mode-mode)]
+ ["View asy picture near cursor" lasy-compile :active t]"--"
+ ("Compile OR View"
+ ["PS" lasy-view-ps :active t]
+ ["PDF (pdflatex)" lasy-view-pdf-via-pdflatex :active t]
+ ["PDF (ps2pdf)" lasy-view-pdf-via-ps2pdf :active t])
+ ("Compile AND View"
+ ["PS" asy-master-tex-view-ps-f :active t]
+ ["PDF (pdflatex)" asy-master-tex-view-pdflatex-f :active t]
+ ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf-f :active t])"--"
+ ["Asymptote insinuates globally LaTeX" asy-insinuate-latex-globally :active (not asy-insinuate-latex-globally-p)]
+ ("Disable Asymptote insinuate Latex"
+ ["locally" asy-no-insinuate-locally :active t]
+ ["globally" asy-no-insinuate-globally :active t])
+ ("Debugger Buffer"
+ ["Visible" (setq asy-compilation-buffer 'visible) :style radio :selected (eq asy-compilation-buffer 'visible) :active t]
+ ["Available" (setq asy-compilation-buffer 'available) :style radio :selected (eq asy-compilation-buffer 'available) :active t]
+ ["None" (setq asy-compilation-buffer 'none) :style radio :selected (eq asy-compilation-buffer 'none) :active t]
+ ["Never" (setq asy-compilation-buffer 'never) :style radio :selected (eq asy-compilation-buffer 'never) :active t])
+ ))
+(if running-xemacs-p
+ (setq asy-latex-menu-item (nconc '("Asymptote") asy-latex-menu-item))
+ (setq asy-latex-menu-item (nconc '("Asymptote" :visible asy-insinuate-latex-p) asy-latex-menu-item)))
+
+(defun asy-insinuate-latex-maybe ()
+ "This function is added to `LaTeX-mode-hook' to define the environment 'asy'
+and, eventually, set its indentation.
+For internal use only."
+ (when (or asy-insinuate-latex-globally-p
+ (save-excursion
+ (beginning-of-buffer)
+ (save-match-data
+ (search-forward "\\begin{asy}" nil t))))
+ (asy-insinuate-latex))
+ (LaTeX-add-environments
+ '("asy" (lambda (env &rest ignore)
+ (unless asy-insinuate-latex-p (asy-insinuate-latex))
+ (LaTeX-insert-environment env)))))
+
+;; (add-hook 'after-init-hook
+;; (lambda ()
+(eval-after-load "latex"
+ '(progn
+ (add-hook 'LaTeX-mode-hook 'asy-insinuate-latex-maybe)
+ (setq lasy-mode-map (copy-keymap LaTeX-mode-map))
+ (setq LaTeX-mode-map-backup (copy-keymap LaTeX-mode-map))
+
+ (defadvice TeX-add-local-master (after asy-adjust-local-variable ())
+ "Delete the line that defines the mode in a file .tex because two-mode-mode reread
+the local variables after switching mode."
+ (when (string= (file-name-extension buffer-file-name) "tex")
+ (save-excursion
+ (goto-char (point-max))
+ (delete-matching-lines
+ "mode: latex"
+ (re-search-backward "^\\([^\n]+\\)Local Variables:"
+ (- (point-max) 3000) t)
+ (re-search-forward (regexp-quote
+ (concat (match-string 1)
+ "End:"))) nil))))
+ (ad-activate 'TeX-add-local-master)
+ ;; (ad-deactivate 'TeX-add-local-master)
+
+ (when lasy-extra-key
+ (define-key lasy-mode-map (kbd "<C-return>")
+ (lambda ()
+ (interactive)
+ (lasy-view-ps nil nil t)))
+ (define-key lasy-mode-map (kbd "<C-S-return>")
+ (lambda ()
+ (interactive)
+ (lasy-view-ps t nil t)))
+ (define-key lasy-mode-map (kbd "<M-return>")
+ (lambda ()
+ (interactive)
+ (lasy-view-pdf-via-pdflatex nil nil t)))
+ (define-key lasy-mode-map (kbd "<M-S-return>")
+ (lambda ()
+ (interactive)
+ (lasy-view-pdf-via-pdflatex t nil t)))
+ (define-key lasy-mode-map (kbd "<C-M-return>")
+ (lambda ()
+ (interactive)
+ (lasy-view-pdf-via-ps2pdf nil nil t)))
+ (define-key lasy-mode-map (kbd "<C-M-S-return>")
+ (lambda ()
+ (interactive)
+ (lasy-view-pdf-via-ps2pdf t nil t)))
+ (define-key lasy-mode-map (kbd "<f4>") 'asy-goto-error))
+
+ (easy-menu-define asy-latex-mode-menu lasy-mode-map "Asymptote insinuates LaTeX" asy-latex-menu-item)
+ ))
+;; ))
+
+(defvar asy-insinuate-latex-p nil
+ "Not nil when current buffer is insinuated by Asymptote.
+May be a local variable.
+For internal use.")
+
+(defvar asy-insinuate-latex-globally-p nil
+ "Not nil when all latex-mode buffers is insinuated by Asymptote.
+For internal use.")
+
+(defun asy-set-latex-asy-indentation ()
+ "Set the indentation of environnment 'asy' like the environnment 'verbatim' is."
+ ;; Regexp matching environments with indentation at col 0 for begin/end.
+ (set (make-local-variable 'LaTeX-verbatim-regexp)
+ (concat (default-value 'LaTeX-verbatim-regexp) "\\|asy"))
+ ;; Alist of environments with special indentation.
+ (make-local-variable 'LaTeX-indent-environment-list)
+ (add-to-list 'LaTeX-indent-environment-list
+ '("asy" current-indentation)))
+
+(defun asy-unset-latex-asy-indentation ()
+ "Unset the indentation of environnment 'asy' like the environnment 'verbatim' is."
+ (set (make-local-variable 'LaTeX-verbatim-regexp)
+ (default-value 'LaTeX-verbatim-regexp))
+ (set (make-local-variable 'LaTeX-indent-environment-list)
+ (default-value 'LaTeX-indent-environment-list)))
+
+(defun asy-no-insinuate-locally ()
+ (interactive)
+ (set (make-local-variable 'asy-insinuate-latex-p) nil)
+ (setq asy-insinuate-latex-globally-p nil)
+ (asy-unset-latex-asy-indentation)
+ (if running-xemacs-p
+ (easy-menu-remove-item nil nil "Asymptote")
+ (menu-bar-update-buffers))
+ (if (and (boundp 'two-mode-bool) two-mode-bool)
+ (lasy-mode))
+ (use-local-map LaTeX-mode-map-backup))
+
+
+(defun asy-no-insinuate-globally ()
+ (interactive)
+ (if running-xemacs-p
+ (easy-menu-remove-item nil nil "Asymptote")
+ (easy-menu-remove-item LaTeX-mode-map nil "Asymptote"))
+ (kill-local-variable asy-insinuate-latex-p)
+ (setq-default asy-insinuate-latex-p nil)
+ (setq asy-insinuate-latex-globally-p nil)
+ (if (not running-xemacs-p)
+ (menu-bar-update-buffers))
+ (setq LaTeX-mode-map (copy-keymap LaTeX-mode-map-backup))
+ ;;Disable lasy-mode in all latex-mode buffers.
+ (when (featurep 'two-mode-mode)
+ (mapc (lambda (buffer)
+ (with-current-buffer buffer
+ (when (and (buffer-file-name) (string= (file-name-extension (buffer-file-name)) "tex"))
+ (asy-unset-latex-asy-indentation)
+ (latex-mode)
+ (setq asy-insinuate-latex-p nil))))
+ (buffer-list))))
+
+;;;###autoload
+(defun asy-insinuate-latex (&optional global)
+ "Add a menu bar in current 'latex-mode' buffer and activate asy keys bindings.
+If the optional parameter (only for internal use) 'global' is 't' then all the FUTURE 'latex-mode' buffers are insinuated.
+To insinuate all (current and future) 'latex-mode' buffers, use 'asy-insinuate-latex-globally' instead.
+You can automate this feature for all the 'latex-mode' buffers by inserting the five following lines in your .emacs initialization file:
+ (eval-after-load \"latex\"
+ '(progn
+ ;; Add here your personal features for 'latex-mode':
+ (asy-insinuate-latex t) ;; Asymptote insinuates globally Latex.
+ ))"
+ (interactive)
+ (if (and (not asy-insinuate-latex-globally-p) (or global (string= major-mode "latex-mode")))
+ (progn
+ (asy-set-latex-asy-indentation)
+ (if global
+ (progn
+ (setq asy-insinuate-latex-p t)
+ (setq asy-insinuate-latex-globally-p t)
+ (setq LaTeX-mode-map (copy-keymap lasy-mode-map))
+ (if running-xemacs-p
+ (add-hook 'LaTeX-mode-hook
+ (lambda ()
+ (if asy-insinuate-latex-globally-p
+ (easy-menu-add asy-latex-mode-menu lasy-mode-map))))))
+ (progn
+ (use-local-map lasy-mode-map)
+ (easy-menu-add asy-latex-mode-menu lasy-mode-map)
+ (set (make-local-variable 'asy-insinuate-latex-p) t)))
+ )))
+
+(defun asy-insinuate-latex-globally ()
+ "Insinuates all (current and future) 'latex-mode' buffers.
+See `asy-insinuate-latex'."
+ (interactive)
+ (asy-insinuate-latex t)
+ (if running-xemacs-p
+ (add-hook 'LaTeX-mode-hook
+ (lambda ()
+ (if asy-insinuate-latex-globally-p
+ (easy-menu-add asy-latex-mode-menu lasy-mode-map)))))
+ (mapc (lambda (buffer)
+ (with-current-buffer buffer
+ (when (and
+ (buffer-file-name)
+ (string= (file-name-extension (buffer-file-name)) "tex"))
+ (setq asy-insinuate-latex-p t)
+ (use-local-map LaTeX-mode-map)
+ (use-local-map lasy-mode-map)
+ (asy-set-latex-asy-indentation)
+ (easy-menu-add asy-latex-mode-menu lasy-mode-map))))
+ (buffer-list)))
+
+(defun lasy-inline-p()
+ "Return nil if the option 'inline' is not used or if `lasy-compilation-inline-auto-detection' value is nil."
+ (if lasy-compilation-inline-auto-detection
+ (save-excursion
+ (re-search-backward "^[^%]* *\\\\usepackage\\[ *inline *\\]{ *asymptote *}" 0 t))
+ nil))
+
+(defvar lasy-run-tex nil)
+(defun lasy-asydef()
+ "Return the content between the tags \begin{asydef} and \end{asydef}."
+ (save-excursion
+ (if (re-search-backward "\\\\begin{asydef}" 0 t)
+ (buffer-substring
+ (progn (next-line)(beginning-of-line)(point))
+ (progn (re-search-forward "\\\\end{asydef}")
+ (previous-line)(end-of-line)
+ (point)))
+ "")))
+
+(defun lasy-compile-tex()
+ "Compile region between \\begin{asy}[text with backslash] and \\end{asy} through a reconstructed file .tex."
+ (interactive)
+ (setq lasy-run-tex t)
+ (save-excursion
+ (let* ((Filename (asy-get-temp-file-name t))
+ (FilenameTex (concat Filename ".tex"))
+ (asydef (lasy-asydef)))
+ (save-excursion
+ (beginning-of-buffer)
+ (write-region (point)
+ (progn
+ (re-search-forward "\\\\begin{document}.*\n")
+ (point)) FilenameTex)
+ (write-region (concat "\\begin{asydef}\n" asydef "\n\\end{asydef}\n") 0 FilenameTex t))
+ (re-search-backward "\\\\begin{asy}")
+ (write-region (point) (progn
+ (re-search-forward "\\\\end{asy}")
+ (point)) FilenameTex t)
+ (with-temp-file FilenameTex
+ (insert-file FilenameTex)
+ (end-of-buffer)
+ (insert "\n\\end{document}"))
+ (let ((default-directory asy-temp-dir))
+ (lasy-view-ps t Filename)))))
+
+(defun lasy-compile()
+ "Compile region between \\begin{asy} and \\end{asy}."
+ (interactive)
+ (if (or (lasy-inline-p) (progn ;; find \begin{asy}[any backslash]
+ (save-excursion
+ (re-search-forward "\\\\end{asy}" (point-max) t)
+ (re-search-backward "\\\\begin{asy}.*\\(\\[.*\\\\.*\\]\\)" 0 t))
+ (match-string 1)))
+ (progn
+ (lasy-compile-tex)) ;; a temporary TeX file must be reconstructed.
+ (progn
+ (setq lasy-run-tex nil)
+ (save-excursion
+ (let ((Filename (asy-get-temp-file-name))
+ (asydef (lasy-asydef)))
+ (write-region (match-string 0) 0 Filename)
+ (re-search-backward "\\\\begin{asy}")
+ (write-region (point) (progn
+ (re-search-forward "\\\\end{asy}")
+ (point)) Filename)
+ (with-temp-file Filename
+ (insert-file-contents Filename)
+ (beginning-of-buffer)
+ (if (re-search-forward "\\\\begin{asy}\\[\\(.*\\)\\]" (point-max) t)
+ (let ((sz (match-string 1)))
+ (replace-match "")
+ (insert (concat asydef "\nsize(" sz ");")))
+ (when (re-search-forward "\\\\begin{asy}" (point-max) t)
+ (replace-match "")
+ (insert asydef)))
+ (while (re-search-forward "\\\\end{asy}" (point-max) t)
+ (replace-match "")))
+ (let* ((asy-compile-command
+ (concat asy-command-location
+ asy-command
+ (if (eq asy-compilation-buffer 'never)
+ " " " -wait ")
+ (asy-protect-file-name Filename))))
+ (asy-internal-compile
+ asy-compile-command t
+ (not (eq asy-compilation-buffer 'never)))))))))
+
+(defun asy-set-master-tex ()
+ "Set the local variable 'asy-TeX-master-file.
+This variable is used by 'asy-master-tex-view-ps"
+ (interactive)
+ (set (make-local-variable 'asy-TeX-master-file)
+ (file-name-sans-extension
+ (file-relative-name
+ (expand-file-name
+ (read-file-name "TeX document: ")))))
+ (if (string= (concat default-directory asy-TeX-master-file)
+ (file-name-sans-extension buffer-file-name))
+ (prog1
+ (set (make-local-variable 'asy-TeX-master-file) nil)
+ (error "You should never give the same name to the TeX file and the Asymptote file"))
+ (save-excursion
+ (end-of-buffer)
+ (if (re-search-backward "asy-TeX-master-file\\(.\\)*$" 0 t)
+ (replace-match (concat "asy-TeX-master-file: \"" asy-TeX-master-file "\""))
+ (insert (concat "
+/// Local Variables:
+/// asy-TeX-master-file: \"" asy-TeX-master-file "\"
+/// End:")) t))))
+
+(defun asy-unset-master-tex ()
+ "Set the local variable 'asy-TeX-master-file to 'nil.
+This variable is used by 'asy-master-tex-view-ps"
+ (interactive)
+ (set (make-local-variable 'asy-TeX-master-file) nil)
+ (save-excursion
+ (end-of-buffer)
+ (if (re-search-backward "^.*asy-TeX-master-file:.*\n" 0 t)
+ (replace-match ""))))
+
+(defun asy-master-tex-error ()
+ "Asy-mode internal use..."
+ (if (y-or-n-p "You try to compile the TeX document that contains this picture.
+You must set the local variable asy-TeX-master-file.
+Do you want set this variable now ?")
+ (asy-set-master-tex) nil))
+
+(defun asy-master-tex-view (Func-view &optional Force fromtex)
+ "Compile the LaTeX document that contains the picture of the current Asymptote code with the function Func-view.
+Func-view can be one of 'lasy-view-ps, 'lasy-view-pdf-via-pdflatex, 'lasy-view-pdf-via-ps2pdf."
+ (interactive)
+ (if (or
+ (and (boundp two-mode-bool) two-mode-bool)
+ (string-match "latex" (downcase mode-name)))
+ (progn ;; Current mode is lasy-mode or latex-mode not asy-mode
+ (funcall Func-view Force nil fromtex))
+ (if asy-TeX-master-file
+ (if (string= asy-TeX-master-file
+ (file-name-sans-extension buffer-file-name))
+ (error "You should never give the same name to the TeX file and the Asymptote file")
+ (funcall Func-view Force asy-TeX-master-file fromtex))
+ (if (asy-master-tex-error)
+ (funcall Func-view Force asy-TeX-master-file fromtex)))))
+
+(defvar asy-last-compilation-code nil
+ "Code returned by the last compilation with `compile'.")
+
+(defvar asy-compilation-auto-close nil
+ "Variable to communicate with `asy-compilation-finish-function'.
+Do not set this variable in any fashion.")
+
+(defun asy-compilation-finish-function (buf msg)
+ "Function to automatically close the compilation buffer '*asy-compilation*'
+when no error or warning occurs."
+ (when (string-match "*asy-compilation*" (buffer-name buf))
+ (when (and asy-compilation-auto-close
+ (eq asy-compilation-buffer 'none))
+ (setq asy-compilation-auto-close nil)
+ (if (not (string-match "exited abnormally" msg))
+ (progn
+ (save-excursion
+ (set-buffer buf)
+ (beginning-of-buffer)
+ (if (not (search-forward-regexp "[wW]arning" nil t))
+ (when (not (eq asy-compilation-buffer 'visible))
+ ;;no errors/Warning, make the compilation window go away
+ (run-at-time 0.5 nil (lambda (buf_)
+ (delete-windows-on buf_)
+ (kill-buffer buf_)) buf)
+ (message (replace-regexp-in-string "\n" "" msg)))
+ (message "Compilation warnings..."))))))))
+
+(if running-xemacs-p
+ (setq compilation-finish-function 'asy-compilation-finish-function)
+ (add-to-list 'compilation-finish-functions
+ 'asy-compilation-finish-function))
+
+(defun asy-compilation-wait(&optional pass auto-close)
+ "Wait for process in *asy-compilation* exits.
+If pass is 't' don't wait.
+If auto-close is 't' close the window if the process exit with success."
+ (setq asy-compilation-auto-close auto-close)
+ (let* ((buff (get-buffer "*asy-compilation*"))
+ (comp-proc (get-buffer-process buff)))
+ (while (and comp-proc
+ (not (eq (process-status comp-proc) 'exit))
+ (not pass))
+ (setq comp-proc (get-buffer-process buff))
+ (sit-for 1)
+ (message "Waiting process...") ;; need message in Windows system
+ )
+ (message "") ;; Erase previous message.
+ (if (and (not pass) comp-proc)
+ (setq asy-last-compilation-code (process-exit-status comp-proc))
+ (setq asy-last-compilation-code 0))
+ (when (and (eq asy-compilation-buffer 'available)
+ (zerop asy-last-compilation-code))
+ (delete-windows-on buff))))
+
+
+(defun asy-internal-shell (command &optional pass)
+ "Execute 'command' in a inferior shell discarding output and
+redirecting stderr in the file given by the command `asy-log-filename'.
+`asy-internal-shell' waits for PROGRAM to terminate and returns a numeric exit status.
+The variable `asy-last-compilation-code' is always set to the exit status.
+The optional argument pass, for compatibility, is not used."
+ (let* ((log-file (asy-log-filename))
+ (discard (if pass 0 nil))
+ (status
+ (progn
+ (let ((view-inhibit-help-message t))(write-region "" 0 log-file nil))
+ (message "%s" command)
+ (call-process shell-file-name nil (list nil log-file) nil shell-command-switch command))))
+ (setq asy-last-compilation-code (if status status 0))
+ (if status status nil)))
+
+;; (defun asy-internal-shell (command &optional pass)
+;; "Execute 'command' in a inferior shell discarding output and
+;; redirecting stderr in the file given by the command `asy-log-filename'.
+;; pass non-nil means `asy-internal-shell' returns immediately with nil value.
+;; Otherwise it waits for PROGRAM to terminate and returns a numeric exit status.
+;; The variable `asy-last-compilation-code' is always set to the exit status or 0 if the
+;; process returns immediately."
+;; (let* ((log-file (asy-log-filename))
+;; (discard (if pass 0 nil))
+;; (status
+;; (progn
+;; (let ((inhibit-redisplay t))(write-region "" 0 log-file nil))
+;; (message "%s" command)
+;; (call-process shell-file-name nil (list discard log-file) nil shell-command-switch command))))
+;; (setq asy-last-compilation-code (if status status 0))
+;; (when pass (sit-for 1))
+;; (if status status nil)))
+
+(defun asy-internal-compile (command &optional pass auto-close stderr)
+ "Execute command.
+pass non-nil means don't wait the end of the process.
+auto-close non-nil means automatically close the compilation buffer.
+stderr non-nil means redirect the standard output error to the file
+returned by `asy-log-filename'.
+In this case command is running in an inferior shell without any output and
+the parameter auto-close is not used (see `asy-internal-shell')."
+ (setq asy-last-compilation-code -1)
+ (let* ((compilation-buffer-name "*asy-compilation*")
+ (compilation-buffer-name-function (lambda (mj) compilation-buffer-name)))
+ (if (or stderr (eq asy-compilation-buffer 'never))
+ (progn
+ (asy-internal-shell command pass)
+ (asy-error-message t))
+ (progn
+ (let ((comp-proc (get-buffer-process compilation-buffer-name)))
+ (if comp-proc
+ (condition-case ()
+ (progn
+ (interrupt-process comp-proc)
+ (sit-for 1)
+ (delete-process comp-proc)
+ (when (and asy-compilation-auto-close
+ (eq asy-compilation-buffer 'none)
+ (not (eq asy-compilation-buffer 'visible)))
+ (sit-for 0.6)))
+ (error ""))
+ ))
+ (let ((view-inhibit-help-message t))
+ (write-region "" 0 (asy-log-filename) nil))
+ (compile command))
+ (asy-compilation-wait pass auto-close))))
+
+(defun asy-open-file(Filename)
+ "Open the ps or pdf file Filename.
+In unix-like system the variables `ps-view-command' and `pdf-view-command' are used.
+In Windows the associated system file type is used instead."
+ (let ((command
+ (if running-unix-p
+ (let ((ext (file-name-extension Filename)))
+ (cond
+ ((string= ext "ps") ps-view-command)
+ ((string= ext "pdf") pdf-view-command)
+ (t (error "Extension Not Supported."))))
+ (asy-protect-file-name (file-name-nondirectory Filename))))
+ )
+ (if running-unix-p
+ (start-process "" nil command Filename)
+ (call-process-shell-command command nil 0))))
+
+(defun lasy-TeX-master-file ()
+ "Return the file name of the master file for the current document.
+The returned string contain the directory but does not contain the extension of the file."
+ (expand-file-name
+ (concat (TeX-master-directory) (TeX-master-file nil t))))
+
+(defun lasy-must-compile-p (TeX-Master-File out-file &optional Force)
+ ""
+ (or Force
+ (file-newer-than-file-p
+ (concat TeX-Master-File ".tex") out-file)
+ (and (stringp (TeX-master-file)) ;; current buffer is not a mater tex file
+ (file-newer-than-file-p buffer-file-name out-file))))
+
+(defun lasy-view-ps (&optional Force Filename fromtex)
+ "Compile a LaTeX document embedding Asymptote code with latex->asy->latex->dvips and/or view the PostScript output.
+If optional argument Force is t then force compilation."
+ (interactive)
+ (setq lasy-run-tex t)
+ (setq lasy-compile-tex fromtex)
+ (if (buffer-modified-p) (save-buffer))
+ (when (eq asy-compilation-buffer 'never) (write-region "" 0 (asy-log-filename) nil))
+ (let*
+ ((b-b-n (if Filename Filename (lasy-TeX-master-file)))
+ (b-b-n-tex (asy-protect-file-name (concat b-b-n ".tex")))
+ (b-b-n-ps (asy-protect-file-name (concat b-b-n ".ps")))
+ (b-b-n-dvi (asy-protect-file-name (concat b-b-n ".dvi")))
+ (b-b-n-asy (asy-protect-file-name (concat b-b-n ".asy")))
+ (stderr (eq asy-compilation-buffer 'never)))
+ (if (lasy-must-compile-p b-b-n (concat b-b-n ".ps") Force)
+ (progn
+ (let ((default-directory (file-name-directory b-b-n)))
+ (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex))
+ (when (and (zerop asy-last-compilation-code) (file-readable-p (concat b-b-n ".asy")))
+ (asy-internal-compile (concat asy-command-location lasy-command " " b-b-n-asy) nil nil stderr)
+ (when (zerop asy-last-compilation-code)
+ (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex))))
+ (when (zerop asy-last-compilation-code)
+ (asy-internal-compile (concat lasy-dvips-command " " b-b-n-dvi " -o " b-b-n-ps) nil t)
+ (when (zerop asy-last-compilation-code)
+ (asy-open-file (concat b-b-n ".ps"))))))
+ (asy-open-file (concat b-b-n ".ps")))))
+
+(defun lasy-view-pdf-via-pdflatex (&optional Force Filename fromtex)
+ "Compile a LaTeX document embedding Asymptote code with pdflatex->asy->pdflatex and/or view the PDF output.
+If optional argument Force is t then force compilation."
+ (interactive)
+ (setq lasy-run-tex t)
+ (setq lasy-compile-tex fromtex)
+ (if (buffer-modified-p) (save-buffer))
+ (when (eq asy-compilation-buffer 'never) (write-region "" 0 (asy-log-filename) nil))
+ (let*
+ ((b-b-n (if Filename Filename (lasy-TeX-master-file)))
+ (b-b-n-tex (asy-protect-file-name (concat b-b-n ".tex")))
+ (b-b-n-pdf (asy-protect-file-name (concat b-b-n ".pdf")))
+ (b-b-n-asy (asy-protect-file-name (concat b-b-n ".asy")))
+ ;; (stderr (or (eq asy-compilation-buffer 'never) lasy-compile-tex)))
+ (stderr (eq asy-compilation-buffer 'never)))
+ (if (lasy-must-compile-p b-b-n (concat b-b-n ".pdf") Force)
+ (progn
+ (let ((default-directory (file-name-directory b-b-n)))
+ (asy-internal-compile (concat lasy-pdflatex-command " " b-b-n-tex))
+ (when (and (zerop asy-last-compilation-code) (file-readable-p (concat b-b-n ".asy")))
+ (asy-internal-compile (concat asy-command-location lasy-command " " b-b-n-asy) nil nil stderr)
+ (when (zerop asy-last-compilation-code)
+ (asy-internal-compile (concat lasy-pdflatex-command " " b-b-n-tex) t)))
+ (when (zerop asy-last-compilation-code)
+ (asy-open-file (concat b-b-n ".pdf")))))
+ (asy-open-file (concat b-b-n ".pdf")))))
+
+(defun lasy-view-pdf-via-ps2pdf (&optional Force Filename fromtex)
+ "Compile a LaTeX document embedding Asymptote code with latex->asy->latex->dvips->ps2pdf14 and/or view the PDF output.
+If optional argument Force is t then force compilation."
+ (interactive)
+ (setq lasy-run-tex t)
+ (setq lasy-compile-tex fromtex)
+ (if (buffer-modified-p) (save-buffer))
+ (when (eq asy-compilation-buffer 'never) (write-region "" 0 (asy-log-filename) nil))
+ (let*
+ ((b-b-n (if Filename Filename (lasy-TeX-master-file)))
+ (b-b-n-tex (asy-protect-file-name (concat b-b-n ".tex")))
+ (b-b-n-ps (asy-protect-file-name (concat b-b-n ".ps")))
+ (b-b-n-dvi (asy-protect-file-name (concat b-b-n ".dvi")))
+ (b-b-n-pdf (asy-protect-file-name (concat b-b-n ".pdf")))
+ (b-b-n-asy (asy-protect-file-name (concat b-b-n ".asy")))
+ ;; (stderr (or (eq asy-compilation-buffer 'never) lasy-compile-tex)))
+ (stderr (eq asy-compilation-buffer 'never)))
+ (if (lasy-must-compile-p b-b-n (concat b-b-n ".pdf") Force)
+ (progn
+ (let ((default-directory (file-name-directory b-b-n)))
+ (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex))
+ (when (and (zerop asy-last-compilation-code) (file-readable-p (concat b-b-n ".asy")))
+ (asy-internal-compile (concat asy-command-location lasy-command " " b-b-n-asy) nil nil stderr)
+ (when (zerop asy-last-compilation-code)
+ (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex))))
+ (when (zerop asy-last-compilation-code)
+ (asy-internal-compile (concat lasy-dvips-pre-pdf-command " " b-b-n-dvi " -o " b-b-n-ps))
+ (when (zerop asy-last-compilation-code)
+ (asy-internal-compile (concat lasy-ps2pdf-command " " b-b-n-ps " " b-b-n-pdf) t)
+ (when (zerop asy-last-compilation-code)
+ (asy-open-file (concat b-b-n ".pdf")))))))
+ (asy-open-file (concat b-b-n ".pdf")))))
+
+;; Goto error of last compilation
+(define-key asy-mode-map (kbd "<f4>") 'asy-goto-error)
+
+;; Save and compile the file with option -V
+(define-key asy-mode-map (kbd "C-c C-c") 'asy-compile)
+
+;; Show the definitions of command at point
+(define-key asy-mode-map (kbd "C-c ?") 'asy-show-function-at-point)
+
+;; new line and indent
+(define-key asy-mode-map (kbd "RET") 'newline-and-indent)
+
+(defun asy-master-tex-view-ps ()
+ "Look at `asy-master-tex-view'"
+ (interactive)
+ (asy-master-tex-view 'lasy-view-ps nil t))
+(define-key asy-mode-map (kbd "<C-return>") 'asy-master-tex-view-ps)
+
+(defun asy-master-tex-view-ps-f ()
+ "Look at `asy-master-tex-view'"
+ (interactive)
+ (asy-master-tex-view 'lasy-view-ps t t))
+(define-key asy-mode-map (kbd "<C-S-return>") 'asy-master-tex-view-ps-f)
+
+(defun asy-master-tex-view-pdflatex ()
+ "Look at `asy-master-tex-view'"
+ (interactive)
+ (asy-master-tex-view 'lasy-view-pdf-via-pdflatex nil t))
+(define-key asy-mode-map (kbd "<M-return>") 'asy-master-tex-view-pdflatex)
+
+(defun asy-master-tex-view-pdflatex-f ()
+ "Look at `asy-master-tex-view'"
+ (interactive)
+ (asy-master-tex-view 'lasy-view-pdf-via-pdflatex t t))
+(define-key asy-mode-map (kbd "<M-S-return>") 'asy-master-tex-view-pdflatex-f)
+
+(defun asy-master-tex-view-ps2pdf ()
+ "Look at `asy-master-tex-view'"
+ (interactive)
+ (asy-master-tex-view 'lasy-view-pdf-via-ps2pdf nil t))
+(define-key asy-mode-map (kbd "<C-M-return>") 'asy-master-tex-view-ps2pdf)
+
+(defun asy-master-tex-view-ps2pdf-f ()
+ "Look at `asy-master-tex-view'"
+ (interactive)
+ (asy-master-tex-view 'lasy-view-pdf-via-ps2pdf t t))
+(define-key asy-mode-map (kbd "<C-M-S-return>") 'asy-master-tex-view-ps2pdf-f)
+
+(provide `asy-mode)
diff --git a/Master/texmf-dist/asymptote/asy.vim b/Master/texmf-dist/asymptote/asy.vim
new file mode 100644
index 00000000000..4cb897f1316
--- /dev/null
+++ b/Master/texmf-dist/asymptote/asy.vim
@@ -0,0 +1,202 @@
+" Vim syntax file
+" Language: Asymptote
+" Maintainer: Andy Hammerlindl
+" Last Change: 2005 Aug 23
+
+" Hacked together from Bram Moolenaar's C syntax file, and Claudio Fleiner's
+" Java syntax file.
+
+" For version 5.x: Clear all syntax items
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+ syntax clear
+elseif exists("b:current_syntax")
+ finish
+endif
+
+" A bunch of useful C keywords
+syn keyword asyStatement break return continue unravel
+syn keyword asyConditional if else
+syn keyword asyRepeat while for do
+syn keyword asyExternal access from import include
+syn keyword asyOperator new operator
+
+syn keyword asyTodo contained TODO FIXME XXX
+
+" asyCommentGroup allows adding matches for special things in comments
+syn cluster asyCommentGroup contains=asyTodo
+
+" String and Character constants
+" Highlight special characters (those proceding a double backslash) differently
+syn match asySpecial display contained "\\\\."
+" Highlight line continuation slashes
+syn match asySpecial display contained "\\$"
+syn region asyString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=asySpecial
+ " asyCppString: same as asyString, but ends at end of line
+if 0
+syn region asyCppString start=+"+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=asySpecial
+endif
+
+"when wanted, highlight trailing white space
+if exists("asy_space_errors")
+ if !exists("asy_no_trail_space_error")
+ syn match asySpaceError display excludenl "\s\+$"
+ endif
+ if !exists("asy_no_tab_space_error")
+ syn match asySpaceError display " \+\t"me=e-1
+ endif
+endif
+
+"catch errors caused by wrong parenthesis and brackets
+syn cluster asyParenGroup contains=asyParenError,asyIncluded,asySpecial,asyCommentSkip,asyCommentString,asyComment2String,@asyCommentGroup,asyCommentStartError,asyUserCont,asyUserLabel,asyBitField,asyCommentSkip,asyOctalZero,asyCppOut,asyCppOut2,asyCppSkip,asyFormat,asyNumber,asyFloat,asyOctal,asyOctalError,asyNumbersCom
+if exists("asy_no_bracket_error")
+ syn region asyParen transparent start='(' end=')' contains=ALLBUT,@asyParenGroup,asyCppParen,asyCppString
+ " asyCppParen: same as asyParen but ends at end-of-line; used in asyDefine
+ syn region asyCppParen transparent start='(' skip='\\$' excludenl end=')' end='$' contained contains=ALLBUT,@asyParenGroup,asyParen,asyString
+ syn match asyParenError display ")"
+ syn match asyErrInParen display contained "[{}]"
+else
+ syn region asyParen transparent start='(' end=')' contains=ALLBUT,@asyParenGroup,asyCppParen,asyErrInBracket,asyCppBracket,asyCppString
+ " asyCppParen: same as asyParen but ends at end-of-line; used in asyDefine
+ syn region asyCppParen transparent start='(' skip='\\$' excludenl end=')' end='$' contained contains=ALLBUT,@asyParenGroup,asyErrInBracket,asyParen,asyBracket,asyString
+if 0
+ syn match asyParenError display "[\])]"
+ syn match asyErrInParen display contained "[\]]"
+endif
+ syn region asyBracket transparent start='\[' end=']' contains=ALLBUT,@asyParenGroup,asyErrInParen,asyCppParen,asyCppBracket,asyCppString
+ " asyCppBracket: same as asyParen but ends at end-of-line; used in asyDefine
+ syn region asyCppBracket transparent start='\[' skip='\\$' excludenl end=']' end='$' contained contains=ALLBUT,@asyParenGroup,asyErrInParen,asyParen,asyBracket,asyString
+ syn match asyErrInBracket display contained "[);]"
+endif
+
+"integer number, or floating point number without a dot and with "f".
+syn case ignore
+syn match asyNumbers display transparent "\<\d\|\.\d" contains=asyNumber,asyFloat
+syn match asyNumber display contained "\d\+"
+"floating point number, with dot, optional exponent
+syn match asyFloat display contained "\d\+\.\d*\(e[-+]\=\d\+\)\="
+"floating point number, starting with a dot, optional exponent
+syn match asyFloat display contained "\.\d\+\(e[-+]\=\d\+\)\="
+"floating point number, without dot, with exponent
+syn match asyFloat display contained "\d\+e[-+]\=\d\+"
+syn case match
+
+if exists("asy_comment_strings")
+ " A comment can contain asyString, asyCharacter and asyNumber.
+ " But a "*/" inside a asyString in a asyComment DOES end the comment! So we
+ " need to use a special type of asyString: asyCommentString, which also ends on
+ " "*/", and sees a "*" at the start of the line as comment again.
+ " Unfortunately this doesn't very well work for // type of comments :-(
+ syntax match asyCommentSkip contained "^\s*\*\($\|\s\+\)"
+ syntax region asyCommentString contained start=+L\="+ skip=+\\\\\|\\"+ end=+"+ end=+\*/+me=s-1 contains=asySpecial,asyCommentSkip
+ syntax region asyComment2String contained start=+L\="+ skip=+\\\\\|\\"+ end=+"+ end="$" contains=asySpecial
+ syntax region asyCommentL start="//" skip="\\$" end="$" keepend contains=@asyCommentGroup,asyComment2String,asyCharacter,asyNumbersCom,asySpaceError
+ syntax region asyComment matchgroup=asyCommentStart start="/\*" matchgroup=NONE end="\*/" contains=@asyCommentGroup,asyCommentStartError,asyCommentString,asyCharacter,asyNumbersCom,asySpaceError
+else
+ syn region asyCommentL start="//" skip="\\$" end="$" keepend contains=@asyCommentGroup,asySpaceError
+ syn region asyComment matchgroup=asyCommentStart start="/\*" matchgroup=NONE end="\*/" contains=@asyCommentGroup,asyCommentStartError,asySpaceError
+endif
+" keep a // comment separately, it terminates a preproc. conditional
+syntax match asyCommentError display "\*/"
+syntax match asyCommentStartError display "/\*"me=e-1 contained
+
+syn keyword asyType void bool int real string
+syn keyword asyType pair triple transform guide path pen frame
+syn keyword asyType picture
+
+syn keyword asyStructure struct typedef
+syn keyword asyStorageClass static public readable private explicit
+
+syn keyword asyPathSpec and cycle controls tension atleast curl
+
+syn keyword asyConstant true false
+syn keyword asyConstant null nullframe nullpath
+
+if exists("asy_syn_plain")
+ syn keyword asyConstant currentpicture currentpen currentprojection
+ syn keyword asyConstant inch inches cm mm bp pt up down right left
+ syn keyword asyConstant E NE N NW W SW S SE
+ syn keyword asyConstant ENE NNE NNW WNW WSW SSW SSE ESE
+ syn keyword asyConstant I pi twopi
+ syn keyword asyConstant solid dotted dashed dashdotted
+ syn keyword asyConstant longdashed longdashdotted
+ syn keyword asyConstant squarecap roundcap extendcap
+ syn keyword asyConstant miterjoin roundjoin beveljoin
+ syn keyword asyConstant zerowinding evenodd
+ syn keyword asyConstant invisible black gray grey white
+ syn keyword asyConstant lightgray lightgrey
+ syn keyword asyConstant red green blue
+ syn keyword asyConstant cmyk Cyan Magenta Yellow Black
+ syn keyword asyConstant yellow magenta cyan
+ syn keyword asyConstant brown darkgreen darkblue
+ syn keyword asyConstant orange purple royalblue olive
+ syn keyword asyConstant chartreuse fuchsia salmon lightblue springgreen
+ syn keyword asyConstant pink
+endif
+
+syn sync ccomment asyComment minlines=15
+
+" Define the default highlighting.
+" For version 5.7 and earlier: only when not done already
+" For version 5.8 and later: only when an item doesn't have highlighting yet
+if version >= 508 || !exists("did_asy_syn_inits")
+ if version < 508
+ let did_asy_syn_inits = 1
+ command -nargs=+ HiLink hi link <args>
+ else
+ command -nargs=+ HiLink hi def link <args>
+ endif
+
+ HiLink asyFormat asySpecial
+ HiLink asyCppString asyString
+ HiLink asyCommentL asyComment
+ HiLink asyCommentStart asyComment
+ HiLink asyLabel Label
+ HiLink asyUserLabel Label
+ HiLink asyConditional Conditional
+ HiLink asyRepeat Repeat
+ HiLink asyCharacter Character
+ HiLink asySpecialCharacter asySpecial
+ HiLink asyNumber Number
+ HiLink asyOctal Number
+ HiLink asyOctalZero PreProc " link this to Error if you want
+ HiLink asyFloat Float
+ HiLink asyOctalError asyError
+ HiLink asyParenError asyError
+ HiLink asyErrInParen asyError
+ HiLink asyErrInBracket asyError
+ HiLink asyCommentError asyError
+ HiLink asyCommentStartError asyError
+ HiLink asySpaceError asyError
+ HiLink asySpecialError asyError
+ HiLink asyOperator Operator
+ HiLink asyStructure Structure
+ HiLink asyStorageClass StorageClass
+ HiLink asyExternal Include
+ HiLink asyPreProc PreProc
+ HiLink asyDefine Macro
+ HiLink asyIncluded asyString
+ HiLink asyError Error
+ HiLink asyStatement Statement
+ HiLink asyPreCondit PreCondit
+ HiLink asyType Type
+ HiLink asyConstant Constant
+ HiLink asyCommentString asyString
+ HiLink asyComment2String asyString
+ HiLink asyCommentSkip asyComment
+ HiLink asyString String
+ HiLink asyComment Comment
+ HiLink asySpecial SpecialChar
+ HiLink asyTodo Todo
+ HiLink asyCppSkip asyCppOut
+ HiLink asyCppOut2 asyCppOut
+ HiLink asyCppOut Comment
+ HiLink asyPathSpec Statement
+
+
+ delcommand HiLink
+endif
+
+let b:current_syntax = "c"
+
+" vim: ts=8
diff --git a/Master/texmf-dist/asymptote/asymptote.py b/Master/texmf-dist/asymptote/asymptote.py
new file mode 100644
index 00000000000..721c464d36b
--- /dev/null
+++ b/Master/texmf-dist/asymptote/asymptote.py
@@ -0,0 +1,48 @@
+# Python module to feed Asymptote with commands
+# (modified from gnuplot.py)
+from subprocess import *
+class asy:
+ def __init__(self):
+ self.session = Popen(['asy','-quiet','-interactive'],stdin=PIPE)
+ self.help()
+ def send(self, cmd):
+ self.session.stdin.write(cmd+'\n')
+ self.session.stdin.flush()
+ def size(self, size):
+ self.send("size(%d);" % size)
+ def draw(self, str):
+ self.send("draw(%s);" % str)
+ def fill(self, str):
+ self.send("fill(%s);" % str)
+ def clip(self, str):
+ self.send("clip(%s);" % str)
+ def label(self, str):
+ self.send("label(%s);" % str)
+ def shipout(self, str):
+ self.send("shipout(\"%s\");" % str)
+ def erase(self):
+ self.send("erase();")
+ def help(self):
+ print "Asymptote session is open. Available methods are:"
+ print " help(), size(int), draw(str), fill(str), clip(str), label(str), shipout(str), send(str), erase()"
+ def __del__(self):
+ print "closing Asymptote session..."
+ self.send('quit');
+ self.session.stdin.close();
+ self.session.wait()
+
+
+
+
+
+if __name__=="__main__":
+ g = asy()
+ g.size(200)
+ g.draw("unitcircle")
+ g.send("draw(unitsquare)")
+ g.fill("unitsquare, blue")
+ g.clip("unitcircle")
+ g.label("\"$O$\", (0,0), SW")
+ raw_input("press ENTER to continue")
+ g.erase()
+ del g
diff --git a/Master/texmf-dist/asymptote/babel.asy b/Master/texmf-dist/asymptote/babel.asy
new file mode 100644
index 00000000000..e93dec2a5cb
--- /dev/null
+++ b/Master/texmf-dist/asymptote/babel.asy
@@ -0,0 +1,4 @@
+void babel(string s)
+{
+ usepackage("babel",s);
+}
diff --git a/Master/texmf-dist/asymptote/bezulate.asy b/Master/texmf-dist/asymptote/bezulate.asy
new file mode 100644
index 00000000000..5b21d8c1605
--- /dev/null
+++ b/Master/texmf-dist/asymptote/bezulate.asy
@@ -0,0 +1,254 @@
+// Bezier triangulation routines written by Orest Shardt, 2008.
+
+private real fuzz=sqrtEpsilon;
+real duplicateFuzz=1e-3; // Work around font errors.
+
+private real[][] intersections(pair a, pair b, path p)
+{
+ pair delta=fuzz*unit(b-a);
+ return intersections(a-delta--b+delta,p,fuzz);
+}
+
+int countIntersections(path[] p, pair start, pair end)
+{
+ int intersects=0;
+ for(path q : p)
+ intersects += intersections(start,end,q).length;
+ return intersects;
+}
+
+path[][] containmentTree(path[] paths)
+{
+ path[][] result;
+ for(path g : paths) {
+ // check if current curve contains or is contained in a group of curves
+ int j;
+ for(j=0; j < result.length; ++j) {
+ path[] resultj=result[j];
+ int test=inside(g,resultj[0],zerowinding);
+ if(test == 1) {
+ // current curve contains group's toplevel curve;
+ // replace toplevel curve with current curve
+ 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) {
+ resultj.append(result[k]);
+ result.delete(k);
+ } else ++k;
+ }
+ break;
+ } else if(test == -1) {
+ // current curve contained within group's toplevel curve
+ resultj.push(g);
+ break;
+ }
+ }
+ // create a new group if this curve does not belong to another group
+ if(j == result.length)
+ result.push(new path[] {g});
+ }
+ return result;
+}
+
+bool isDuplicate(pair a, pair b, real relSize)
+{
+ return abs(a-b) <= duplicateFuzz*relSize;
+}
+
+path removeDuplicates(path p)
+{
+ real relSize = abs(max(p)-min(p));
+ bool cyclic=cyclic(p);
+ for(int i=0; i < length(p); ++i) {
+ if(isDuplicate(point(p,i),point(p,i+1),relSize)) {
+ p=subpath(p,0,i)&subpath(p,i+1,length(p));
+ --i;
+ }
+ }
+ return cyclic ? p&cycle : p;
+}
+
+path section(path p, real t1, real t2, bool loop=false)
+{
+ if(t2 < t1 || loop && t1 == t2)
+ t2 += length(p);
+ return subpath(p,t1,t2);
+}
+
+path uncycle(path p, real t)
+{
+ return subpath(p,t,t+length(p));
+}
+
+// returns outer paths
+void connect(path[] paths, path[] result, path[] patch)
+{
+ path[][] tree=containmentTree(paths);
+ for(path[] group : tree) {
+ path outer = group[0];
+ group.delete(0);
+ path[][] innerTree = containmentTree(group);
+ path[] remainingCurves;
+ path[] inners;
+ for(path[] innerGroup:innerTree)
+ {
+ inners.push(innerGroup[0]);
+ if(innerGroup.length>1)
+ remainingCurves.append(innerGroup[1:]);
+ }
+ connect(remainingCurves,result,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);
+
+ // find first intersection of line segment with outer curve
+ real[][] ints=intersections(start,start+d*direction,outer);
+ assert(ints.length != 0);
+ real endtime=ints[0][1]; // endtime is time on outer
+ pair end = point(outer,endtime);
+
+ // find first intersection of end--start with any inner curve
+ real starttime=0; // 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
+ curveIndex=j;
+ }
+ }
+ start=point(inners[curveIndex],starttime);
+
+ real timeoffset=2;
+ bool found=false;
+ path portion;
+ path[] allCurves = {outer};
+ allCurves.append(inners);
+
+ while(!found && timeoffset > fuzz) {
+ timeoffset /= 2;
+ if(countIntersections(allCurves,start,
+ point(outer,endtime+timeoffset)) == 2)
+ {
+ portion = subpath(outer,endtime,endtime+timeoffset)--start--cycle;
+ found=true;
+ // check if an inner curve is inside the portion
+ for(int k = 0; found && k < inners.length; ++k)
+ {
+ if(k!=curveIndex &&
+ inside(portion,point(inners[k],0),zerowinding))
+ found = false;
+ }
+ }
+ }
+
+ if(!found) timeoffset=-2;
+ while(!found && timeoffset < -fuzz) {
+ timeoffset /= 2;
+ if(countIntersections(allCurves,start,
+ point(outer,endtime+timeoffset))==2)
+ {
+ portion = subpath(outer,endtime+timeoffset,endtime)--start--cycle;
+ found = true;
+ // check if an inner curve is inside the portion
+ for(int k = 0; found && k < inners.length; ++k)
+ {
+ if(k!=curveIndex &&
+ inside(portion,point(inners[k],0),zerowinding))
+ found = false;
+ }
+ }
+ }
+ assert(found);
+ endtime=min(endtime,endtime+timeoffset);
+ timeoffset=abs(timeoffset);
+
+ // depends on the curves having opposite orientations
+ path remainder=section(outer,endtime+timeoffset,endtime)
+ --uncycle(inners[curveIndex],
+ starttime)--cycle;
+ inners.delete(curveIndex);
+ outer = remainder;
+ patch.append(portion);
+ }
+ result.append(outer);
+ }
+}
+
+bool checkSegment(path g, pair p, pair q)
+{
+ pair mid=0.5*(p+q);
+ return intersections(p,q,g).length == 2 &&
+ inside(g,mid,zerowinding) && intersections(g,mid).length == 0;
+}
+
+path subdivide(path p)
+{
+ path q;
+ int l=length(p);
+ for(int i=0; i < l; ++i)
+ q=q&subpath(p,i,i+0.5)&subpath(p,i+0.5,i+1);
+ return cyclic(p) ? q&cycle : q;
+}
+
+path[] bezulate(path[] p)
+{
+ if(p.length == 1 && length(p[0]) <= 4) return p;
+ path[] patch;
+ path[] result;
+ connect(p,result,patch);
+ for(int i=0; i < result.length; ++i) {
+ path p=result[i];
+ int refinements=0;
+ if(size(p) <= 1) return p;
+ if(!cyclic(p))
+ abort("path must be cyclic and nonselfintersecting.");
+ p=removeDuplicates(p);
+ if(length(p) > 4) {
+ static real SIZE_STEPS=10;
+ static real factor=1.05/SIZE_STEPS;
+ for(int k=1; k <= SIZE_STEPS; ++k) {
+ real L=factor*k*abs(max(p)-min(p));
+ for(int i=0; length(p) > 4 && i < length(p); ++i) {
+ bool found=false;
+ pair start=point(p,i);
+ //look for quadrilaterals and triangles with one line, 4 | 3 curves
+ for(int desiredSides=4; !found && desiredSides >= 3;
+ --desiredSides) {
+ if(desiredSides == 3 && length(p) <= 3)
+ break;
+ pair end;
+ int endi=i+desiredSides-1;
+ end=point(p,endi);
+ found=checkSegment(p,start,end) && abs(end-start) < L;
+ if(found) {
+ path p1=subpath(p,endi,i+length(p))--cycle;
+ patch.append(subpath(p,i,endi)--cycle);
+ p=removeDuplicates(p1);
+ i=-1; // increment will make i be 0
+ }
+ }
+ if(!found && k == SIZE_STEPS && length(p) > 4 && i == length(p)-1) {
+ // avoid infinite recursion
+ ++refinements;
+ if(refinements > mantissaBits) {
+ warning("subdivisions","too many subdivisions",position=true);
+ } else {
+ p=subdivide(p);
+ i=-1;
+ }
+ }
+ }
+ }
+ }
+ if(length(p) <= 4)
+ patch.append(p);
+ }
+ return patch;
+}
diff --git a/Master/texmf-dist/asymptote/binarytree.asy b/Master/texmf-dist/asymptote/binarytree.asy
new file mode 100644
index 00000000000..0a80aec4b66
--- /dev/null
+++ b/Master/texmf-dist/asymptote/binarytree.asy
@@ -0,0 +1,326 @@
+/* **********************************************************************
+ * binarytree: An Asymptote module to draw binary trees *
+ * *
+ * Copyright (C) 2006 *
+ * Tobias Langner tobias[at]langner[dot]nightlabs[dot]de *
+ * *
+ * Modified by John Bowman *
+ * *
+ ************************************************************************
+ * *
+ * 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. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this library; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin St, Fifth Floor, *
+ * Boston, MA 02110-1301 USA *
+ * *
+ * Or get it online: *
+ * http://www.gnu.org/copyleft/lesser.html *
+ * *
+ ***********************************************************************/
+
+/**
+ * default values
+ */
+real minDistDefault=0.2cm;
+real nodeMarginDefault=0.1cm;
+
+/**
+ * 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
+ */
+ void setLeft(binarytreeNode left) {
+ this.left=left;
+ this.left.parent=this;
+ }
+
+ /**
+ * sets 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
+ */
+ bool isRoot() {
+ return parent == null;
+ }
+
+ /**
+ * Returns the level of the subtree rooted at this node.
+ */
+ int getLevel() {
+ if(isRoot())
+ return 1;
+ else
+ return parent.getLevel()+1;
+ }
+
+ /**
+ * sets the children of this binarytreeNode
+ */
+ void setChildren(binarytreeNode left, binarytreeNode right) {
+ setLeft(left);
+ setRight(right);
+ }
+
+ /**
+ * creates 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.
+ */
+ int getHeight() {
+ if(left == null && right == null)
+ return 1;
+ if(left == null)
+ return right.getHeight()+1;
+ if(right == null)
+ return left.getHeight()+1;
+
+ return max(left.getHeight(),right.getHeight())+1;
+ }
+}
+
+binarytreeNode operator init() {return null;}
+
+/**
+ * "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.
+ */
+object draw(picture pic=currentpicture, binarytreeNode node, pair pos,
+ int height, real minDist, real levelDist, real nodeDiameter,
+ pen p=currentpen) {
+ 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>.
+ */
+ real getDistance(int level, int height, real minDist) {
+ return(nodeDiameter+minDist)*2^(height-level);
+ }
+
+ 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.
+ */
+ void deferredDrawNodeConnection(pair parentPos, pair childPos) {
+ pic.add(new void(frame f, transform t) {
+ pair start,end;
+ // calculate connection path
+ transform T=shift(nodeDiameter/2*unit(t*childPos-t*parentPos));
+ path arr=(T*t*parentPos)--(inverse(T)*t*childPos);
+ draw(f,PenMargin(arr,p).g,p,Arrow(5));
+ });
+ pic.addPoint(parentPos);
+ pic.addPoint(childPos);
+ }
+
+ if(left != null) {
+ pair childPos=pos-(0,levelDist)-(dist/2,0);
+ draw(pic,left,childPos,height,minDist,levelDist,nodeDiameter,p);
+ deferredDrawNodeConnection(pos,childPos);
+ }
+
+ if(right != null) {
+ pair childPos=pos-(0,levelDist)+(dist/2,0);
+ draw(pic,right,childPos,height,minDist,levelDist,nodeDiameter,p);
+ deferredDrawNodeConnection(pos,childPos);
+ }
+
+ picture obj;
+ draw(obj,circle((0,0),nodeDiameter/2),p);
+ label(obj,label,(0,0),p);
+
+ add(pic,obj,pos);
+
+ return label;
+}
+
+struct key {
+ int n;
+ bool active;
+}
+
+key key(int n, bool active=true) {key k; k.n=n; k.active=active; return k;}
+
+key operator cast(int n) {return key(n);}
+int operator cast(key k) {return k.n;}
+int[] operator cast(key[] k) {
+ int[] I;
+ for(int i=0; i < k.length; ++i)
+ I[i]=k[i].n;
+ return I;
+}
+
+key nil=key(0,false);
+
+/**
+ * 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.
+ */
+ void addKey(int key) {
+ binarytreeNode newNode=binarytreeNode(key);
+
+ if(root == null) {
+ root=newNode;
+ keys.push(key);
+ return;
+ }
+
+ binarytreeNode n=root;
+ while(n != null) {
+ if(key < n.key) {
+ if(n.left != null)
+ n=n.left;
+ else {
+ n.setLeft(newNode);
+ keys.push(key);
+ return;
+ }
+ } else if(key > n.key) {
+ if(n.right != null)
+ n=n.right;
+ else {
+ n.setRight(newNode);
+ keys.push(key);
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * returns the height of the tree
+ */
+ int getHeight() {
+ if(root == null)
+ return 0;
+ else
+ return root.getHeight();
+ }
+
+ /**
+ * adds all given keys to the tree sequentially
+ */
+ void addSearchKeys(int[] keys) {
+ for(int i=0; i < keys.length; ++i) {
+ int key=keys[i];
+ // Ignore duplicate keys
+ if(find(this.keys == key) == -1)
+ addKey(key);
+ }
+ }
+
+ binarytreeNode build(key[] keys, int[] ind) {
+ if(ind[0] >= keys.length) return null;
+ key k=keys[ind[0]];
+ ++ind[0];
+ if(!k.active) return null;
+ binarytreeNode bt=binarytreeNode(k);
+ binarytreeNode left=build(keys,ind);
+ binarytreeNode right=build(keys,ind);
+ bt.left=left; bt.right=right;
+ if(left != null) left.parent=bt;
+ if(right != null) right.parent=bt;
+ return bt;
+ }
+
+ void addKeys(key[] keys) {
+ int[] ind={0};
+ root=build(keys,ind);
+ this.keys=keys;
+ }
+
+
+ /**
+ * returns all key in the tree
+ */
+ int[] getKeys() {
+ return keys;
+ }
+}
+
+binarytree searchtree(...int[] keys)
+{
+ binarytree bt;
+ bt.addSearchKeys(keys);
+ return bt;
+}
+
+binarytree binarytree(...key[] keys)
+{
+ binarytree bt;
+ bt.addKeys(keys);
+ return bt;
+}
+
+/**
+ * draws the given binary tree.
+ */
+void draw(picture pic=currentpicture, binarytree tree,
+ real minDist=minDistDefault, real nodeMargin=nodeMarginDefault,
+ pen p=currentpen)
+{
+ int[] keys=tree.getKeys();
+
+ // calculate the node diameter so that all keys fit into it
+ frame f;
+ for(int i=0; i < keys.length; ++i)
+ label(f,math(string(keys[i])),p);
+
+ 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);
+}
diff --git a/Master/texmf-dist/asymptote/bsp.asy b/Master/texmf-dist/asymptote/bsp.asy
new file mode 100644
index 00000000000..2a4d93daa81
--- /dev/null
+++ b/Master/texmf-dist/asymptote/bsp.asy
@@ -0,0 +1,209 @@
+private import math;
+import three;
+
+real epsilon=10*realEpsilon;
+
+// Routines for hidden surface removal (via binary space partition):
+// Structure face is derived from picture.
+struct face {
+ picture pic;
+ transform t;
+ frame fit;
+ triple normal,point;
+ triple min,max;
+ void operator init(path3 p) {
+ this.normal=normal(p);
+ if(this.normal == O) abort("path is linear");
+ this.point=point(p,0);
+ min=min(p);
+ max=max(p);
+ }
+ face copy() {
+ face f=new face;
+ f.pic=pic.copy();
+ f.t=t;
+ f.normal=normal;
+ f.point=point;
+ f.min=min;
+ f.max=max;
+ add(f.fit,fit);
+ return f;
+ }
+}
+
+picture operator cast(face f) {return f.pic;}
+face operator cast(path3 p) {return face(p);}
+
+struct line {
+ triple point;
+ triple dir;
+}
+
+private line intersection(face a, face b)
+{
+ line L;
+ L.point=intersectionpoint(a.normal,a.point,b.normal,b.point);
+ L.dir=unit(cross(a.normal,b.normal));
+ return L;
+}
+
+struct half {
+ pair[] left,right;
+
+ // Sort the points in the pair array z according to whether they lie on the
+ // left or right side of the line L in the direction dir passing through P.
+ // Points exactly on L are considered to be on the right side.
+ // Also push any points of intersection of L with the path operator --(... z)
+ // onto each of the arrays left and right.
+ void operator init(pair dir, pair P ... pair[] z) {
+ pair lastz;
+ pair invdir=dir != 0 ? 1/dir : 0;
+ bool left,last;
+ for(int i=0; i < z.length; ++i) {
+ left=(invdir*z[i]).y > (invdir*P).y;
+ if(i > 0 && last != left) {
+ pair w=extension(P,P+dir,lastz,z[i]);
+ this.left.push(w);
+ this.right.push(w);
+ }
+ if(left) this.left.push(z[i]);
+ else this.right.push(z[i]);
+ last=left;
+ lastz=z[i];
+ }
+ }
+}
+
+struct splitface {
+ face back,front;
+}
+
+// Return the pieces obtained by splitting face a by face cut.
+splitface split(face a, face cut, projection P)
+{
+ splitface S;
+
+ void nointersection() {
+ if(abs(dot(a.point-P.camera,a.normal)) >=
+ abs(dot(cut.point-P.camera,cut.normal))) {
+ S.back=a;
+ S.front=null;
+ } else {
+ S.back=null;
+ S.front=a;
+ }
+ }
+
+ if(P.infinity) {
+ P=P.copy();
+ static real factor=1/sqrtEpsilon;
+ P.camera *= factor*max(abs(a.min),abs(a.max),
+ abs(cut.min),abs(cut.max));
+ }
+
+ if((abs(a.normal-cut.normal) < epsilon ||
+ abs(a.normal+cut.normal) < epsilon)) {
+ nointersection();
+ return S;
+ }
+
+ line L=intersection(a,cut);
+
+ if(dot(P.camera-L.point,P.camera-P.target) < 0) {
+ nointersection();
+ return S;
+ }
+
+ pair point=a.t*project(L.point,P);
+ pair dir=a.t*project(L.point+L.dir,P)-point;
+ pair invdir=dir != 0 ? 1/dir : 0;
+ triple apoint=L.point+cross(L.dir,a.normal);
+ bool left=(invdir*(a.t*project(apoint,P))).y >= (invdir*point).y;
+
+ real t=intersect(apoint,P.camera,cut.normal,cut.point);
+ bool rightfront=left ^ (t <= 0 || t >= 1);
+
+ face back=a, front=a.copy();
+ pair max=max(a.fit);
+ pair min=min(a.fit);
+ half h=half(dir,point,max,(min.x,max.y),min,(max.x,min.y),max);
+ if(h.right.length == 0) {
+ if(rightfront) front=null;
+ else back=null;
+ } else if(h.left.length == 0) {
+ if(rightfront) back=null;
+ else front=null;
+ }
+ if(front != null)
+ clip(front.fit,operator --(... rightfront ? h.right : h.left)--cycle,
+ zerowinding);
+ if(back != null)
+ clip(back.fit,operator --(... rightfront ? h.left : h.right)--cycle,
+ zerowinding);
+ S.back=back;
+ S.front=front;
+ return S;
+}
+
+// A binary space partition
+struct bsp
+{
+ bsp back;
+ bsp front;
+ face node;
+
+ // Construct the bsp.
+ void operator init(face[] faces, projection P) {
+ if(faces.length != 0) {
+ this.node=faces.pop();
+ face[] front,back;
+ for(int i=0; i < faces.length; ++i) {
+ splitface split=split(faces[i],this.node,P);
+ if(split.front != null) front.push(split.front);
+ if(split.back != null) back.push(split.back);
+ }
+ this.front=bsp(front,P);
+ this.back=bsp(back,P);
+ }
+ }
+
+ // Draw from back to front.
+ void add(frame f) {
+ if(back != null) back.add(f);
+ add(f,node.fit,group=true);
+ if(labels(node.fit)) layer(f); // Draw over any existing TeX layers.
+ if(front != null) front.add(f);
+ }
+}
+
+void add(picture pic=currentpicture, face[] faces,
+ projection P=currentprojection)
+{
+ int n=faces.length;
+ face[] Faces=new face[n];
+ for(int i=0; i < n; ++i)
+ Faces[i]=faces[i].copy();
+
+ pic.nodes.push(new void (frame f, transform t, transform T,
+ pair m, pair M) {
+ // Fit all of the pictures so we know their exact sizes.
+ face[] faces=new face[n];
+ for(int i=0; i < n; ++i) {
+ faces[i]=Faces[i].copy();
+ face F=faces[i];
+ F.t=t*T*F.pic.T;
+ F.fit=F.pic.fit(t,T*F.pic.T,m,M);
+ }
+
+ bsp bsp=bsp(faces,P);
+ if(bsp != null) bsp.add(f);
+ });
+
+ for(int i=0; i < n; ++i) {
+ picture F=Faces[i].pic;
+ pic.userBox3(F.userMin3(), F.userMax3());
+ pic.bounds.append(F.T, F.bounds);
+ // The above 2 lines should be replaced with a routine in picture which
+ // copies only sizing data from another picture.
+ }
+}
diff --git a/Master/texmf-dist/asymptote/contour.asy b/Master/texmf-dist/asymptote/contour.asy
new file mode 100644
index 00000000000..27a79439e2d
--- /dev/null
+++ b/Master/texmf-dist/asymptote/contour.asy
@@ -0,0 +1,1491 @@
+/*
+ Contour routines written by Radoslav Marinov, John Bowman, and Chris Savage.
+
+ [2009/10/15: C Savage] generate oriented contours
+ [2009/10/19: C Savage] use boxes instead of triangles
+*/
+
+/*
+ Contours lines/guides are oriented throughout. By convention,
+ for a single contour, higher values are to the left and/or lower
+ values are to the right along the direction of the lines/guide.
+*/
+
+import graph_settings;
+
+private real eps=sqrtEpsilon;
+
+/*
+ GRID CONTOURS
+
+ Contours on a grid of points are determined as follows:
+ for each grid square, the function is approximated as the unique
+ paraboloid passing through the function values at the four
+ corners. The intersection of a paraboloid with the f(x,y)=c
+ plane is a line or hyperbola.
+
+ Grid data structures:
+
+ boxcontour:
+ Describes a particular contour segment in a grid square.
+
+ boxdata:
+ Describes contours in a grid square (holds boxcontours).
+
+ segment:
+ Describes a contour line. Usually a closed (interior) contour,
+ a line that terminates on the border, or a border segment used
+ to enclose a region.
+
+ Segment:
+ Describes a contour line.
+
+ Main grid routines:
+
+ setcontour:
+ Determines the contours in a grid square.
+
+ contouredges:
+ Determines the contour segments over a grid of function values.
+
+ connect:
+ Converts contours into guides
+
+*/
+
+private typedef int boxtype;
+private boxtype exterior=-1;
+private boxtype edge = 0;
+private boxtype interior=+1;
+
+private typedef int contourshape;
+private contourshape line =1;
+private contourshape hyperbola=2;
+
+// Describe position by grid square and position in square
+private struct gridpoint {
+ int i,j;
+ pair z;
+ void operator init(int i, int j, pair z) {
+ this.i=i;
+ this.j=j;
+ this.z=z;
+ }
+ void operator init(gridpoint gp) {
+ this.i=gp.i;
+ this.j=gp.j;
+ this.z=gp.z;
+ }
+}
+
+private bool same(gridpoint gp1, gridpoint gp2)
+{
+ return abs(gp2.z-gp1.z+(gp2.i-gp1.i,gp2.j-gp1.j)) < eps;
+}
+
+
+// Describe contour in unit square(scaling to be done later).
+private struct boxcontour {
+ bool active;
+ contourshape type; // Shape of contour segment(line or hyperbola)
+ pair a,b; // Start/end point of contour segment.
+ // Higher values to left along a--b.
+ real x0,y0,m; // For hyperbola: (x-x0)*(y-y0)=m
+ int signx,signy; // Sign of x-x0&y-y0 for hyperbola piece;
+ // identifies which direction it opens
+ int i,j; // Indices of lower left corner in position or
+ // data array.
+ int index; // Contour index
+
+ void operator init(contourshape type, pair a, pair b,
+ real x0, real y0, real m, int signx, int signy,
+ int i, int j, int index) {
+ this.active=true;
+ this.type=type;
+ this.a=a;
+ this.b=b;
+
+ this.x0=x0;
+ this.y0=y0;
+ this.m=m;
+ this.signx=signx;
+ this.signy=signy;
+
+ this.i=i;
+ this.j=j;
+ this.index=index;
+ }
+ // Generate list of points along the line/hyperbola segment
+ // representing the contour in the box
+ gridpoint[] points(int subsample=1, bool first=true, bool last=true) {
+ gridpoint[] gp;
+ if(first)
+ gp.push(gridpoint(i,j,a));
+ if(subsample > 0) {
+ // Linear case
+ if(type == line) {
+ for(int k=1; k <= subsample; ++k) {
+ pair z=interp(a,b,k/(subsample+1));
+ gp.push(gridpoint(i,j,z));
+ }
+ } else if(type == hyperbola) {
+ // Special hyperbolic case of m=0
+ // The contours here are infinite lines at x=x0 and y=y0,
+ // but handedness always connects a semi-infinite
+ // horizontal segment with a semi-infinite vertical segment
+ // connected at (x0,y0).
+ // If (x0,y0) is outside the unit box, there is only one
+ // line segment to include; otherwise, there are both
+ // a horizontal and a vertical line segment to include.
+ if(m == 0) {
+ // Single line
+ if(a.x == b.x || a.y == b.y) {
+ for(int k=1; k <= subsample; ++k) {
+ pair z=interp(a,b,k/(subsample+1));
+ gp.push(gridpoint(i,j,z));
+ }
+ // Two lines(may get one extra point here)
+ } else {
+ int nsub=quotient(subsample,2);
+ pair mid=(x0,y0);
+ for(int k=1; k <= nsub; ++k) {
+ pair z=interp(a,mid,k/(nsub+1));
+ gp.push(gridpoint(i,j,z));
+ }
+ gp.push(gridpoint(i,j,mid));
+ for(int k=1; k <= nsub; ++k) {
+ pair z=interp(mid,b,k/(nsub+1));
+ gp.push(gridpoint(i,j,z));
+ }
+ }
+ // General hyperbolic case (m != 0).
+ // Parametric equations(m > 0):
+ // x(t)=x0 +/- sqrt(m)*exp(t)
+ // y(t)=y0 +/- sqrt(m)*exp(-t)
+ // Parametric equations (m < 0):
+ // x(t)=x0 +/- sqrt(-m)*exp(t)
+ // y(t)=y0 -/+ sqrt(-m)*exp(-t)
+ // Points will be taken equally spaced in parameter t.
+ } else {
+ real sqrtm=sqrt(abs(m));
+ real ta=log(signx*(a.x-x0)/sqrtm);
+ real tb=log(signx*(b.x-x0)/sqrtm);
+ real[] t=uniform(ta,tb,subsample+1);
+ for(int k=1; k <= subsample; ++k) {
+ pair z=(x0+signx*sqrtm*exp(t[k]),
+ y0+signy*sqrtm*exp(-t[k]));
+ gp.push(gridpoint(i,j,z));
+ }
+ }
+ }
+ }
+ if(last)
+ gp.push(gridpoint(i,j,b));
+
+ return gp;
+ }
+}
+
+// Hold data for a single grid square
+private struct boxdata {
+ boxtype type; // Does box contain a contour segment (edge of
+ // contour region) or is it entirely interior/
+ // exterior to contour region ?
+ real min,max; // Smallest/largest corner value
+ real max2; // Second-largest corner value
+ boxcontour[] data; // Stores actual contour segment data
+
+ int count() {return data.length;}
+ void operator init(real f00, real f10, real f01, real f11) {
+ real[] X={f00,f10,f01,f11};
+ min=min(X);
+ max=max(X);
+ X.delete(find(X == max));
+ max2=max(X);
+ }
+ void settype(real c) {
+ // Interior case(f >= c)
+ if(min > c-eps) {
+ type=interior;
+ // Exterior case(f < c)
+ } else if(max < c-eps) {
+ type=exterior;
+ // Special case: only one corner at f=c, f < c elsewhere
+ //(no segment in this case)
+ } else if((max < c+eps) && (max2 < c-eps)) {
+ type=exterior;
+ // Edge of contour passes through box
+ } else {
+ type=edge;
+ }
+ }
+}
+
+
+/*
+ Determine contours within a unit square box.
+
+ Here, we approximate the function on the unit square to be a quadric
+ surface passing through the specified values at the four corners:
+ f(x,y)=(1-x)(1-y) f00+x(1-y) f10+(1-x)y f01+xy f11
+ =a0+ax x+ay y+axy xy
+ where f00, f10, f01 and f11 are the function values at the four
+ corners of the unit square 0 < x < 1&0 < y < 1 and:
+ a0 =f00
+ ax =f10-f00
+ ay =f01-f00
+ axy=f00+f11-f10-f01
+ This can also be expressed in paraboloid form as:
+ f(x,y)=alpha [(x+y-cp)^2-(x-y-cn)^2]+d
+ where:
+ alpha=axy/4
+ cp =-(ax+ay)/a11
+ cn =-(ax-ay)/a11
+ d =(a0 axy-ax ay)/axy
+ In the procedure below, we take f00 - > f00-c etc. for a contour
+ level c and we search for f=0.
+
+ For this surface, there are two possible contour shapes:
+ linear: (y-y0)/(x-x0)=m
+ hyperbolic: (x-x0)*(y-y0)=m
+ The linear case has a single line. The hyperbolic case may have
+ zero, one or two segments within the box (there are two sides of
+ a hyperbola, each of which may or may not pass through the unit
+ square). A hyperbola with m=0 is a special case that is handled
+ separately below.
+
+ If c0 is the desired contour level, we effectively find the
+ contours at c0-epsilon for arbitrarily small epsilon. Flat
+ regions equal to c0 are considered to be interior to the
+ contour curves. Regions that lie at the contour level are
+ considered to be interior to the contour curves. As a result,
+ contours are only constructed if they are immediately adjacent
+ to some region interior to the square that falls below the
+ contour value; in other words, if an edge falls on the contour
+ value, but a point within the square arbitrarily close to the
+ edge falls above the contour value, that edge (or applicable
+ portion) is not included. This requirement gives the following:
+ *) ensures contours on an edge are unique (do not appear in
+ an adjacent square with the same orientation)
+ *) no three line vertices (four line vertices are possible, but
+ are not usually an issue)
+ *) all segments can be joined into closed curves or curves that
+ terminate on the boundary (no unclosed curves terminate in
+ the interior region of the grid)
+
+ Note the logic below skips cases that have been filtered out
+ by the boxdata.settype() routine.
+*/
+private void setcontour(real f00, real f10, real f01, real f11, real epsf,
+ boxdata bd, int i, int j, int index) {
+ // SPECIAL CASE: two diagonal corners at the contour level with
+ // the other two below does not yield any contours within the
+ // unit box, but may have been previously misidentified as an
+ // edge containing region.
+ if(((f00*f11 == 0) && (f10*f01 > 0)) || ((f01*f10 == 0) && (f00*f11 > 0))) {
+ bd.type=exterior;
+ return;
+ }
+
+ // NOTE: From this point on, we can assume at least one contour
+ // segment exists in the square. This allows several cases to
+ // be ignored or simplified below, particularly edge cases.
+
+ // Form used to approximate function on unit square
+ real F(real x, real y) {
+ return interp(interp(f00,f10,x),interp(f01,f11,x),y);
+ }
+
+ // Write contour as a0+ax*x+ay*y +axy*x*y=0
+ real a0 =f00;
+ real ax =f10-f00;
+ if(abs(ax) < epsf) ax=0;
+ real ay =f01-f00;
+ if(abs(ay) < epsf) ay=0;
+ real axy=f00+f11-f01 -f10;
+ if(abs(axy) < epsf) axy=0;
+
+ // Linear contour(s)
+ if(axy == 0) {
+ pair a,b;
+ // Horizontal
+ if(ax == 0) {
+ if(ay == 0) return; // Contour is at most an isolated point; ignore.
+ real y0=-a0/ay;
+ if(abs(y0-1) < eps) y0=1;
+ if((f00 > 0) || (f01 < 0)) {
+ a=(1,y0);
+ b=(0,y0);
+ } else {
+ a=(0,y0);
+ b=(1,y0);
+ }
+ // Vertical
+ } else if(ay == 0) {
+ real x0=-a0/ax;
+ if(abs(x0-1) < eps) x0=1;
+ if((f00 > 0) || (f10 < 0)) {
+ a=(x0,0);
+ b=(x0,1);
+ } else {
+ a=(x0,1);
+ b=(x0,0);
+ }
+ // Angled line
+ } else {
+ real x0=-a0/ax;
+ if(abs(x0-1) < eps) x0=1;
+ real y0=-a0/ay;
+ if(abs(y0-1) < eps) y0=1;
+ int count=0;
+ real[] farr={f00,f10,f11,f01};
+ farr.cyclic=true;
+ pair[] corners={(0,0),(1,0),(1,1),(0,1)};
+ pair[] sidedir={(1,0),(0,1),(-1,0),(0,-1)};
+
+ int count=0;
+ for(int i=0; i < farr.length; ++i) {
+ // Corner
+ if(farr[i] == 0) {
+ ++count;
+ if(farr[i-1] > 0) {
+ a=corners[i];
+ } else {
+ b=corners[i];
+ }
+ // Side
+ } else if(farr[i]*farr[i+1] < 0) {
+ ++count;
+ if(farr[i] > 0) {
+ a=corners[i]-(farr[i]/(farr[i+1]-farr[i]))*sidedir[i];
+ } else {
+ b=corners[i]-(farr[i]/(farr[i+1]-farr[i]))*sidedir[i];
+ }
+ }
+ }
+ // Check(if logic is correct above, this will not happen)
+ if(count != 2) {
+ abort("Unexpected error in setcontour routine: odd number of"
+ +" crossings (linear case)");
+ }
+ }
+ boxcontour bc=boxcontour(line,a,b,0,0,0,1,1,i,j,index);
+ bd.data.push(bc);
+ return;
+ }
+
+ // Hyperbolic contour(s)
+ // Described in form: (x-x0)*(y-y0)=m
+ real x0=-ay/axy;
+ if(abs(x0-1) < eps) x0=1;
+ real y0=-ax/axy;
+ if(abs(y0-1) < eps) y0=1;
+ real m =ay*ax-a0*axy;
+ m=(abs(m) < eps) ? 0 : m/axy^2;
+
+ // Special case here: straight segments (possibly crossing)
+ if(m == 0) {
+ pair a,b;
+ int signx,signy;
+ // Assuming at least one corner is below contour level here
+ if(x0 == 0) {
+ signx=+1;
+ if(y0 == 0) {
+ a=(1,0);
+ b=(0,1);
+ signy=+1;
+ } else if(y0 == 1) {
+ a=(0,0);
+ b=(1,1);
+ signy=-1;
+ } else if(y0 < 0 || y0 > 1) {
+ a=(0,0);
+ b=(0,1);
+ signy=y0 > 0 ? -1 : +1;
+ } else {
+ if(f10 > 0) {
+ a=(1,y0);
+ b=(0,1);
+ signy=+1;
+ } else {
+ a=(0,0);
+ b=(1,y0);
+ signy=-1;
+ }
+ }
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,signx,signy,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else if(x0 == 1) {
+ signx=-1;
+ if(y0 == 0) {
+ a=(1,1);
+ b=(0,0);
+ signy=+1;
+ } else if(y0 == 1) {
+ a=(0,1);
+ b=(1,0);
+ signy=-1;
+ } else if(y0 < 0 || y0 > 1) {
+ a=(1,1);
+ b=(1,0);
+ signy=y0 > 0 ? -1 : +1;
+ } else {
+ if(f01 > 0) {
+ a=(0,y0);
+ b=(1,0);
+ signy=-1;
+ } else {
+ a=(1,1);
+ b=(0,y0);
+ signy=+1;
+ }
+ }
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,signx,signy,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else if(y0 == 0) {
+ signy=+1;
+ if(x0 < 0 || x0 > 1) {
+ a=(1,0);
+ b=(0,0);
+ signx=x0 > 0 ? -1 : +1;
+ } else {
+ if(f11 > 0) {
+ a=(x0,1);
+ b=(0,0);
+ signx=-1;
+ } else {
+ a=(1,0);
+ b=(x0,1);
+ signx=+1;
+ }
+ }
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,signx,signy,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else if(y0 == 1) {
+ signy=-1;
+ if(x0 < 0 || x0 > 1) {
+ a=(0,1);
+ b=(1,1);
+ signx=x0 > 0 ? -1 : +1;
+ } else {
+ if(f00 > 0) {
+ a=(x0,0);
+ b=(1,1);
+ signx=+1;
+ } else {
+ a=(0,1);
+ b=(x0,0);
+ signx=-1;
+ }
+ }
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,signx,signy,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else if(x0 < 0 || x0 > 1) {
+ signx=x0 > 0 ? -1 : +1;
+ if(f00 > 0) {
+ a=(1,y0);
+ b=(0,y0);
+ signy=+1;
+ } else {
+ a=(0,y0);
+ b=(1,y0);
+ signy=-1;
+ }
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,signx,signy,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else if(y0 < 0 || y0 > 1) {
+ signy=y0 > 0 ? -1 : +1;
+ if(f00 > 0) {
+ a=(x0,0);
+ b=(x0,1);
+ signx=+1;
+ } else {
+ a=(x0,1);
+ b=(x0,0);
+ signx=-1;
+ }
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,signx,signy,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else {
+ if(f10 > 0) {
+ a=(0,y0);
+ b=(x0,0);
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,-1,-1,i,j,index);
+ bd.data.push(bc);
+ a=(1,y0);
+ b=(x0,1);
+ bc=boxcontour(hyperbola,a,b,x0,y0,m,+1,+1,i,j,index);
+ bd.data.push(bc);
+ return;
+ } else {
+ a=(x0,0);
+ b=(1,y0);
+ boxcontour bc=boxcontour(hyperbola,a,b,x0,y0,m,+1,-1,i,j,index);
+ bd.data.push(bc);
+ a=(x0,1);
+ b=(0,y0);
+ bc=boxcontour(hyperbola,a,b,x0,y0,m,-1,+1,i,j,index);
+ bd.data.push(bc);
+ return;
+ }
+ }
+ }
+
+ // General hyperbola case
+ int signc=(F(x0,y0) > 0) ? +1 : -1;
+
+ pair[] points;
+
+ real xB=(y0 == 0) ? infinity : x0-m/y0;
+ if(abs(xB) < eps) xB=0;
+ if(xB >= 0 && xB <= 1-eps) points.push((xB,0));
+
+ real xT=(y0 == 1) ? infinity : x0+m/(1-y0);
+ if(abs(xT-1) < eps) xT=1;
+ if(xT >= eps && xT <= 1) points.push((xT,1));
+
+ real yL=(x0 == 0) ? infinity : y0-m/x0;
+ if(abs(yL-1) < eps) yL=1;
+
+ if(yL > eps && yL <= 1) points.push((0,yL));
+
+ real yR=(x0 == 1) ? infinity : y0+m/(1-x0);
+ if(abs(yR) < eps) yR=0;
+ if(yR >= 0 && yR <= 1-eps) points.push((1,yR));
+
+ // Check (if logic is correct above, this will not happen)
+ if(!(points.length == 2 || points.length == 4)) {
+ abort("Unexpected error in setcontour routine: odd number of"
+ +" crossings (hyperbolic case)");
+ }
+
+ // Lower left side
+ if((x0 > 0) && (y0 > 0) && (f00*signc < 0)) {
+ pair[] pts0;
+ for(int i=0; i < points.length; ++i) {
+ if((points[i].x < x0) && (points[i].y < y0)) {
+ pts0.push(points[i]);
+ }
+ }
+ if(pts0.length == 2) {
+ pair a0,b0;
+ if((f00 > 0) ^(pts0[0].x < pts0[1].x)) {
+ a0=pts0[0];
+ b0=pts0[1];
+ } else {
+ a0=pts0[1];
+ b0=pts0[0];
+ }
+ boxcontour bc=boxcontour(hyperbola,a0,b0,x0,y0,m,-1,-1,i,j,index);
+ bd.data.push(bc);
+ }
+ }
+
+ // Lower right side
+ if((x0 < 1) && (y0 > 0) && (f10*signc < 0)) {
+ pair[] pts0;
+ for(int i=0; i < points.length; ++i) {
+ if((points[i].x > x0) && (points[i].y < y0)) {
+ pts0.push(points[i]);
+ }
+ }
+ if(pts0.length == 2) {
+ pair a0,b0;
+ if((f10 > 0) ^(pts0[0].x < pts0[1].x)) {
+ a0=pts0[0];
+ b0=pts0[1];
+ } else {
+ a0=pts0[1];
+ b0=pts0[0];
+ }
+ boxcontour bc=boxcontour(hyperbola,a0,b0,x0,y0,m,+1,-1,i,j,index);
+ bd.data.push(bc);
+ }
+ }
+
+ // Upper right side
+ if((x0 < 1) && (y0 < 1) && (f11*signc < 0)) {
+ pair[] pts0;
+ for(int i=0; i < points.length; ++i) {
+ if((points[i].x > x0) && (points[i].y > y0)) {
+ pts0.push(points[i]);
+ }
+ }
+ if(pts0.length == 2) {
+ pair a0,b0;
+ if((f11 > 0) ^(pts0[0].x > pts0[1].x)) {
+ a0=pts0[0];
+ b0=pts0[1];
+ } else {
+ a0=pts0[1];
+ b0=pts0[0];
+ }
+ boxcontour bc=boxcontour(hyperbola,a0,b0,x0,y0,m,+1,+1,i,j,index);
+ bd.data.push(bc);
+ }
+ }
+
+ // Upper left side
+ if((x0 > 0) && (y0 < 1) && (f01*signc < 0)) {
+ pair[] pts0;
+ for(int i=0; i < points.length; ++i) {
+ if((points[i].x < x0) && (points[i].y > y0)) {
+ pts0.push(points[i]);
+ }
+ }
+ if(pts0.length == 2) {
+ pair a0,b0;
+ if((f01 > 0) ^(pts0[0].x > pts0[1].x)) {
+ a0=pts0[0];
+ b0=pts0[1];
+ } else {
+ a0=pts0[1];
+ b0=pts0[0];
+ }
+ boxcontour bc=boxcontour(hyperbola,a0,b0,x0,y0,m,-1,+1,i,j,index);
+ bd.data.push(bc);
+ }
+ }
+ return;
+}
+
+
+// Checks if end of first contour segment matches the beginning of
+// the second.
+private bool connected(boxcontour bc1, boxcontour bc2) {
+ return abs(bc2.a-bc1.b+(bc2.i-bc1.i,bc2.j-bc1.j)) < eps;
+}
+
+// Returns index of first active element in bca that with beginning
+// that connects to the end of bc, or -1 if no such element.
+private int connectedindex(boxcontour bc, boxcontour[] bca,
+ bool activeonly=true) {
+ for(int i=0; i < bca.length; ++i) {
+ if(!bca[i].active) continue;
+ if(connected(bc,bca[i])) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Returns index of first active element in bca with end that connects
+// to the start of bc, or -1 if no such element.
+private int connectedindex(boxcontour[] bca, boxcontour bc,
+ bool activeonly=true) {
+ for(int i=0; i < bca.length; ++i) {
+ if(!bca[i].active) continue;
+ if(connected(bca[i],bc)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+// Processes indices for grid regions touching the
+// end/start (forward=true/false) of the contour segment
+private void searchindex(boxcontour bc, bool forward, void f(int i, int j)) {
+ pair z=forward ? bc.b : bc.a;
+
+ int i=bc.i;
+ int j=bc.j;
+
+ if(z == (0,0)) f(i-1,j-1);
+ if(z.y == 0) f(i,j-1);
+ if(z == (1,0)) f(i+1,j-1);
+ if(z.x == 1) f(i+1,j);
+ if(z == (1,1)) f(i+1,j+1);
+ if(z.y == 1) f(i,j+1);
+ if(z == (0,1)) f(i-1,j+1);
+ if(z.x == 0) f(i-1,j);
+}
+
+// Contour segment
+private struct segment {
+ gridpoint[] data;
+ void operator init() {
+ }
+ void operator init(boxcontour bc, int subsample=1) {
+ bc.active=false;
+ this.data.append(bc.points(subsample,first=true,last=true));
+ }
+ void operator init(int i, int j, pair z) {
+ gridpoint gp=gridpoint(i,j,z);
+ data.push(gp);
+ }
+ void operator init(gridpoint[] gp) {
+ this.data.append(gp);
+ }
+ gridpoint start() {
+ if(data.length == 0) {
+ return gridpoint(-1,-1,(-infinity,-infinity));
+ }
+ gridpoint gp=data[0];
+ return gridpoint(gp.i,gp.j,gp.z);
+ }
+ gridpoint end() {
+ if(data.length == 0) {
+ return gridpoint(-1,-1,(-infinity,-infinity));
+ }
+ gridpoint gp=data[data.length-1];
+ return gridpoint(gp.i,gp.j,gp.z);
+ }
+ bool closed() {
+ return same(this.start(),this.end());
+ }
+ void append(boxcontour bc, int subsample=1) {
+ bc.active=false;
+ data.append(bc.points(subsample,first=false,last=true));
+ }
+ void prepend(boxcontour bc, int subsample=1) {
+ bc.active=false;
+ data.insert(0 ... bc.points(subsample,first=true,last=false));
+ }
+ void append(int i, int j, pair z) {
+ gridpoint gp=gridpoint(i,j,z);
+ data.push(gp);
+ }
+ void prepend(int i, int j, pair z) {
+ gridpoint gp=gridpoint(i,j,z);
+ data.insert(0,gp);
+ }
+ segment copy() {
+ segment seg=new segment;
+ seg.data=new gridpoint[data.length];
+ for(int i=0; i < data.length; ++i) {
+ seg.data[i]=gridpoint(data[i].i,data[i].j,data[i].z);
+ }
+ return seg;
+ }
+ segment reversecopy() {
+ segment seg=new segment;
+ seg.data=new gridpoint[data.length];
+ for(int i=0; i < data.length; ++i) {
+ seg.data[data.length-i-1]=gridpoint(data[i].i,data[i].j,data[i].z);
+ }
+ return seg;
+ }
+}
+
+// Container to hold edge and border segments that form one continuous line
+private struct Segment {
+ segment[] edges;
+ segment[] borders;
+ void operator init() {
+ }
+ void operator init(segment seg) {
+ edges.push(seg);
+ }
+ void operator init(gridpoint[] gp) {
+ segment seg=segment(gp);
+ edges.push(seg);
+ }
+ gridpoint start() {
+ if(edges.length == 0) {
+ if(borders.length > 0) {
+ return borders[0].start();
+ }
+ return gridpoint(-1,-1,(-infinity,-infinity));
+ }
+ return edges[0].start();
+ }
+ gridpoint end() {
+ if(edges.length == 0 && borders.length == 0) {
+ return gridpoint(-1,-1,(-infinity,-infinity));
+ }
+ if(edges.length > borders.length) {
+ return edges[edges.length-1].end();
+ } else {
+ return borders[borders.length-1].end();
+ }
+ }
+ bool closed() {
+ return same(this.start(),this.end());
+ }
+ void addedge(segment seg) {
+ edges.push(seg);
+ }
+ void addedge(gridpoint[] gp) {
+ segment seg=segment(gp);
+ edges.push(seg);
+ }
+ void addborder(segment seg) {
+ borders.push(seg);
+ }
+ void addborder(gridpoint[] gp) {
+ segment seg=segment(gp);
+ borders.push(seg);
+ }
+ void append(Segment S) {
+ edges.append(S.edges);
+ borders.append(S.borders);
+ }
+}
+
+private Segment[] Segment(segment[] s)
+{
+ return sequence(new Segment(int i) {return Segment(s[i]);},s.length);
+}
+
+private Segment[][] Segment(segment[][] s)
+{
+ Segment[][] S=new Segment[s.length][];
+ for(int i=0; i < s.length; ++i)
+ S[i]=Segment(s[i]);
+ return S;
+}
+
+// Return contour points for a 2D data array.
+// f: two-dimensional array of corresponding f(z) data values
+// c: array of contour values
+// subsample: number of points to use in each box in addition to endpoints
+segment[][] contouredges(real[][] f, real[] c, int subsample=1)
+{
+ int nx=f.length-1;
+ if(nx <= 0)
+ abort("array f must have length >= 2");
+ int ny=f[0].length-1;
+ if(ny <= 0)
+ abort("array f[0] must have length >= 2");
+
+ c=sort(c);
+ boxdata[][] bd=new boxdata[nx][ny];
+
+ segment[][] result=new segment[c.length][];
+
+ for(int i=0; i < nx; ++i) {
+ boxdata[] bdi=bd[i];
+ real[] fi=f[i];
+ real[] fp=f[i+1];
+
+ for(int j=0; j < ny; ++j) {
+ boxdata bdij=bdi[j]=boxdata(fi[j],fp[j],fi[j+1],fp[j+1]);
+
+ int checkcell(int cnt) {
+ real C=c[cnt];
+
+ real f00=fi[j];
+ real f10=fp[j];
+ real f01=fi[j+1];
+ real f11=fp[j+1];
+
+ real epsf=eps*max(abs(f00),abs(f10),abs(f01),abs(f11),abs(C));
+
+ f00=f00-C;
+ f10=f10-C;
+ f01=f01-C;
+ f11=f11-C;
+
+ if(abs(f00) < epsf) f00=0;
+ if(abs(f10) < epsf) f10=0;
+ if(abs(f01) < epsf) f01=0;
+ if(abs(f11) < epsf) f11=0;
+
+
+ int countm=0;
+ int countz=0;
+ int countp=0;
+
+ void check(real vertdat) {
+ if(vertdat < -eps)++countm;
+ else {
+ if(vertdat <= eps)++countz;
+ else++countp;
+ }
+ }
+
+ check(f00);
+ check(f10);
+ check(f01);
+ check(f11);
+
+ if(countm == 4) return 1; // nothing to do
+ if(countp == 4) return -1; // nothing to do
+ if((countm == 3 || countp == 3) && countz == 1) return 0;
+
+ // Calculate individual box contours
+ bdij.settype(C);
+ if(bdij.type == edge)
+ setcontour(f00,f10,f01,f11,epsf,bdij,i,j,cnt);
+ return 0;
+ }
+
+ void process(int l, int u) {
+ if(l >= u) return;
+ int i=quotient(l+u,2);
+ int sign=checkcell(i);
+ if(sign == -1) process(i+1,u);
+ else if(sign == 1) process(l,i);
+ else {
+ process(l,i);
+ process(i+1,u);
+ }
+ }
+
+ process(0,c.length);
+ }
+ }
+
+ // Find contours and follow them
+ for(int i=0; i < nx; ++i) {
+ boxdata[] bdi=bd[i];
+ for(int j=0; j < ny; ++j) {
+ boxdata bd0=bdi[j];
+ if(bd0.count() == 0) continue;
+ for(int k=0; k < bd0.count(); ++k) {
+ boxcontour bc0=bd0.data[k];
+
+ if(!bc0.active) continue;
+
+ // Note: boxcontour set inactive when added to segment
+ segment seg=segment(bc0,subsample);
+
+ // Forward direction
+ bool foundnext=true;
+ while(foundnext) {
+ foundnext=false;
+ searchindex(bc0,true,new void(int i, int j) {
+ if((i >= 0) && (i < nx) && (j >= 0) && (j < ny)) {
+ boxcontour[] data=bd[i][j].data;
+ int k0=connectedindex(bc0,data);
+ if(k0 >= 0) {
+ bc0=data[k0];
+ seg.append(bc0,subsample);
+ foundnext=true;
+ }
+ }
+ });
+ }
+
+ // Backward direction
+ bc0=bd0.data[k];
+ bool foundprev=true;
+ while(foundprev) {
+ foundprev=false;
+ searchindex(bc0,false,new void(int i, int j) {
+ if((i >= 0) && (i < nx) && (j >= 0) && (j < ny)) {
+ boxcontour[] data=bd[i][j].data;
+ int k0=connectedindex(data,bc0);
+ if(k0 >= 0) {
+ bc0=data[k0];
+ seg.prepend(bc0,subsample);
+ foundprev=true;
+ }
+ }
+ });
+ }
+
+ result[bc0.index].push(seg);
+ }
+ }
+ }
+
+ // Note: every segment here _should_ be cyclic or terminate on the
+ // boundary
+ return result;
+}
+
+// Connect contours into guides.
+// Same initial/final points indicates a closed path.
+// Borders are always joined using--.
+private guide connect(Segment S, pair[][] z, interpolate join)
+{
+ pair loc(gridpoint gp) {
+ pair offset=z[gp.i][gp.j];
+ pair size=z[gp.i+1][gp.j+1]-z[gp.i][gp.j];
+ return offset+(size.x*gp.z.x,size.y*gp.z.y);
+ }
+ pair[] loc(gridpoint[] gp) {
+ pair[] result=new pair[gp.length];
+ for(int i; i < gp.length; ++i) {
+ result[i]=loc(gp[i]);
+ }
+ return result;
+ }
+
+ bool closed=S.closed();
+
+ pair[][] edges=new pair[S.edges.length][];
+ for(int i; i < S.edges.length; ++i) {
+ edges[i]=loc(S.edges[i].data);
+ }
+ pair[][] borders=new pair[S.borders.length][];
+ for(int i; i < S.borders.length; ++i) {
+ borders[i]=loc(S.borders[i].data);
+ }
+
+ if(edges.length == 0 && borders.length == 1) {
+ guide g=operator--(...borders[0]);
+ if(closed) g=g--cycle;
+ return g;
+ }
+
+ if(edges.length == 1 && borders.length == 0) {
+ pair[] pts=edges[0];
+ if(closed) pts.delete(pts.length-1);
+ guide g=join(...pts);
+ if(closed) g=join(g,cycle);
+ return g;
+ }
+
+ guide[] ge=new guide[edges.length];
+ for(int i=0; i < ge.length; ++i)
+ ge[i]=join(...edges[i]);
+
+ guide[] gb=new guide[borders.length];
+ for(int i=0; i < gb.length; ++i)
+ gb[i]=operator--(...borders[i]);
+
+ guide g=ge[0];
+ if(0 < gb.length) g=g&gb[0];
+ for(int i=1; i < ge.length; ++i) {
+ g=g&ge[i];
+ if(i < gb.length) g=g&gb[i];
+ }
+ if(closed) g=g&cycle;
+ return g;
+}
+
+// Connect contours into guides.
+private guide[] connect(Segment[] S, pair[][] z, interpolate join)
+{
+ return sequence(new guide(int i) {return connect(S[i],z,join);},S.length);
+}
+
+// Connect contours into guides.
+private guide[][] connect(Segment[][] S, pair[][] z, interpolate join)
+{
+ guide[][] result=new guide[S.length][];
+ for(int i=0; i < S.length; ++i) {
+ result[i]=connect(S[i],z,join);
+ }
+ return result;
+}
+
+// Return contour guides for a 2D data array.
+// z: two-dimensional array of nonoverlapping mesh points
+// f: two-dimensional array of corresponding f(z) data values
+// c: array of contour values
+// join: interpolation operator (e.g. operator--or operator ..)
+// subsample: number of interior points to include in each grid square
+// (in addition to points on edge)
+guide[][] contour(pair[][] z, real[][] f, real[] c,
+ interpolate join=operator--, int subsample=1)
+{
+ segment[][] seg=contouredges(f,c,subsample);
+ Segment[][] Seg=Segment(seg);
+ return connect(Seg,z,join);
+}
+
+// Return contour guides for a 2D data array on a uniform lattice
+// f: two-dimensional array of real data values
+// a,b: diagonally opposite vertices of rectangular domain
+// c: array of contour values
+// join: interpolation operator (e.g. operator--or operator ..)
+// subsample: number of interior points to include in each grid square
+// (in addition to points on edge)
+guide[][] contour(real[][] f, pair a, pair b, real[] c,
+ interpolate join=operator--, int subsample=1)
+{
+ int nx=f.length-1;
+ if(nx == 0)
+ abort("array f must have length >= 2");
+ int ny=f[0].length-1;
+ if(ny == 0)
+ abort("array f[0] must have length >= 2");
+
+ pair[][] z=new pair[nx+1][ny+1];
+ for(int i=0; i <= nx; ++i) {
+ pair[] zi=z[i];
+ real xi=interp(a.x,b.x,i/nx);
+ for(int j=0; j <= ny; ++j) {
+ zi[j]=(xi,interp(a.y,b.y,j/ny));
+ }
+ }
+ return contour(z,f,c,join,subsample);
+}
+
+// return contour guides for a real-valued function
+// f: real-valued function of two real variables
+// a,b: diagonally opposite vertices of rectangular domain
+// c: array of contour values
+// nx,ny: number of subdivisions in x and y directions(determines accuracy)
+// join: interpolation operator (e.g. operator--or operator ..)
+// subsample: number of interior points to include in each grid square
+// (in addition to points on edge)
+guide[][] contour(real f(real, real), pair a, pair b,
+ real[] c, int nx=ngraph, int ny=nx,
+ interpolate join=operator--, int subsample=1)
+{
+ // evaluate function at points and subsample
+ real[][] dat=new real[nx+1][ny+1];
+
+ for(int i=0; i <= nx; ++i) {
+ real x=interp(a.x,b.x,i/nx);
+ real[] dati=dat[i];
+ for(int j=0; j <= ny; ++j) {
+ dati[j]=f(x,interp(a.y,b.y,j/ny));
+ }
+ }
+
+ return contour(dat,a,b,c,join,subsample);
+}
+
+guide[][] contour(real f(pair), pair a, pair b,
+ real[] c, int nx=ngraph, int ny=nx,
+ interpolate join=operator--, int subsample=1)
+{
+ return contour(new real(real x, real y) {return f((x,y));},
+ a,b,c,nx,ny,join,subsample);
+}
+
+void draw(picture pic=currentpicture, Label[] L=new Label[],
+ guide[][] g, pen[] p)
+{
+ begingroup(pic);
+ for(int cnt=0; cnt < g.length; ++cnt) {
+ guide[] gcnt=g[cnt];
+ pen pcnt=p[cnt];
+ for(int i=0; i < gcnt.length; ++i)
+ draw(pic,gcnt[i],pcnt);
+ if(L.length > 0) {
+ Label Lcnt=L[cnt];
+ for(int i=0; i < gcnt.length; ++i) {
+ if(Lcnt.s != "" && size(gcnt[i]) > 1)
+ label(pic,Lcnt,gcnt[i],pcnt);
+ }
+ }
+ }
+ endgroup(pic);
+}
+
+void draw(picture pic=currentpicture, Label[] L=new Label[],
+ guide[][] g, pen p=currentpen)
+{
+ draw(pic,L,g,sequence(new pen(int) {return p;},g.length));
+}
+
+// Draw the contour
+void draw(picture pic=currentpicture, Label L,
+ guide[] g, pen p=currentpen)
+{
+ draw(pic,g,p);
+ for(int i=0; i < g.length; ++i) {
+ if(L.s != "" && size(g[i]) > 1) {
+ label(pic,L,g[i],p);
+ }
+ }
+}
+
+/* CONTOURS FOR IRREGULARLY SPACED POINTS */
+//
+// +---------+
+// |\ /|
+// | \ / |
+// | \ / |
+// | \ / |
+// | X |
+// | / \ |
+// | / \ |
+// | / \ |
+// |/ \|
+// +---------+
+//
+
+// Is triangle p0--p1--p2--cycle counterclockwise ?
+private bool isCCW(pair p0, pair p1, pair p2) {return side(p0,p1,p2) < 0;}
+
+private struct segment
+{
+ bool active;
+ bool reversed; // True if lower values are to the left along line a--b.
+ pair a,b; // Endpoints; a is always an edge point if one exists.
+ int c; // Contour value.
+}
+
+// Case 1: line passes through two vertices of a triangle
+private segment case1(pair p0, pair p1, pair p2,
+ real v0, real v1, real v2)
+{
+ // Will cause a duplicate guide; luckily case1 is rare
+ segment rtrn;
+ rtrn.active=true;
+ rtrn.a=p0;
+ rtrn.b=p1;
+ rtrn.reversed=(isCCW(p0,p1,p2) ^(v2 > 0));
+ return rtrn;
+}
+
+// Cases 2 and 3: line passes through a vertex and a side of a triangle
+//(the first vertex passed and the side between the other two)
+private segment case2(pair p0, pair p1, pair p2,
+ real v0, real v1, real v2)
+{
+ segment rtrn;
+ rtrn.active=true;
+ pair val=interp(p1,p2,abs(v1/(v2-v1)));
+ rtrn.a=val;
+ rtrn.b=p0;
+ rtrn.reversed=!(isCCW(p0,p1,p2) ^(v2 > 0));
+ return rtrn;
+}
+
+private segment case3(pair p0, pair p1, pair p2,
+ real v0, real v1, real v2)
+{
+ segment rtrn;
+ rtrn.active=true;
+ pair val=interp(p1,p2,abs(v1/(v2-v1)));
+ rtrn.a=p0;
+ rtrn.b=val;
+ rtrn.reversed=(isCCW(p0,p1,p2) ^(v2 > 0));
+ return rtrn;
+}
+
+// Case 4: line passes through two sides of a triangle
+//(through the sides formed by the first&second, and second&third vertices)
+private segment case4(pair p0, pair p1, pair p2,
+ real v0, real v1, real v2)
+{
+ segment rtrn;
+ rtrn.active=true;
+ rtrn.a=interp(p1,p0,abs(v1/(v0-v1)));
+ rtrn.b=interp(p1,p2,abs(v1/(v2-v1)));
+ rtrn.reversed=(isCCW(p0,p1,p2) ^(v2 > 0));
+ return rtrn;
+}
+
+// Check if a line passes through a triangle, and draw the required line.
+private segment checktriangle(pair p0, pair p1, pair p2,
+ real v0, real v1, real v2)
+{
+ // default null return
+ static segment dflt;
+
+ real eps=eps*max(abs(v0),abs(v1),abs(v2),1);
+
+ if(v0 < -eps) {
+ if(v1 < -eps) {
+ if(v2 < -eps) return dflt; // nothing to do
+ else if(v2 <= eps) return dflt; // nothing to do
+ else return case4(p0,p2,p1,v0,v2,v1);
+ } else if(v1 <= eps) {
+ if(v2 < -eps) return dflt; // nothing to do
+ else if(v2 <= eps) return case1(p1,p2,p0,v1,v2,v0);
+ else return case3(p1,p0,p2,v1,v0,v2);
+ } else {
+ if(v2 < -eps) return case4(p0,p1,p2,v0,v1,v2);
+ else if(v2 <= eps)
+ return case2(p2,p0,p1,v2,v0,v1);
+ else return case4(p1,p0,p2,v1,v0,v2);
+ }
+ } else if(v0 <= eps) {
+ if(v1 < -eps) {
+ if(v2 < -eps) return dflt; // nothing to do
+ else if(v2 <= eps) return case1(p0,p2,p1,v0,v2,v1);
+ else return case2(p0,p1,p2,v0,v1,v2);
+ } else if(v1 <= eps) {
+ if(v2 < -eps) return case1(p0,p1,p2,v0,v1,v2);
+ else if(v2 <= eps) return dflt; // use finer partitioning.
+ else return case1(p0,p1,p2,v0,v1,v2);
+ } else {
+ if(v2 < -eps) return case2(p0,p1,p2,v0,v1,v2);
+ else if(v2 <= eps) return case1(p0,p2,p1,v0,v2,v1);
+ else return dflt; // nothing to do
+ }
+ } else {
+ if(v1 < -eps) {
+ if(v2 < -eps) return case4(p1,p0,p2,v1,v0,v2);
+ else if(v2 <= eps)
+ return case2(p2,p0,p1,v2,v0,v1);
+ else return case4(p0,p1,p2,v0,v1,v2);
+ } else if(v1 <= eps) {
+ if(v2 < -eps) return case3(p1,p0,p2,v1,v0,v2);
+ else if(v2 <= eps) return case1(p1,p2,p0,v1,v2,v0);
+ else return dflt; // nothing to do
+ } else {
+ if(v2 < -eps) return case4(p0,p2,p1,v0,v2,v1);
+ else if(v2 <= eps) return dflt; // nothing to do
+ else return dflt; // nothing to do
+ }
+ }
+}
+
+// Collect connecting path segments.
+private void collect(pair[][][] points, real[] c)
+{
+ for(int cnt=0; cnt < c.length; ++cnt) {
+ pair[][] gdscnt=points[cnt];
+ for(int i=0; i < gdscnt.length; ++i) {
+ pair[] gig=gdscnt[i];
+ int Li=gig.length;
+ for(int j=i+1; j < gdscnt.length; ++j) {
+ pair[] gjg=gdscnt[j];
+ int Lj=gjg.length;
+ if(abs(gig[0]-gjg[Lj-1]) < eps) {
+ gig.delete(0);
+ gdscnt[j].append(gig);
+ gdscnt.delete(i);
+ --i;
+ break;
+ } else if(abs(gig[Li-1]-gjg[0]) < eps) {
+ gjg.delete(0);
+ gig.append(gjg);
+ gdscnt[j]=gig;
+ gdscnt.delete(i);
+ --i;
+ break;
+ }
+ }
+ }
+ }
+}
+
+// Join path segments.
+private guide[][] connect(pair[][][] points, real[] c, interpolate join)
+{
+ // set up return value
+ guide[][] result=new guide[c.length][];
+ for(int cnt=0; cnt < c.length; ++cnt) {
+ pair[][] pointscnt=points[cnt];
+ guide[] resultcnt=result[cnt]=new guide[pointscnt.length];
+ for(int i=0; i < pointscnt.length; ++i) {
+ pair[] pts=pointscnt[i];
+ guide gd;
+ if(pts.length > 0) {
+ if(pts.length > 1 && abs(pts[0]-pts[pts.length-1]) < eps) {
+ guide[] g=sequence(new guide(int i) {
+ return pts[i];
+ },pts.length-1);
+ g.push(cycle);
+ gd=join(...g);
+ } else
+ gd=join(...sequence(new guide(int i) {
+ return pts[i];
+ },pts.length));
+ }
+ resultcnt[i]=gd;
+ }
+ }
+ return result;
+}
+
+guide[][] contour(pair[] z, real[] f, real[] c, interpolate join=operator--)
+{
+ if(z.length != f.length)
+ abort("z and f arrays have different lengths");
+
+ int[][] trn=triangulate(z);
+
+ // array to store guides found so far
+ pair[][][] points=new pair[c.length][][];
+
+ for(int cnt=0; cnt < c.length; ++cnt) {
+ pair[][] pointscnt=points[cnt];
+ real C=c[cnt];
+ for(int i=0; i < trn.length; ++i) {
+ int[] trni=trn[i];
+ int i0=trni[0], i1=trni[1], i2=trni[2];
+ segment seg=checktriangle(z[i0],z[i1],z[i2],f[i0]-C,f[i1]-C,f[i2]-C);
+ if(seg.active)
+ pointscnt.push(seg.reversed ? new pair[] {seg.b,seg.a} :
+ new pair[] {seg.a,seg.b});
+ }
+ }
+
+ collect(points,c);
+
+ return connect(points,c,join);
+}
+
+// Extend palette by the colors below and above at each end.
+pen[] extend(pen[] palette, pen below, pen above) {
+ pen[] p=copy(palette);
+ p.insert(0,below);
+ p.push(above);
+ return p;
+}
+
+// Compute the interior palette for a sequence of cyclic contours
+// corresponding to palette.
+pen[][] interior(picture pic=currentpicture, guide[][] g, pen[] palette)
+{
+ if(palette.length != g.length+1)
+ abort("Palette array must have length one more than guide array");
+ pen[][] fillpalette=new pen[g.length][];
+ for(int i=0; i < g.length; ++i) {
+ guide[] gi=g[i];
+ guide[] gp;
+ if(i+1 < g.length) gp=g[i+1];
+ guide[] gm;
+ if(i > 0) gm=g[i-1];
+
+ pen[] fillpalettei=new pen[gi.length];
+ for(int j=0; j < gi.length; ++j) {
+ path P=gi[j];
+ if(cyclic(P)) {
+ int index=i+1;
+ bool nextinside;
+ for(int k=0; k < gp.length; ++k) {
+ path next=gp[k];
+ if(cyclic(next)) {
+ if(inside(P,point(next,0)))
+ nextinside=true;
+ else if(inside(next,point(P,0)))
+ index=i;
+ }
+ }
+ if(!nextinside) {
+ // Check to see if previous contour is inside
+ for(int k=0; k < gm.length; ++k) {
+ path prev=gm[k];
+ if(cyclic(prev)) {
+ if(inside(P,point(prev,0)))
+ index=i;
+ }
+ }
+ }
+ fillpalettei[j]=palette[index];
+ }
+ fillpalette[i]=fillpalettei;
+ }
+ }
+ return fillpalette;
+}
+
+// Fill the interior of cyclic contours with palette
+void fill(picture pic=currentpicture, guide[][] g, pen[][] palette)
+{
+ for(int i=0; i < g.length; ++i) {
+ guide[] gi=g[i];
+ guide[] gp;
+ if(i+1 < g.length) gp=g[i+1];
+ guide[] gm;
+ if(i > 0) gm=g[i-1];
+
+ for(int j=0; j < gi.length; ++j) {
+ path P=gi[j];
+ path[] S=P;
+ if(cyclic(P)) {
+ for(int k=0; k < gp.length; ++k) {
+ path next=gp[k];
+ if(cyclic(next) && inside(P,point(next,0)))
+ S=S^^next;
+ }
+ for(int k=0; k < gm.length; ++k) {
+ path next=gm[k];
+ if(cyclic(next) && inside(P,point(next,0)))
+ S=S^^next;
+ }
+ fill(pic,S,palette[i][j]+evenodd);
+ }
+ }
+ }
+}
diff --git a/Master/texmf-dist/asymptote/contour3.asy b/Master/texmf-dist/asymptote/contour3.asy
new file mode 100644
index 00000000000..3d925be684e
--- /dev/null
+++ b/Master/texmf-dist/asymptote/contour3.asy
@@ -0,0 +1,481 @@
+import graph_settings;
+import three;
+
+real eps=10000*realEpsilon;
+
+private struct weighted
+{
+ triple normal;
+ real ratio;
+ int kpa0,kpa1,kpa2;
+ int kpb0,kpb1,kpb2;
+ triple v;
+}
+
+private struct bucket
+{
+ triple v;
+ triple val;
+ int count;
+}
+
+struct vertex
+{
+ triple v;
+ triple normal;
+}
+
+// A group of 3 or 4 points.
+private struct object
+{
+ bool active;
+ weighted[] pts;
+}
+
+// Return contour vertices for a 3D data array.
+// z: three-dimensional array of nonoverlapping mesh points
+// f: three-dimensional arrays of real data values
+// midpoint: optional array containing estimate of f at midpoint values
+vertex[][] contour3(triple[][][] v, real[][][] f,
+ real[][][] midpoint=new real[][][],
+ projection P=currentprojection)
+{
+ int nx=v.length-1;
+ if(nx == 0)
+ abort("array v must have length >= 2");
+ int ny=v[0].length-1;
+ if(ny == 0)
+ abort("array v[0] must have length >= 2");
+ int nz=v[0][0].length-1;
+ if(nz == 0)
+ abort("array v[0][0] must have length >= 2");
+
+ bool midpoints=midpoint.length > 0;
+
+ bucket[][][][] kps=new bucket[2nx+1][2ny+1][2nz+1][];
+ for(int i=0; i < 2nx+1; ++i)
+ for(int j=0; j < 2ny+1; ++j)
+ for(int k=0; k < 2nz+1; ++k)
+ kps[i][j][k]=new bucket[];
+
+ object[] objects;
+
+ // go over region a rectangle at a time
+ for(int i=0; i < nx; ++i) {
+ triple[][] vi=v[i];
+ triple[][] vp=v[i+1];
+ real[][] fi=f[i];
+ real[][] fp=f[i+1];
+ int i2=2i;
+ int i2p1=i2+1;
+ int i2p2=i2+2;
+ for(int j=0; j < ny; ++j) {
+ triple[] vij=vi[j];
+ triple[] vpj=vp[j];
+ triple[] vip=vi[j+1];
+ triple[] vpp=vp[j+1];
+ real[] fij=fi[j];
+ real[] fpj=fp[j];
+ real[] fip=fi[j+1];
+ real[] fpp=fp[j+1];
+ int j2=2j;
+ int j2p1=j2+1;
+ int j2p2=j2+2;
+
+ for(int k=0; k < nz; ++k) {
+ // vertex values
+ real vdat0=fij[k];
+ real vdat1=fij[k+1];
+ real vdat2=fip[k];
+ real vdat3=fip[k+1];
+ real vdat4=fpj[k];
+ real vdat5=fpj[k+1];
+ real vdat6=fpp[k];
+ real vdat7=fpp[k+1];
+
+ // define points
+ triple p000=vij[k];
+ triple p001=vij[k+1];
+ triple p010=vip[k];
+ triple p011=vip[k+1];
+ triple p100=vpj[k];
+ triple p101=vpj[k+1];
+ triple p110=vpp[k];
+ triple p111=vpp[k+1];
+ triple m0=0.25*(p000+p010+p110+p100);
+ triple m1=0.25*(p010+p110+p111+p011);
+ triple m2=0.25*(p110+p100+p101+p111);
+ triple m3=0.25*(p100+p000+p001+p101);
+ triple m4=0.25*(p000+p010+p011+p001);
+ triple m5=0.25*(p001+p011+p111+p101);
+ triple mc=0.5*(m0+m5);
+
+ // optimization: we make sure we don't work with empty rectangles
+ int countm=0;
+ int countz=0;
+ int countp=0;
+
+ void check(real vdat) {
+ if(vdat < -eps) ++countm;
+ else {
+ if(vdat <= eps) ++countz;
+ else ++countp;
+ }
+ }
+
+ check(vdat0);
+ check(vdat1);
+ check(vdat2);
+ check(vdat3);
+ check(vdat4);
+ check(vdat5);
+ check(vdat6);
+ check(vdat7);
+
+ if(countm == 8 || countp == 8 ||
+ ((countm == 7 || countp == 7) && countz == 1)) continue;
+
+ int k2=2k;
+ int k2p1=k2+1;
+ int k2p2=k2+2;
+
+ // Evaluate midpoints of cube sides.
+ // Then evaluate midpoint of cube.
+ real vdat8=midpoints ? midpoint[i2p1][j2p1][k2] :
+ 0.25*(vdat0+vdat2+vdat6+vdat4);
+ real vdat9=midpoints ? midpoint[i2p1][j2p2][k2p1] :
+ 0.25*(vdat2+vdat6+vdat7+vdat3);
+ real vdat10=midpoints ? midpoint[i2p2][j2p1][k2p1] :
+ 0.25*(vdat7+vdat6+vdat4+vdat5);
+ real vdat11=midpoints ? midpoint[i2p1][j2][k2p1] :
+ 0.25*(vdat0+vdat4+vdat5+vdat1);
+ real vdat12=midpoints ? midpoint[i2][j2p1][k2p1] :
+ 0.25*(vdat0+vdat2+vdat3+vdat1);
+ real vdat13=midpoints ? midpoint[i2p1][j2p1][k2p2] :
+ 0.25*(vdat1+vdat3+vdat7+vdat5);
+ real vdat14=midpoints ? midpoint[i2p1][j2p1][k2p1] :
+ 0.125*(vdat0+vdat1+vdat2+vdat3+vdat4+vdat5+vdat6+vdat7);
+
+ // Go through the 24 pyramids, 4 for each side.
+
+ void addval(int kp0, int kp1, int kp2, triple add, triple v) {
+ bucket[] cur=kps[kp0][kp1][kp2];
+ for(int q=0; q < cur.length; ++q) {
+ if(length(cur[q].v-v) < eps) {
+ cur[q].val += add;
+ ++cur[q].count;
+ return;
+ }
+ }
+ bucket newbuck;
+ newbuck.v=v;
+ newbuck.val=add;
+ newbuck.count=1;
+ cur.push(newbuck);
+ }
+
+ void accrue(weighted w) {
+ triple val1=w.normal*w.ratio;
+ triple val2=w.normal*(1-w.ratio);
+ addval(w.kpa0,w.kpa1,w.kpa2,val1,w.v);
+ addval(w.kpb0,w.kpb1,w.kpb2,val2,w.v);
+ }
+
+ triple dir=P.normal;
+
+ void addnormals(weighted[] pts) {
+ triple vec2=pts[1].v-pts[0].v;
+ triple vec1=pts[0].v-pts[2].v;
+ triple vec0=-vec2-vec1;
+ vec2=unit(vec2);
+ vec1=unit(vec1);
+ vec0=unit(vec0);
+ triple normal=cross(vec2,vec1);
+ normal *= sgn(dot(normal,dir));
+ real angle0=acos(-dot(vec1,vec2));
+ real angle1=acos(-dot(vec2,vec0));
+ pts[0].normal=normal*angle0;
+ pts[1].normal=normal*angle1;
+ pts[2].normal=normal*(pi-angle0-angle1);
+ }
+
+ void addobj(object obj) {
+ if(!obj.active) return;
+
+ if(obj.pts.length == 4) {
+ weighted[] points=obj.pts;
+ object obj1;
+ object obj2;
+ obj1.active=true;
+ obj2.active=true;
+ obj1.pts=new weighted[] {points[0],points[1],points[2]};
+ obj2.pts=new weighted[] {points[1],points[2],points[3]};
+ addobj(obj1);
+ addobj(obj2);
+ } else {
+ addnormals(obj.pts);
+ for(int q=0; q < obj.pts.length; ++q)
+ accrue(obj.pts[q]);
+ objects.push(obj);
+ }
+ }
+
+ weighted setupweighted(triple va, triple vb, real da, real db,
+ int[] kpa, int[] kpb) {
+ weighted w;
+ real ratio=abs(da/(db-da));
+ w.v=interp(va,vb,ratio);
+ w.ratio=ratio;
+ w.kpa0=i2+kpa[0];
+ w.kpa1=j2+kpa[1];
+ w.kpa2=k2+kpa[2];
+ w.kpb0=i2+kpb[0];
+ w.kpb1=j2+kpb[1];
+ w.kpb2=k2+kpb[2];
+
+ return w;
+ }
+
+ weighted setupweighted(triple v, int[] kp) {
+ weighted w;
+ w.v=v;
+ w.ratio=0.5;
+ w.kpa0=w.kpb0=i2+kp[0];
+ w.kpa1=w.kpb1=j2+kp[1];
+ w.kpa2=w.kpb2=k2+kp[2];
+
+ return w;
+ }
+
+ // Checks if a pyramid contains a contour object.
+ object checkpyr(triple v0, triple v1, triple v2, triple v3,
+ real d0, real d1, real d2, real d3,
+ int[] c0, int[] c1, int[] c2, int[] c3) {
+ object obj;
+ real a0=abs(d0);
+ real a1=abs(d1);
+ real a2=abs(d2);
+ real a3=abs(d3);
+
+ bool b0=a0 < eps;
+ bool b1=a1 < eps;
+ bool b2=a2 < eps;
+ bool b3=a3 < eps;
+
+ weighted[] pts;
+
+ if(b0) pts.push(setupweighted(v0,c0));
+ if(b1) pts.push(setupweighted(v1,c1));
+ if(b2) pts.push(setupweighted(v2,c2));
+ if(b3) pts.push(setupweighted(v3,c3));
+
+ if(!b0 && !b1 && abs(d0+d1)+eps < a0+a1)
+ pts.push(setupweighted(v0,v1,d0,d1,c0,c1));
+ if(!b0 && !b2 && abs(d0+d2)+eps < a0+a2)
+ pts.push(setupweighted(v0,v2,d0,d2,c0,c2));
+ if(!b0 && !b3 && abs(d0+d3)+eps < a0+a3)
+ pts.push(setupweighted(v0,v3,d0,d3,c0,c3));
+ if(!b1 && !b2 && abs(d1+d2)+eps < a1+a2)
+ pts.push(setupweighted(v1,v2,d1,d2,c1,c2));
+ if(!b1 && !b3 && abs(d1+d3)+eps < a1+a3)
+ pts.push(setupweighted(v1,v3,d1,d3,c1,c3));
+ if(!b2 && !b3 && abs(d2+d3)+eps < a2+a3)
+ pts.push(setupweighted(v2,v3,d2,d3,c2,c3));
+
+ int s=pts.length;
+ //There are three or four points.
+ if(s > 2) {
+ obj.active=true;
+ obj.pts=pts;
+ } else obj.active=false;
+
+ return obj;
+ }
+
+ void check4pyr(triple v0, triple v1, triple v2, triple v3,
+ triple v4, triple v5,
+ real d0, real d1, real d2, real d3, real d4, real d5,
+ int[] c0, int[] c1, int[] c2, int[] c3, int[] c4,
+ int[] c5) {
+ addobj(checkpyr(v5,v4,v0,v1,d5,d4,d0,d1,c5,c4,c0,c1));
+ addobj(checkpyr(v5,v4,v1,v2,d5,d4,d1,d2,c5,c4,c1,c2));
+ addobj(checkpyr(v5,v4,v2,v3,d5,d4,d2,d3,c5,c4,c2,c3));
+ addobj(checkpyr(v5,v4,v3,v0,d5,d4,d3,d0,c5,c4,c3,c0));
+ }
+
+ static int[] pp000={0,0,0};
+ static int[] pp001={0,0,2};
+ static int[] pp010={0,2,0};
+ static int[] pp011={0,2,2};
+ static int[] pp100={2,0,0};
+ static int[] pp101={2,0,2};
+ static int[] pp110={2,2,0};
+ static int[] pp111={2,2,2};
+ static int[] pm0={1,1,0};
+ static int[] pm1={1,2,1};
+ static int[] pm2={2,1,1};
+ static int[] pm3={1,0,1};
+ static int[] pm4={0,1,1};
+ static int[] pm5={1,1,2};
+ static int[] pmc={1,1,1};
+
+ check4pyr(p000,p010,p110,p100,mc,m0,
+ vdat0,vdat2,vdat6,vdat4,vdat14,vdat8,
+ pp000,pp010,pp110,pp100,pmc,pm0);
+ check4pyr(p010,p110,p111,p011,mc,m1,
+ vdat2,vdat6,vdat7,vdat3,vdat14,vdat9,
+ pp010,pp110,pp111,pp011,pmc,pm1);
+ check4pyr(p110,p100,p101,p111,mc,m2,
+ vdat6,vdat4,vdat5,vdat7,vdat14,vdat10,
+ pp110,pp100,pp101,pp111,pmc,pm2);
+ check4pyr(p100,p000,p001,p101,mc,m3,
+ vdat4,vdat0,vdat1,vdat5,vdat14,vdat11,
+ pp100,pp000,pp001,pp101,pmc,pm3);
+ check4pyr(p000,p010,p011,p001,mc,m4,
+ vdat0,vdat2,vdat3,vdat1,vdat14,vdat12,
+ pp000,pp010,pp011,pp001,pmc,pm4);
+ check4pyr(p001,p011,p111,p101,mc,m5,
+ vdat1,vdat3,vdat7,vdat5,vdat14,vdat13,
+ pp001,pp011,pp111,pp101,pmc,pm5);
+ }
+ }
+ }
+
+ vertex preparevertex(weighted w) {
+ vertex ret;
+ triple normal=O;
+ bool first=true;
+ bucket[] kp1=kps[w.kpa0][w.kpa1][w.kpa2];
+ bucket[] kp2=kps[w.kpb0][w.kpb1][w.kpb2];
+ bool notfound1=true;
+ bool notfound2=true;
+ int count=0;
+ int stop=max(kp1.length,kp2.length);
+ for(int r=0; r < stop; ++r) {
+ if(notfound1) {
+ if(length(w.v-kp1[r].v) < eps) {
+ if(first) {
+ ret.v=kp1[r].v;
+ first=false;
+ }
+ normal += kp1[r].val;
+ count += kp1[r].count;
+ notfound1=false;
+ }
+ }
+ if(notfound2) {
+ if(length(w.v-kp2[r].v) < eps) {
+ if(first) {
+ ret.v=kp2[r].v;
+ first=false;
+ }
+ normal += kp2[r].val;
+ count += kp2[r].count;
+ notfound2=false;
+ }
+ }
+ }
+ ret.normal=normal*2/count;
+ return ret;
+ }
+
+ // Prepare return value.
+ vertex[][] g;
+
+ for(int q=0; q < objects.length; ++q) {
+ object p=objects[q];
+ g.push(new vertex[] {preparevertex(p.pts[0]),preparevertex(p.pts[1]),
+ preparevertex(p.pts[2])});
+ }
+ return g;
+}
+
+// Return contour vertices for a 3D data array on a uniform lattice.
+// f: three-dimensional arrays of real data values
+// midpoint: optional array containing estimate of f at midpoint values
+// a,b: diagonally opposite points of rectangular parellelpiped domain
+vertex[][] contour3(real[][][] f, real[][][] midpoint=new real[][][],
+ triple a, triple b, projection P=currentprojection)
+
+{
+ int nx=f.length-1;
+ if(nx == 0)
+ abort("array f must have length >= 2");
+ int ny=f[0].length-1;
+ if(ny == 0)
+ abort("array f[0] must have length >= 2");
+ int nz=f[0][0].length-1;
+ if(nz == 0)
+ abort("array f[0][0] must have length >= 2");
+
+ triple[][][] v=new triple[nx+1][ny+1][nz+1];
+ for(int i=0; i <= nx; ++i) {
+ real xi=interp(a.x,b.x,i/nx);
+ triple[][] vi=v[i];
+ for(int j=0; j <= ny; ++j) {
+ triple[] vij=v[i][j];
+ real yj=interp(a.y,b.y,j/ny);
+ for(int k=0; k <= nz; ++k) {
+ vij[k]=(xi,yj,interp(a.z,b.z,k/nz));
+ }
+ }
+ }
+ return contour3(v,f,midpoint,P);
+}
+
+// Return contour vertices for a 3D data array, using a pyramid mesh
+// f: real-valued function of three real variables
+// a,b: diagonally opposite points of rectangular parellelpiped domain
+// nx,ny,nz number of subdivisions in x, y, and z directions
+vertex[][] contour3(real f(real, real, real), triple a, triple b,
+ int nx=nmesh, int ny=nx, int nz=nx,
+ projection P=currentprojection)
+{
+ // evaluate function at points and midpoints
+ real[][][] dat=new real[nx+1][ny+1][nz+1];
+ real[][][] midpoint=new real[2nx+2][2ny+2][2nz+1];
+
+ for(int i=0; i <= nx; ++i) {
+ real x=interp(a.x,b.x,i/nx);
+ real x2=interp(a.x,b.x,(i+0.5)/nx);
+ real[][] dati=dat[i];
+ real[][] midpointi2=midpoint[2i];
+ real[][] midpointi2p1=midpoint[2i+1];
+ for(int j=0; j <= ny; ++j) {
+ real y=interp(a.y,b.y,j/ny);
+ real y2=interp(a.y,b.y,(j+0.5)/ny);
+ real datij[]=dati[j];
+ real[] midpointi2p1j2=midpointi2p1[2j];
+ real[] midpointi2p1j2p1=midpointi2p1[2j+1];
+ real[] midpointi2j2p1=midpointi2[2j+1];
+ for(int k=0; k <= nz; ++k) {
+ real z=interp(a.z,b.z,k/nz);
+ real z2=interp(a.z,b.z,(k+0.5)/nz);
+ datij[k]=f(x,y,z);
+ if(i == nx || j == ny || k == nz) continue;
+ int k2p1=2k+1;
+ midpointi2p1j2p1[2k]=f(x2,y2,z);
+ midpointi2p1j2p1[k2p1]=f(x2,y2,z2);
+ midpointi2p1j2[k2p1]=f(x2,y,z2);
+ midpointi2j2p1[k2p1]=f(x,y2,z2);
+ if(i == 0) midpoint[2nx][2j+1][k2p1]=f(b.x,y2,z2);
+ if(j == 0) midpointi2p1[2ny][k2p1]=f(x2,b.y,z2);
+ if(k == 0) midpointi2p1j2p1[2nz]=f(x2,y2,b.z);
+ }
+ }
+ }
+ return contour3(dat,midpoint,a,b,P);
+}
+
+// Construct contour surface for a 3D data array, using a pyramid mesh.
+surface surface(vertex[][] g)
+{
+ surface s=surface(g.length);
+ for(int i=0; i < g.length; ++i) {
+ vertex[] cur=g[i];
+ s.s[i]=patch(new triple[] {cur[0].v,cur[0].v,cur[1].v,cur[2].v},
+ normals=new triple[] {cur[0].normal,cur[0].normal,
+ cur[1].normal,cur[2].normal});
+ }
+ return s;
+}
diff --git a/Master/texmf-dist/asymptote/drawtree.asy b/Master/texmf-dist/asymptote/drawtree.asy
new file mode 100644
index 00000000000..832dfc658a1
--- /dev/null
+++ b/Master/texmf-dist/asymptote/drawtree.asy
@@ -0,0 +1,101 @@
+// A simple tree drawing module contributed by adarovsky
+// See example treetest.asy
+
+real treeNodeStep = 0.5cm;
+real treeLevelStep = 1cm;
+real treeMinNodeWidth = 2cm;
+
+struct TreeNode {
+ TreeNode parent;
+ TreeNode[] children;
+
+ frame content;
+
+ pair pos;
+ real adjust;
+}
+
+void add( TreeNode child, TreeNode parent )
+{
+ child.parent = parent;
+ parent.children.push( child );
+}
+
+TreeNode makeNode( TreeNode parent = null, frame f )
+{
+ TreeNode child = new TreeNode;
+ child.content = f;
+ if( parent != null ) {
+ add( child, parent );
+ }
+ return child;
+}
+
+TreeNode makeNode( TreeNode parent = null, Label label )
+{
+ frame f;
+ box( f, label);
+ return makeNode( parent, f );
+}
+
+
+real layout( int level, TreeNode node )
+{
+ if( node.children.length > 0 ) {
+ real width[] = new real[node.children.length];
+ real curWidth = 0;
+
+ for( int i = 0; i < node.children.length; ++i ) {
+ width[i] = layout( level+1, node.children[i] );
+
+ node.children[i].pos = (curWidth + width[i]/2,
+ -level*treeLevelStep);
+ curWidth += width[i] + treeNodeStep;
+ }
+
+ real midPoint = ( sum( width )+treeNodeStep*(width.length-1)) / 2;
+ for( int i = 0; i < node.children.length; ++i ) {
+ node.children[i].adjust = - midPoint;
+ }
+
+ return max( (max(node.content)-min(node.content)).x,
+ sum(width)+treeNodeStep*(width.length-1) );
+ }
+ else {
+ return max( treeMinNodeWidth, (max(node.content)-min(node.content)).x );
+ }
+}
+
+void drawAll( TreeNode node, frame f )
+{
+ pair pos;
+ if( node.parent != null )
+ pos = (node.parent.pos.x+node.adjust, 0);
+ else
+ pos = (node.adjust, 0);
+ node.pos += pos;
+
+ node.content = shift(node.pos)*node.content;
+ add( f, node.content );
+
+
+ if( node.parent != null ) {
+ path p = point(node.content, N)--point(node.parent.content,S);
+ draw( f, p, currentpen );
+ }
+
+ for( int i = 0; i < node.children.length; ++i )
+ drawAll( node.children[i], f );
+}
+
+void draw( TreeNode root, pair pos )
+{
+ frame f;
+
+ root.pos = (0,0);
+ layout( 1, root );
+
+ drawAll( root, f );
+
+ add(f,pos);
+}
diff --git a/Master/texmf-dist/asymptote/embed.asy b/Master/texmf-dist/asymptote/embed.asy
new file mode 100644
index 00000000000..165de0b7534
--- /dev/null
+++ b/Master/texmf-dist/asymptote/embed.asy
@@ -0,0 +1,38 @@
+if(latex()) {
+ usepackage("hyperref");
+ texpreamble("\hypersetup{"+settings.hyperrefOptions+"}");
+ texpreamble("
+\ifx\pdfhorigin\undefined%
+\usepackage[3D,dvipdfmx]{movie15}
+\else%
+\usepackage[3D]{movie15}
+\fi%
+\FPmessagesfalse%
+");
+}
+
+// See http://www.ctan.org/tex-archive/macros/latex/contrib/movie15/README
+// for documentation of the options.
+
+// Embed object in pdf file
+string embed(string name, 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+"}";
+}
+
+string hyperlink(string url, string text)
+{
+ return "\href{"+url+"}{"+text+"}";
+}
+
+string link(string label, string text, string options="")
+{
+ // Run LaTeX twice to resolve references.
+ settings.twice=true;
+ if(options != "") options="["+options+"]";
+ return "\movieref"+options+"{"+label+"}{"+text+"}";
+}
diff --git a/Master/texmf-dist/asymptote/external.asy b/Master/texmf-dist/asymptote/external.asy
new file mode 100644
index 00000000000..053f1eff4c1
--- /dev/null
+++ b/Master/texmf-dist/asymptote/external.asy
@@ -0,0 +1,37 @@
+usepackage("hyperref");
+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 options; // Ignore movie15 options.
+ if(image == "") {
+ image=stripdirectory(stripextension(name))+"."+nativeformat();
+ convert(name+"[0]",image,nativeformat());
+
+ if(!settings.keep) {
+ exitfcn currentexitfunction=atexit();
+ void exitfunction() {
+ if(currentexitfunction != null) currentexitfunction();
+ delete(image);
+ }
+ atexit(exitfunction);
+ }
+ }
+ if(width != 0) options += ", width="+(string) (width/pt)+"pt";
+ if(height != 0) options +=", height="+(string) (height/pt)+"pt";
+ return "\href{run:"+name+"}{"+graphic(image,options)+"}";
+}
+
+string hyperlink(string url, string text)
+{
+ return "\href{"+url+"}{"+text+"}";
+}
+
+string link(string url, string text, string options="")
+{
+ return hyperlink("run:"+url,text);
+}
+
diff --git a/Master/texmf-dist/asymptote/feynman.asy b/Master/texmf-dist/asymptote/feynman.asy
new file mode 100644
index 00000000000..4360ee9f524
--- /dev/null
+++ b/Master/texmf-dist/asymptote/feynman.asy
@@ -0,0 +1,622 @@
+/*****************************************************************************
+ * feynman.asy -- An Asymptote library for drawing Feynman diagrams. *
+ * *
+ * by: Martin Wiebusch <martin.wiebusch@gmx.net> *
+ * last change: 2007/04/13 *
+ *****************************************************************************/
+
+
+/* default parameters ********************************************************/
+
+// default ratio of width (distance between two loops) to amplitude for a gluon
+// line. The gluon function uses this ratio, if the width parameter is
+// negative.
+real gluonratio;
+
+// default ratio of width (distance between two crests) to amplitude for a
+// photon line. The photon function uses this ratio, if the width parameter is
+// negative.
+real photonratio;
+
+// default gluon amplitude
+real gluonamplitude;
+
+// default photon amplitude
+real photonamplitude;
+
+// default pen for drawing the background. Usually white.
+pen backgroundpen;
+
+// default pen for drawing gluon lines
+pen gluonpen;
+
+// default pen for drawing photon lines
+pen photonpen;
+
+// default pen for drawing fermion lines
+pen fermionpen;
+
+// default pen for drawing scalar lines
+pen scalarpen;
+
+// default pen for drawing ghost lines
+pen ghostpen;
+
+// default pen for drawing double lines
+pen doublelinepen;
+
+// default pen for drawing vertices
+pen vertexpen;
+
+// default pen for drawing big vertices (drawVertexOX and drawVertexBoxX)
+pen bigvertexpen;
+
+// inner spacing of a double line
+real doublelinespacing;
+
+// default arrow for propagators
+arrowbar currentarrow;
+
+// if true, each of the drawSomething commands blots out the background
+// (with pen backgroundpen) before drawing.
+bool overpaint;
+
+// margin around lines. If one line is drawn over anoter, a white margin
+// of size linemargin is kept around the top one.
+real linemargin;
+
+// at vertices, where many lines join, the last line drawn should not blot
+// out the others. By not erasing the background near the ends of lines,
+// this is prevented for lines with an angle greater than minvertexangle to
+// each other. Note, that small values for minvertexangle mean that the
+// background is only erased behind a small segment of every line. Setting
+// minvertexangle = 0 effectively disables background erasing for lines.
+real minvertexangle;
+
+// size (radius) of vertices
+real vertexsize;
+
+// size (radius) of big vertices (drawVertexOX and drawVertexBoxX)
+real bigvertexsize;
+
+/* defaults for momentum arrows **********************************************/
+
+// (momentum arrows are small arrows parallel to particle lines indicating the
+// direction of momentum)
+
+// default size of the arrowhead of momentum arrows
+arrowbar currentmomarrow;
+
+// default length of momentum arrows
+real momarrowlength;
+
+// default pen for momentum arrows
+pen momarrowpen;
+
+// default offset between momentum arrow and related particle line
+real momarrowoffset;
+
+// default margin for momentum arrows
+real momarrowmargin;
+
+// factor for determining the size of momentum arrowheads. After changing it,
+// you still have to update currentmomarrow manually.
+real momarrowfactor;
+
+// size function for momentum arrowheads
+real momarrowsize(pen p=momarrowpen) { return momarrowfactor*linewidth(p); }
+
+
+/* defaults for texshipout ***************************************************/
+
+// tex command for including graphics. It takes one argument, which is the
+// name of the graphics (eps or pdf) file.
+string includegraphicscommand;
+
+// Determines whether the suffix (.eps or .pdf) should be appended to the stem
+// of the file name in the \includegraphics command.
+bool appendsuffix;
+
+
+/* helper functions **********************************************************/
+
+// internal function for overpainting
+private void do_overpaint(picture pic, path p, pen bgpen,
+ real halfwidth, real vertexangle)
+{
+ real tanvertexangle = tan(vertexangle*pi/180);
+ if(tanvertexangle != 0) {
+ real t1 = arctime(p, halfwidth/tanvertexangle+halfwidth);
+ real t2 = arctime(p, arclength(p)-halfwidth/tanvertexangle-halfwidth);
+ draw(pic, subpath(p, t1, t2),
+ bgpen+linewidth(2*halfwidth));
+ }
+}
+
+// returns the path of a gluon line along path p, with amplitude amp and width
+// width (distance between two loops). If width is negative, the width is
+// set to amp*gluonratio
+path gluon(path p, real amp = gluonamplitude, real width=-1)
+{
+ if(width < 0) width = abs(gluonratio*amp);
+
+ real pathlen = arclength(p);
+ int ncurls = floor(pathlen/width);
+ real firstlen = (pathlen - width*(ncurls-1))/2;
+ real firstt = arctime(p, firstlen);
+ pair firstv = dir(p, firstt);
+ guide g = point(p, 0)..{firstv}( point(p, firstt)
+ +amp*unit(rotate(90)*firstv));
+
+ real t1;
+ pair v1;
+ real t2;
+ pair v2;
+ pathlen -= firstlen;
+ for(real len = firstlen+width/2; len < pathlen; len += width) {
+ t1 = arctime(p, len);
+ v1 = dir(p, t1);
+ t2 = arctime(p, len + width/2);
+ v2 = dir(p, t2);
+
+ g=g..{-v1}(point(p, t1)+amp*unit(rotate(-90)*v1))
+ ..{+v2}(point(p, t2)+amp*unit(rotate(+90)*v2));
+ }
+ g = g..point(p, size(p));
+ return g;
+}
+
+// returns the path of a photon line along path p, with amplitude amp and width
+// width (distance between two crests). If width is negative, the width is
+// set to amp*photonratio
+path photon(path p, real amp = photonamplitude, real width=-1)
+{
+ if(width < 0)
+ width = abs(photonratio*amp)/2;
+ else
+ width = width/2;
+
+ real pathlen = arclength(p);
+ int ncurls = floor(pathlen/width);
+ real firstlen = (pathlen - width*ncurls)/2;
+ real firstt = arctime(p, firstlen+width);
+ guide g = point(p, 0){unit(point(p, firstt)-point(p, 0))};
+
+ real t;
+ pair v;
+ pathlen -= firstlen;
+ for(real len = firstlen+width; len < pathlen; len += width) {
+ t = arctime(p, len);
+ v = dir(p, t);
+
+ g=g..{v}(point(p, t)+amp*unit(rotate(90)*v));
+ amp = -amp;
+ }
+ g = g..{unit(point(p, size(p))-point(p, t))}point(p, size(p));
+ return g;
+}
+
+// returns the path of a momentum arrow along path p, with length length,
+// an offset offset from the path p and at position position. position will
+// usually be one of the predefined pairs left or right. Making adjust
+// nonzero shifts the momentum arrow along the path.
+path momArrowPath(path p,
+ align align,
+ position pos,
+ real offset = momarrowoffset,
+ real length = momarrowlength)
+{
+ real pathlen = arclength(p);
+
+ real t1, t2;
+ if(pos.relative) {
+ t1 = arctime(p, (pathlen-length)*pos.position.x);
+ t2 = arctime(p, (pathlen-length)*pos.position.x+length);
+ } else {
+ t1 = arctime(p, (pathlen-length)/2 + pos.position.x);
+ t2 = arctime(p, (pathlen+length)/2+ pos.position.x);
+ }
+
+ pair v1 = dir(p, t1);
+ pair v2 = dir(p, t2);
+
+ pair p1, p2;
+ if(align.relative) {
+ p1 = point(p, t1) + offset*abs(align.dir)
+ *unit(rotate(degrees(align.dir)-90)*v1);
+ p2 = point(p, t2) + offset*abs(align.dir)
+ *unit(rotate(degrees(align.dir)-90)*v2);
+ } else {
+ p1 = point(p, t1) + offset*align.dir;
+ p2 = point(p, t2) + offset*align.dir;
+ }
+
+ return p1{v1}..{v2}p2;
+}
+
+
+
+
+/* drawing functions *********************************************************/
+
+// draw a gluon line on picture pic, along path p, with amplitude amp, width
+// width (distance between loops) and with pen fgpen. If erasebg is true,
+// bgpen is used to erase the background behind the line and at a margin
+// margin around it. The background is not erased at a certain distance to
+// the endpoints, which is determined by vertexangle (see comments to the
+// default parameter minvertexangle). For negative values of width, the width
+// is set to gluonratio*amp.
+void drawGluon(picture pic = currentpicture,
+ path p,
+ real amp = gluonamplitude,
+ real width = -1,
+ pen fgpen = gluonpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real vertexangle = minvertexangle,
+ real margin = linemargin)
+{
+ if(width < 0) width = abs(2*amp);
+
+ if(erasebg) do_overpaint(pic, p, bgpen, amp+margin, vertexangle);
+ draw(pic, gluon(p, amp, width), fgpen);
+}
+
+// draw a photon line on picture pic, along path p, with amplitude amp, width
+// width (distance between loops) and with pen fgpen. If erasebg is true,
+// bgpen is used to erase the background behind the line and at a margin
+// margin around it. The background is not erased at a certain distance to
+// the endpoints, which is determined by vertexangle (see comments to the
+// default parameter minvertexangle). For negative values of width, the width
+// is set to photonratio*amp.
+void drawPhoton(picture pic = currentpicture,
+ path p,
+ real amp = photonamplitude,
+ real width = -1,
+ pen fgpen = currentpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real vertexangle = minvertexangle,
+ real margin = linemargin)
+{
+ if(width < 0) width = abs(4*amp);
+
+ if(erasebg) do_overpaint(pic, p, bgpen, amp+margin, vertexangle);
+ draw(pic, photon(p, amp, width), fgpen);
+}
+
+// draw a fermion line on picture pic, along path p with pen fgpen and an
+// arrowhead arrow. If erasebg is true, bgpen is used to erase the background
+// at a margin margin around the line. The background is not erased at a
+// certain distance to the endpoints, which is determined by vertexangle
+// (see comments to the default parameter minvertexangle).
+void drawFermion(picture pic = currentpicture,
+ path p,
+ pen fgpen = currentpen,
+ arrowbar arrow = currentarrow,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real vertexangle = minvertexangle,
+ real margin = linemargin)
+{
+ if(erasebg) do_overpaint(pic, p, bgpen,
+ linewidth(fgpen)+margin, vertexangle);
+ draw(pic, p, fgpen, arrow);
+}
+
+// draw a scalar line on picture pic, along path p with pen fgpen and an
+// arrowhead arrow. If erasebg is true, bgpen is used to erase the background
+// at a margin margin around the line. The background is not erased at a
+// certain distance to the endpoints, which is determined by vertexangle
+// (see comments to the default parameter minvertexangle).
+void drawScalar(picture pic = currentpicture,
+ path p,
+ pen fgpen = scalarpen,
+ arrowbar arrow = currentarrow,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real vertexangle = minvertexangle,
+ real margin = linemargin)
+{
+ if(erasebg) do_overpaint(pic, p, bgpen,
+ linewidth(fgpen)+margin, vertexangle);
+ draw(pic, p, fgpen, arrow);
+}
+
+// draw a ghost line on picture pic, along path p with pen fgpen and an
+// arrowhead arrow. If erasebg is true, bgpen is used to erase the background
+// at a margin margin around the line. The background is not erased at a
+// certain distance to the endpoints, which is determined by vertexangle
+// (see comments to the default parameter minvertexangle).
+void drawGhost(picture pic = currentpicture,
+ path p,
+ pen fgpen = ghostpen,
+ arrowbar arrow = currentarrow,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real vertexangle = minvertexangle,
+ real margin = linemargin)
+{
+ if(erasebg) do_overpaint(pic, p, bgpen,
+ linewidth(fgpen)+margin, vertexangle);
+ draw(pic, p, fgpen, arrow);
+}
+
+// draw a double line on picture pic, along path p with pen fgpen, an inner
+// spacing of dlspacint and an arrowhead arrow. If erasebg is true, bgpen is
+// used to erase the background at a margin margin around the line. The
+// background is not erased at a certain distance to the endpoints, which is
+// determined by vertexangle (see comments to the default parameter
+// minvertexangle).
+void drawDoubleLine(picture pic = currentpicture,
+ path p,
+ pen fgpen = doublelinepen,
+ real dlspacing = doublelinespacing,
+ arrowbar arrow = currentarrow,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real vertexangle = minvertexangle,
+ real margin = linemargin)
+{
+ if(erasebg) do_overpaint(pic, p, bgpen,
+ linewidth(fgpen)+margin, vertexangle);
+
+ real htw = linewidth(fgpen)+dlspacing/2;
+ draw(pic, p, fgpen+2*htw);
+ draw(pic, p, bgpen+(linewidth(dlspacing)));
+ path rect = (-htw,-htw)--(-htw,htw)--(0,htw)--(0,-htw)--cycle;
+ fill(shift(point(p,0))*rotate(degrees(dir(p,0)))*rect, bgpen);
+ fill(shift(point(p,size(p)))*scale(-1)*rotate(degrees(dir(p,size(p))))*
+ rect,bgpen);
+ draw(pic, p, invisible, arrow);
+}
+
+// draw a vertex dot on picture pic, at position xy with radius r and pen
+// fgpen
+void drawVertex(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen)
+{
+ fill(pic, circle(xy, r), fgpen);
+}
+
+// draw an empty vertex dot on picture pic, at position xy with radius r
+// and pen fgpen. If erasebg is true, the background is erased in the inside
+// of the circle.
+void drawVertexO(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen)
+{
+ if(erasebg)
+ filldraw(pic, circle(xy, r), bgpen, fgpen);
+ else
+ draw(pic, circle(xy, r), fgpen);
+}
+
+// draw a vertex triangle on picture pic, at position xy with radius r and pen
+// fgpen
+void drawVertexTriangle(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen)
+{
+ real cospi6 = cos(pi/6);
+ real sinpi6 = sin(pi/6);
+ path triangle = (cospi6,-sinpi6)--(0,1)--(-cospi6,-sinpi6)--cycle;
+ fill(pic, shift(xy)*scale(r)*triangle, fgpen);
+}
+
+// draw an empty vertex triangle on picture pic, at position xy with size r
+// and pen fgpen. If erasebg is true, the background is erased in the inside
+// of the triangle.
+void drawVertexTriangleO(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen)
+{
+ real cospi6 = cos(pi/6);
+ real sinpi6 = sin(pi/6);
+ path triangle = (cospi6,-sinpi6)--(0,1)--(-cospi6,-sinpi6)--cycle;
+
+ if(erasebg)
+ filldraw(pic, shift(xy)*scale(r)*triangle, bgpen, fgpen);
+ else
+ draw(pic, shift(xy)*scale(r)*triangle, fgpen);
+}
+
+// draw a vertex box on picture pic, at position xy with radius r and pen
+// fgpen
+void drawVertexBox(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen)
+{
+ path box = (1,1)--(-1,1)--(-1,-1)--(1,-1)--cycle;
+ fill(pic, shift(xy)*scale(r)*box, fgpen);
+}
+
+// draw an empty vertex box on picture pic, at position xy with size r
+// and pen fgpen. If erasebg is true, the background is erased in the inside
+// of the box.
+void drawVertexBoxO(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen)
+{
+ path box = (1,1)--(-1,1)--(-1,-1)--(1,-1)--cycle;
+ if(erasebg)
+ filldraw(pic, shift(xy)*scale(r)*box, bgpen, fgpen);
+ else
+ draw(pic, shift(xy)*scale(r)*box, fgpen);
+}
+
+// draw an X on picture pic, at position xy with size r and pen
+// fgpen
+void drawVertexX(picture pic = currentpicture,
+ pair xy,
+ real r = vertexsize,
+ pen fgpen = vertexpen)
+{
+ draw(pic, shift(xy)*scale(r)*((-1,-1)--(1,1)), fgpen);
+ draw(pic, shift(xy)*scale(r)*((1,-1)--(-1,1)), fgpen);
+}
+
+// draw a circle with an X in the middle on picture pic, at position xy with
+// size r and pen fgpen. If erasebg is true, the background is erased in the
+// inside of the circle.
+void drawVertexOX(picture pic = currentpicture,
+ pair xy,
+ real r = bigvertexsize,
+ pen fgpen = vertexpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen)
+{
+ if(erasebg)
+ filldraw(pic, circle(xy, r), bgpen, fgpen);
+ else
+ draw(pic, circle(xy, r), fgpen);
+ draw(pic, shift(xy)*scale(r)*(NW--SE), fgpen);
+ draw(pic, shift(xy)*scale(r)*(SW--NE), fgpen);
+}
+
+// draw a box with an X in the middle on picture pic, at position xy with
+// size r and pen fgpen. If erasebg is true, the background is erased in the
+// inside of the box.
+void drawVertexBoxX(picture pic = currentpicture,
+ pair xy,
+ real r = bigvertexsize,
+ pen fgpen = vertexpen,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen)
+{
+ path box = (1,1)--(-1,1)--(-1,-1)--(1,-1)--cycle;
+ box = shift(xy)*scale(r)*box;
+ if(erasebg)
+ filldraw(pic, box, bgpen, fgpen);
+ else
+ draw(pic, box, fgpen);
+ draw(pic, shift(xy)*scale(r)*((-1,-1)--(1,1)), fgpen);
+ draw(pic, shift(xy)*scale(r)*((1,-1)--(-1,1)), fgpen);
+}
+
+// draw a momentum arrow on picture pic, along path p, at position position
+// (use one of the predefined pairs left or right), with an offset offset
+// from the path, a length length, a pen fgpen and an arrowhead arrow. Making
+// adjust nonzero shifts the momentum arrow along the path. If erasebg is true,
+// the background is erased inside a margin margin around the momentum arrow.
+// Make sure that offset and margin are chosen in such a way that the momentum
+// arrow does not overdraw the particle line.
+void drawMomArrow(picture pic = currentpicture,
+ path p,
+ align align,
+ position pos = MidPoint,
+ real offset = momarrowoffset,
+ real length = momarrowlength,
+ pen fgpen = momarrowpen,
+ arrowbar arrow = currentmomarrow,
+ bool erasebg = overpaint,
+ pen bgpen = backgroundpen,
+ real margin = momarrowmargin)
+{
+ path momarrow = momArrowPath(p, align, pos, offset, length);
+ if(erasebg) do_overpaint(pic, momarrow, bgpen,
+ linewidth(fgpen)+margin, 90);
+ draw(pic, momarrow, fgpen, arrow);
+}
+
+
+/* initialisation ************************************************************/
+
+// The function fmdefaults() tries to guess reasonable values for the
+// default parameters above by looking at the default parameters of plain.asy
+// (essentially, currentpen, arrowfactor and dotfactor). After customising the
+// default parameters of plain.asy, you may call fmdefaults to adjust the
+// parameters of feynman.asy.
+void fmdefaults()
+{
+ real arrowsize=arrowsize(currentpen);
+ real linewidth=linewidth(currentpen);
+
+ gluonratio = 2;
+ photonratio = 4;
+ gluonamplitude = arrowsize/3;
+ photonamplitude = arrowsize/4;
+
+ backgroundpen = white;
+ gluonpen = currentpen;
+ photonpen = currentpen;
+ fermionpen = currentpen;
+ scalarpen = dashed+linewidth;
+ ghostpen = dotted+linewidth;
+ doublelinepen = currentpen;
+ vertexpen = currentpen;
+ bigvertexpen = currentpen;
+ currentarrow = MidArrow;
+
+ doublelinespacing = 2*linewidth;
+ linemargin = 0.5*arrowsize;
+ minvertexangle = 30;
+ overpaint = true;
+ vertexsize = 0.5*dotfactor*linewidth;
+ bigvertexsize = 0.4*arrowsize;
+
+ momarrowfactor = 1.5*arrowfactor;
+ momarrowlength = 2.5*arrowsize;
+ momarrowpen = currentpen+0.5*linewidth;
+ momarrowoffset = 0.8*arrowsize;
+ momarrowmargin = 0.25*arrowsize;
+ currentmomarrow = EndArrow(momarrowsize());
+
+ includegraphicscommand = "\includegraphics";
+ appendsuffix = false;
+}
+
+// We call fmdefaults once, when the module is loaded.
+fmdefaults();
+
+
+/* shipout *******************************************************************/
+
+bool YAlign = false;
+bool XYAlign = true;
+
+// texshipout("filename", pic) creates two files: filename.eps holding the
+// picture pic and filename.tex holding some LaTeX code that includes the
+// picture from filename.eps and shifts it vertically in such a way that the
+// point (0,0) lies on the baseline.
+void texshipout(string stem,
+ picture pic = currentpicture,
+ bool xalign = YAlign)
+{
+ file tf = output(stem + ".tex");
+ pair min=pic.min();
+ real depth = min.y;
+ real xoffset = min.x;
+ if(xalign) {
+ write(tf, "\makebox[0pt][l]{\kern");
+ write(tf, xoffset);
+ write(tf, "bp\relax");
+ }
+ write(tf, "\raisebox{");
+ write(tf, depth);
+ write(tf, "bp}{"+includegraphicscommand+"{");
+ write(tf, stem);
+ string suffix="."+nativeformat();
+ if(appendsuffix)
+ write(tf, suffix);
+ write(tf, "}}");
+ if(xalign)
+ write(tf, "}");
+ close(tf);
+ shipout(stem+suffix, pic);
+}
+
+
diff --git a/Master/texmf-dist/asymptote/flowchart.asy b/Master/texmf-dist/asymptote/flowchart.asy
new file mode 100644
index 00000000000..3db66173755
--- /dev/null
+++ b/Master/texmf-dist/asymptote/flowchart.asy
@@ -0,0 +1,526 @@
+// Flowchart routines written by Jacques Pienaar, Steve Melenchuk, John Bowman.
+
+private import math;
+
+struct flowdir {}
+
+restricted flowdir Horizontal;
+restricted flowdir Vertical;
+
+real minblockwidth=0;
+real minblockheight=0;
+real mincirclediameter=0;
+real defaultexcursion=0.1;
+
+struct block {
+ // The absolute center of the block in user coordinates.
+ pair center;
+
+ // The size of the block
+ pair size;
+
+ // The relative center of the block.
+ pair f_center;
+
+ // These eight variables return the appropriate location on the block
+ // in relative coordinates, where the lower left corner of the block is (0,0).
+ pair f_top;
+ pair f_left;
+ pair f_right;
+ pair f_bottom;
+ pair f_topleft;
+ pair f_topright;
+ pair f_bottomleft;
+ pair f_bottomright;
+
+ void operator init(pair z) {
+ center=z;
+ }
+
+ void operator init(real x, real y) {
+ center=(x,y);
+ }
+
+ pair shift(transform t=identity()) {
+ return t*center-f_center;
+ }
+
+ // Returns the relative position along the boundary of the block.
+ pair f_position(real x);
+
+ // Returns the absolute position along the boundary of the block.
+ pair position(real x, transform t=identity()) {
+ return shift(t)+f_position(x);
+ }
+
+ // These eight functions return the appropriate location on the block
+ // in absolute coordinates.
+ pair top(transform t=identity()) {
+ return shift(t)+f_top;
+ }
+ pair bottom(transform t=identity()) {
+ return shift(t)+f_bottom;
+ }
+ pair left(transform t=identity()) {
+ return shift(t)+f_left;
+ }
+ pair right(transform t=identity()) {
+ return shift(t)+f_right;
+ }
+ pair topleft(transform t=identity()) {
+ return shift(t)+f_topleft;
+ }
+ pair topright(transform t=identity()) {
+ return shift(t)+f_topright;
+ }
+ pair bottomleft(transform t=identity()) {
+ return shift(t)+f_bottomleft;
+ }
+ pair bottomright(transform t=identity()) {
+ return shift(t)+f_bottomright;
+ }
+
+ // Return a frame representing the block.
+ frame draw(pen p=currentpen);
+
+ // Store optional label on outgoing edge.
+ Label label;
+
+ // Store rectilinear path directions.
+ pair[] dirs;
+
+ // Store optional arrow.
+ arrowbar arrow=None;
+};
+
+// Construct a rectangular block with header and body objects.
+block rectangle(object header, object body, pair center=(0,0),
+ pen headerpen=mediumgray, pen bodypen=invisible,
+ pen drawpen=currentpen,
+ real dx=3, real minheaderwidth=minblockwidth,
+ real minheaderheight=minblockwidth,
+ real minbodywidth=minblockheight,
+ real minbodyheight=minblockheight)
+{
+ frame fbody=body.f;
+ frame fheader=header.f;
+ pair mheader=min(fheader);
+ pair Mheader=max(fheader);
+ pair mbody=min(fbody);
+ pair Mbody=max(fbody);
+ pair bound0=Mheader-mheader;
+ pair bound1=Mbody-mbody;
+ real width=max(bound0.x,bound1.x);
+ pair z0=maxbound((width+2dx,bound0.y+2dx),(minbodywidth,minbodyheight));
+ pair z1=maxbound((width+2dx,bound1.y+2dx),(minheaderwidth,minheaderheight));
+ path shape=(0,0)--(0,z1.y)--(0,z0.y+z1.y)--(z0.x,z0.y+z1.y)--z1--(z0.x,0)--
+ cycle;
+
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shift(0,z1.y)*box((0,0),z0),headerpen,drawpen);
+ add(block,shift(-0.5*(Mheader+mheader))*fheader,(0,z1.y)+0.5z0);
+ filldraw(block,box((0,0),z1),bodypen,drawpen);
+ add(block,shift(-0.5*(Mbody+mbody))*fbody,0.5z1);
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=interp(point(shape,0),point(shape,3),0.5);
+ block.f_bottomleft=point(shape,0);
+ block.f_bottom=point(shape,5.5);
+ block.f_bottomright=point(shape,5);
+ block.f_right=point(shape,4.5);
+ block.f_topright=point(shape,3);
+ block.f_top=point(shape,2.5);
+ block.f_topleft=point(shape,2);
+ block.f_left=point(shape,0.5);
+ block.center=center;
+ block.size=point(shape,3);
+ return block;
+}
+
+// As above, but without the header.
+block rectangle(object body, pair center=(0,0),
+ pen fillpen=invisible, pen drawpen=currentpen,
+ real dx=3, real minwidth=minblockwidth,
+ real minheight=minblockheight)
+{
+ frame f=body.f;
+ pair m=min(f);
+ pair M=max(f);
+ pair z=maxbound(M-m+dx*(2,2),(minwidth,minheight));
+ path shape=box((0,0),z);
+
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shape,fillpen,drawpen);
+ add(block,shift(-0.5*(M+m))*f,0.5z);
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=0.5*z;
+ block.center=center;
+ block.size=z;
+ block.f_bottomleft=point(shape,0);
+ block.f_bottom=point(shape,0.5);
+ block.f_bottomright=point(shape,1);
+ block.f_right=point(shape,1.5);
+ block.f_topright=point(shape,2);
+ block.f_top=point(shape,2.5);
+ block.f_topleft=point(shape,3);
+ block.f_left=point(shape,3.5);
+ return block;
+}
+
+block parallelogram(object body, pair center=(0,0),
+ pen fillpen=invisible, pen drawpen=currentpen,
+ real dx=3, real slope=2,
+ real minwidth=minblockwidth,
+ real minheight=minblockheight)
+{
+ frame f=body.f;
+ pair m=min(f);
+ pair M=max(f);
+ pair bound=maxbound(M-m+dx*(0,2),(minwidth,minheight));
+
+ real skew=bound.y/slope;
+ real a=bound.x+skew;
+ real b=bound.y;
+
+ path shape=(0,0)--(a,0)--(a+skew,b)--(skew,b)--cycle;
+
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shape,fillpen,drawpen);
+ add(block,shift(-0.5*(M+m))*f,((a+skew)/2,b/2));
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=((a+skew)/2,b/2);
+ block.center=center;
+ block.size=(a+skew,b);
+ block.f_bottomleft=(0,0);
+ block.f_bottom=((a+skew)/2,0);
+ block.f_bottomright=(a,0);
+ block.f_right=(a+skew/2,b/2);
+ block.f_topright=(a+skew,b);
+ block.f_top=((a+skew)/2,b);
+ block.f_topleft=(skew,b);
+ block.f_left=(skew/2,b/2);
+ return block;
+}
+
+block diamond(object body, pair center=(0,0),
+ pen fillpen=invisible, pen drawpen=currentpen,
+ real ds=5, real dw=1,
+ real height=20, real minwidth=minblockwidth,
+ real minheight=minblockheight)
+{
+ frame f=body.f;
+ pair m=min(f);
+ pair M=max(f);
+ pair bound=maxbound(M-m,(minwidth,minheight));
+
+ real e=ds;
+ real a=0.5bound.x-dw;
+ real b=0.5bound.y;
+ real c=b+height;
+
+ real arg=a^2+b^2+c^2-2b*c-e^2;
+ real denom=e^2-a^2;
+ real slope=arg >= 0 && denom != 0 ? (a*(c-b)-e*sqrt(arg))/denom : 1.0;
+ real d=abs(c/slope);
+
+ path shape=(2d,c)--(d,2c)--(0,c)--(d,0)--cycle;
+
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shape,fillpen,drawpen);
+ add(block,shift(-0.5*(M+m))*f,(d,c));
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=(point(shape,1).x,point(shape,0).y);
+ block.center=center;
+ block.size=(point(shape,0).x,point(shape,1).y);
+ block.f_bottomleft=point(shape,2.5);
+ block.f_bottom=point(shape,3);
+ block.f_bottomright=point(shape,3.5);
+ block.f_right=point(shape,0);
+ block.f_topright=point(shape,0.5);
+ block.f_top=point(shape,1);
+ block.f_topleft=point(shape,1.5);
+ block.f_left=point(shape,2);
+ return block;
+}
+
+block circle(object body, pair center=(0,0), pen fillpen=invisible,
+ pen drawpen=currentpen, real dr=3,
+ real mindiameter=mincirclediameter)
+{
+ frame f=body.f;
+ pair m=min(f);
+ pair M=max(f);
+ real r=max(0.5length(M-m)+dr,0.5mindiameter);
+
+ path shape=(0,r)..(r,2r)..(2r,r)..(r,0)..cycle;
+
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shape,fillpen,drawpen);
+ add(block,shift(-0.5*(M+m))*f,(r,r));
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=(r,r);
+ block.center=center;
+ block.size=(2r,2r);
+ block.f_left=point(shape,0);
+ block.f_topleft=point(shape,0.5);
+ block.f_top=point(shape,1);
+ block.f_topright=point(shape,1.5);
+ block.f_right=point(shape,2);
+ block.f_bottomright=point(shape,2.5);
+ block.f_bottom=point(shape,3);
+ block.f_bottomleft=point(shape,3.5);
+ return block;
+}
+
+block roundrectangle(object body, pair center=(0,0),
+ pen fillpen=invisible, pen drawpen=currentpen,
+ real ds=5, real dw=0, real minwidth=minblockwidth,
+ real minheight=minblockheight)
+{
+ frame f=body.f;
+ pair m=min(f);
+ pair M=max(f);
+ pair bound=maxbound(M-m,(minwidth,minheight));
+
+ real a=bound.x;
+ real b=bound.y;
+
+ path shape=(0,ds+dw)--(0,ds+b-dw){up}..{right}
+ (ds+dw,2ds+b)--(ds+a-dw,2ds+b){right}..{down}
+ (2ds+a,ds+b-dw)--(2ds+a,ds+dw){down}..{left}
+ (ds+a-dw,0)--(ds+dw,0){left}..{up}cycle;
+
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shape,fillpen,drawpen);
+ add(block,shift(-0.5*(M+m))*f,(ds,ds)+0.5bound);
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=(ds+0.5a,ds+0.5b);
+ block.center=center;
+ block.size=(2ds+a,2ds+b);
+ block.f_bottomleft=point(shape,7.5);
+ block.f_bottom=point(shape,6.5);
+ block.f_bottomright=point(shape,5.5);
+ block.f_right=point(shape,4.5);
+ block.f_topright=point(shape,3.5);
+ block.f_top=point(shape,2.5);
+ block.f_topleft=point(shape,1.5);
+ block.f_left=point(shape,0.5);
+ return block;
+}
+
+block bevel(object body, pair center=(0,0), pen fillpen=invisible,
+ pen drawpen=currentpen, real dh=5, real dw=5,
+ real minwidth=minblockwidth, real minheight=minblockheight)
+{
+ frame f=body.f;
+ pair m=min(f);
+ pair M=max(f);
+ pair bound=maxbound(M-m,(minwidth,minheight));
+
+ real a=bound.x;
+ real b=0.5bound.y;
+
+ path shape=(2dw+a,b+dh)--(dw+a,2b+2dh)--(dw,2b+2dh)--(0,b+dh)--(dw,0)--
+ (dw+a,0)--cycle;
+ block block;
+ block.draw=new frame(pen p) {
+ frame block;
+ filldraw(block,shape,fillpen,drawpen);
+ add(block,shift(-0.5*(M+m))*f,(0.5bound+(dw,dh)));
+ return block;
+ };
+ block.f_position=new pair(real x) {
+ return point(shape,x);
+ };
+ block.f_center=(dw+0.5a,dh+b);
+ block.center=center;
+ block.size=(2dw+a,2dh+2b);
+ block.f_bottomleft=point(shape,4);
+ block.f_bottom=point(shape,4.5);
+ block.f_bottomright=point(shape,5);
+ block.f_right=point(shape,0);
+ block.f_topright=point(shape,1);
+ block.f_top=point(shape,1.5);
+ block.f_topleft=point(shape,2);
+ block.f_left=point(shape,3);
+ return block;
+}
+
+path path(pair point[] ... flowdir dir[])
+{
+ path line=point[0];
+ pair current, prev=point[0];
+ for(int i=1; i < point.length; ++i) {
+ if(i-1 >= dir.length || dir[i-1] == Horizontal)
+ current=(point[i].x,point[i-1].y);
+ else
+ current=(point[i-1].x,point[i].y);
+
+ if(current != prev) {
+ line=line--current;
+ prev=current;
+ }
+
+ current=point[i];
+ if(current != prev) {
+ line=line--current;
+ prev=current;
+ }
+ }
+ return line;
+}
+
+void draw(picture pic=currentpicture, block block, pen p=currentpen)
+{
+ pic.add(new void(frame f, transform t) {
+ add(f,shift(block.shift(t))*block.draw(p));
+ },true);
+ pic.addBox(block.center,block.center,
+ -0.5*block.size+min(p),0.5*block.size+max(p));
+}
+
+typedef block blockconnector(block, block);
+
+blockconnector blockconnector(picture pic, transform t, pen p=currentpen,
+ margin margin=PenMargin)
+{
+ return new block(block b1, block b2) {
+ if(b1.dirs.length == 0) {
+ if(abs(b1.center.y-b2.center.y) < sqrtEpsilon) {
+ // horizontally aligned
+ b1.dirs[0]=b1.center.x < b2.center.x ? right : left;
+ blockconnector(pic,t,p,margin)(b1,b2);
+ } else if(abs(b1.center.x-b2.center.x) < sqrtEpsilon) {
+ // vertically aligned
+ b1.dirs[0]=b1.center.y < b2.center.y ? up : down;
+ blockconnector(pic,t,p,margin)(b1,b2);
+ } else {
+ if(abs(b1.center.y-b2.center.y) < abs(b1.center.x-b2.center.x)) {
+ b1.dirs[0]=b1.center.x < b2.center.x ? right : left;
+ b1.dirs[1]=b1.center.y < b2.center.y ? up : down;
+ blockconnector(pic,t,p,margin)(b1,b2);
+ } else {
+ b1.dirs[0]=b1.center.y < b2.center.y ? up : down;
+ b1.dirs[1]=b1.center.x < b2.center.x ? right : left;
+ blockconnector(pic,t,p,margin)(b1,b2);
+ }
+ }
+ return b2;
+ }
+
+ // compute the link for given directions (and label if any)
+ pair[] dirs=copy(b1.dirs); // deep copy
+ pair current,prev;
+ pair dir=dirs[0];
+ if(dir == up) prev=b1.top(t);
+ if(dir == down) prev=b1.bottom(t);
+ if(dir == left) prev=b1.left(t);
+ if(dir == right) prev=b1.right(t);
+ path line=prev;
+ arrowbar arrow=b1.arrow;
+
+ int i;
+ for(i=1; i < dirs.length-1; ++i) {
+ if(abs(length(dirs[i-1])-1) < sqrtEpsilon)
+ current=prev+t*dirs[i-1]*defaultexcursion;
+ else
+ current=prev+t*dirs[i-1];
+
+ if(current != prev) {
+ line=line--current;
+ prev=current;
+ }
+ }
+ dir=dirs[dirs.length-1];
+ current=0;
+ if(dir == up) current=b2.bottom(t);
+ if(dir == down) current=b2.top(t);
+ if(dir == left) current=b2.right(t);
+ if(dir == right) current=b2.left(t);
+ if(abs(dirs[i-1].y) < sqrtEpsilon &&
+ abs(prev.x-current.x) > sqrtEpsilon) {
+ prev=(current.x,prev.y);
+ line=line--prev; // horizontal
+ } else if(abs(dirs[i-1].x) < sqrtEpsilon &&
+ abs(prev.y-current.y) > sqrtEpsilon) {
+ prev=(prev.x,current.y);
+ line=line--prev;
+ }
+ if(current != prev)
+ line=line--current;
+
+ draw(pic,b1.label,line,p,arrow,margin);
+
+ b1.label="";
+ b1.dirs.delete();
+ b1.arrow=None;
+ return b2;
+ };
+}
+
+struct Dir
+{
+ pair z;
+ void operator init(pair z) {this.z=z;}
+}
+
+Dir Right=Dir(right);
+Dir Left=Dir(left);
+Dir Up=Dir(up);
+Dir Down=Dir(down);
+
+// Add a label to the current link
+block operator --(block b1, Label label)
+{
+ b1.label=label;
+ return b1;
+}
+
+// Add a direction to the current link
+block operator --(block b1, Dir dir)
+{
+ b1.dirs.push(dir.z);
+ return b1;
+}
+
+// Add an arrowbar to the current link
+block operator --(block b, arrowbar arrowbar)
+{
+ b.arrow=arrowbar;
+ return b;
+}
diff --git a/Master/texmf-dist/asymptote/fontsize.asy b/Master/texmf-dist/asymptote/fontsize.asy
new file mode 100644
index 00000000000..54b01829550
--- /dev/null
+++ b/Master/texmf-dist/asymptote/fontsize.asy
@@ -0,0 +1 @@
+if(latex()) usepackage("type1cm");
diff --git a/Master/texmf-dist/asymptote/geometry.asy b/Master/texmf-dist/asymptote/geometry.asy
new file mode 100644
index 00000000000..bbbc823fc54
--- /dev/null
+++ b/Master/texmf-dist/asymptote/geometry.asy
@@ -0,0 +1,7192 @@
+// geometry.asy
+
+// Copyright (C) 2007
+// Author: Philippe IVALDI 2007/09/01
+// http://www.piprime.fr/
+
+// This program is free software ; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation ; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY ; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program ; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// COMMENTARY:
+// An Asymptote geometry module.
+
+// THANKS:
+// Special thanks to Olivier Guibé for his help in mathematical issues.
+
+// BUGS:
+
+// CODE:
+
+import math;
+import markers;
+
+// A rotation in the direction dir limited to [-90,90]
+// This is useful for rotating text along a line in the direction dir.
+private transform rotate(explicit pair dir)
+{
+ real angle=degrees(dir);
+ if(angle > 90 && angle < 270) angle -= 180;
+ return rotate(angle);
+}
+
+// *=======================================================*
+// *........................HEADER.........................*
+/*<asyxml><variable type="real" signature="epsgeo"><code></asyxml>*/
+real epsgeo = 10 * sqrt(realEpsilon);/*<asyxml></code><documentation>Variable used in the approximate calculations.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="void" signature="addMargins(picture,real,real,real,real)"><code></asyxml>*/
+void addMargins(picture pic = currentpicture,
+ real lmargin = 0, real bmargin = 0,
+ real rmargin = lmargin, real tmargin = bmargin,
+ bool rigid = true, bool allObject = true)
+{/*<asyxml></code><documentation>Add margins to 'pic' with respect to
+ the current bounding box of 'pic'.
+ If 'rigid' is false, margins are added iff an infinite curve will
+ be prolonged on the margin.
+ If 'allObject' is false, fixed - size objects (such as labels and
+ arrowheads) will be ignored.</documentation></function></asyxml>*/
+ pair m = allObject ? truepoint(pic, SW) : point(pic, SW);
+ pair M = allObject ? truepoint(pic, NE) : point(pic, NE);
+ if(rigid) {
+ draw(m - inverse(pic.calculateTransform()) * (lmargin, bmargin), invisible);
+ draw(M + inverse(pic.calculateTransform()) * (rmargin, tmargin), invisible);
+ } else pic.addBox(m, M, -(lmargin, bmargin), (rmargin, tmargin));
+}
+
+real approximate(real t)
+{
+ real ot = t;
+ if(abs(t - ceil(t)) < epsgeo) ot = ceil(t);
+ else if(abs(t - floor(t)) < epsgeo) ot = floor(t);
+ return ot;
+}
+
+real[] approximate(real[] T)
+{
+ return map(approximate, T);
+}
+
+/*<asyxml><function type="real" signature="binomial(real,real)"><code></asyxml>*/
+real binomial(real n, real k)
+{/*<asyxml></code><documentation>Return n!/((n - k)!*k!)</documentation></function></asyxml>*/
+ return gamma(n + 1)/(gamma(n - k + 1) * gamma(k + 1));
+}
+
+/*<asyxml><function type="real" signature="rf(real,real,real)"><code></asyxml>*/
+real rf(real x, real y, real z)
+{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the first kind.
+ x, y, and z must be non negative, and at most one can be zero.</documentation></function></asyxml>*/
+ real ERRTOL = 0.0025,
+ TINY = 1.5e-38,
+ BIG = 3e37,
+ THIRD = 1/3,
+ C1 = 1/24,
+ C2 = 0.1,
+ C3 = 3/44,
+ C4 = 1/14;
+ real alamb, ave, delx, dely, delz, e2, e3, sqrtx, sqrty, sqrtz, xt, yt, zt;
+ if(min(x, y, z) < 0 || min(x + y, x + z, y + z) < TINY ||
+ max(x, y, z) > BIG) abort("rf: invalid arguments.");
+ xt = x;
+ yt = y;
+ zt = z;
+ do {
+ sqrtx = sqrt(xt);
+ sqrty = sqrt(yt);
+ sqrtz = sqrt(zt);
+ alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
+ xt = 0.25 * (xt + alamb);
+ yt = 0.25 * (yt + alamb);
+ zt = 0.25 * (zt + alamb);
+ ave = THIRD * (xt + yt + zt);
+ delx = (ave - xt)/ave;
+ dely = (ave - yt)/ave;
+ delz = (ave - zt)/ave;
+ } while(max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL);
+ e2 = delx * dely - delz * delz;
+ e3 = delx * dely * delz;
+ return (1.0 + (C1 * e2 - C2 - C3 * e3) * e2 + C4 * e3)/sqrt(ave);
+}
+
+/*<asyxml><function type="real" signature="rd(real,real,real)"><code></asyxml>*/
+real rd(real x, real y, real z)
+{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the second kind.
+ x and y must be positive, and at most one can be zero.
+ z must be non negative.</documentation></function></asyxml>*/
+ real ERRTOL = 0.0015,
+ TINY = 1e-25,
+ BIG = 4.5 * 10.0^21,
+ C1 = (3/14),
+ C2 = (1/6),
+ C3 = (9/22),
+ C4 = (3/26),
+ C5 = (0.25 * C3),
+ C6 = (1.5 * C4);
+ real alamb, ave, delx, dely, delz, ea, eb, ec, ed, ee, fac, sqrtx, sqrty,
+ sqrtz, sum, xt, yt, zt;
+ if (min(x, y) < 0 || min(x + y, z) < TINY || max(x, y, z) > BIG)
+ abort("rd: invalid arguments");
+ xt = x;
+ yt = y;
+ zt = z;
+ sum = 0;
+ fac = 1;
+ do {
+ sqrtx = sqrt(xt);
+ sqrty = sqrt(yt);
+ sqrtz = sqrt(zt);
+ alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
+ sum += fac/(sqrtz * (zt + alamb));
+ fac = 0.25 * fac;
+ xt = 0.25 * (xt + alamb);
+ yt = 0.25 * (yt + alamb);
+ zt = 0.25 * (zt + alamb);
+ ave = 0.2 * (xt + yt + 3.0 * zt);
+ delx = (ave - xt)/ave;
+ dely = (ave - yt)/ave;
+ delz = (ave - zt)/ave;
+ } while (max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL);
+ ea = delx * dely;
+ eb = delz * delz;
+ ec = ea - eb;
+ ed = ea - 6 * eb;
+ ee = ed + ec + ec;
+ return 3 * sum + fac * (1.0 + ed * (-C1 + C5 * ed - C6 * delz * ee)
+ +delz * (C2 * ee + delz * (-C3 * ec + delz * C4 * ea)))/(ave * sqrt(ave));
+}
+
+/*<asyxml><function type="real" signature="elle(real,real)"><code></asyxml>*/
+real elle(real phi, real k)
+{/*<asyxml></code><documentation>Legendre elliptic integral of the 2nd kind,
+ evaluated using Carlson's functions RD and RF.
+ The argument ranges are -infinity < phi < +infinity, 0 <= k * sin(phi) <= 1.</documentation></function></asyxml>*/
+ real result;
+ if (phi >= 0 && phi <= pi/2) {
+ real cc, q, s;
+ s = sin(phi);
+ cc = cos(phi)^2;
+ q = (1 - s * k) * (1 + s * k);
+ result = s * (rf(cc, q, 1) - (s * k)^2 * rd(cc, q, 1)/3);
+ } else
+ if (phi <= pi && phi >= 0) {
+ result = 2 * elle(pi/2, k) - elle(pi - phi, k);
+ } else
+ if (phi <= 3 * pi/2 && phi >= 0) {
+ result = 2 * elle(pi/2, k) + elle(phi - pi, k);
+ } else
+ if (phi <= 2 * pi && phi >= 0) {
+ result = 4 * elle(pi/2, k) - elle(2 * pi - phi, k);
+ } else
+ if (phi >= 0) {
+ int nb = floor(0.5 * phi/pi);
+ result = nb * elle(2 * pi, k) + elle(phi%(2 * pi), k);
+ } else result = -elle(-phi, k);
+ return result;
+}
+
+/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real,real,real,real,real,real)"><code></asyxml>*/
+pair[] intersectionpoints(pair A, pair B,
+ real a, real b, real c, real d, real f, real g)
+{/*<asyxml></code><documentation>Intersection points with the line (AB) and the quadric curve
+ a * x^2 + b * x * y + c * y^2 + d * x + f * y + g = 0 given in the default coordinate system</documentation></function></asyxml>*/
+ pair[] op;
+ real ap = B.y - A.y,
+ bpp = A.x - B.x,
+ cp = A.y * B.x - A.x * B.y;
+ real sol[];
+ if (abs(ap) > epsgeo) {
+ real aa = ap * c + a * bpp^2/ap - b * bpp,
+ bb = ap * f - bpp * d + 2 * a * bpp * cp/ap - b * cp,
+ cc = ap * g - cp * d + a * cp^2/ap;
+ sol = quadraticroots(aa, bb, cc);
+ for (int i = 0; i < sol.length; ++i) {
+ op.push((-bpp * sol[i]/ap - cp/ap, sol[i]));
+ }
+ } else {
+ real aa = a * bpp,
+ bb = d * bpp - b * cp,
+ cc = g * bpp - cp * f + c * cp^2/bpp;
+ sol = quadraticroots(aa, bb, cc);
+ for (int i = 0; i < sol.length; ++i) {
+ op.push((sol[i], -cp/bpp));
+ }
+ }
+ return op;
+}
+
+/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real[])"><code></asyxml>*/
+pair[] intersectionpoints(pair A, pair B, real[] equation)
+{/*<asyxml></code><documentation>Return the intersection points of the line AB with
+ the conic whose an equation is
+ equation[0] * x^2 + equation[1] * x * y + equation[2] * y^2 + equation[3] * x + equation[4] * y + equation[5] = 0</documentation></function></asyxml>*/
+ if(equation.length != 6) abort("intersectionpoints: bad length of array for a conic equation.");
+ return intersectionpoints(A, B, equation[0], equation[1], equation[2],
+ equation[3], equation[4], equation[5]);
+}
+// *........................HEADER.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *......................COORDINATES......................*
+
+real EPS = sqrt(realEpsilon);
+
+/*<asyxml><typedef type = "convert" return = "pair" params = "pair"><code></asyxml>*/
+typedef pair convert(pair);/*<asyxml></code><documentation>Function type to convert pair in an other coordinate system.</documentation></typedef></asyxml>*/
+/*<asyxml><typedef type = "abs" return = "real" params = "pair"><code></asyxml>*/
+typedef real abs(pair);/*<asyxml></code><documentation>Function type to calculate modulus of pair.</documentation></typedef></asyxml>*/
+/*<asyxml><typedef type = "dot" return = "real" params = "pair, pair"><code></asyxml>*/
+typedef real dot(pair, pair);/*<asyxml></code><documentation>Function type to calculate dot product.</documentation></typedef></asyxml>*/
+/*<asyxml><typedef type = "polar" return = "pair" params = "real, real"><code></asyxml>*/
+typedef pair polar(real, real);/*<asyxml></code><documentation>Function type to calculate the coordinates from the polar coordinates.</documentation></typedef></asyxml>*/
+
+/*<asyxml><struct signature="coordsys"><code></asyxml>*/
+struct coordsys
+{/*<asyxml></code><documentation>This structure represents a coordinate system in the plane.</documentation></asyxml>*/
+ /*<asyxml><method type = "pair" signature="relativetodefault(pair)"><code></asyxml>*/
+ restricted convert relativetodefault = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to this coordinate system to
+ the pair relatively to the default coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type = "pair" signature="defaulttorelativet(pair)"><code></asyxml>*/
+ restricted convert defaulttorelative = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to the default coordinate system to
+ the pair relatively to this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type = "real" signature="dot(pair,pair)"><code></asyxml>*/
+ restricted dot dot = new real(pair m, pair n){return dot(m, n);};/*<asyxml></code><documentation>Return the dot product of this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type = "real" signature="abs(pair)"><code></asyxml>*/
+ restricted abs abs = new real(pair m){return abs(m);};/*<asyxml></code><documentation>Return the modulus of a pair in this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type = "pair" signature="polar(real,real)"><code></asyxml>*/
+ restricted polar polar = new pair(real r, real a){return (r * cos(a), r * sin(a));};/*<asyxml></code><documentation>Polar coordinates routine of this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><property type = "pair" signature="O,i,j"><code></asyxml>*/
+ restricted pair O = (0, 0), i = (1, 0), j = (0, 1);/*<asyxml></code><documentation>Origin and units vector.</documentation></property></asyxml>*/
+ /*<asyxml><method type = "void" signature="init(convert,convert,polar,dot)"><code></asyxml>*/
+ void init(convert rtd, convert dtr,
+ polar polar, dot dot)
+ {/*<asyxml></code><documentation>The default constructor of the coordinate system.</documentation></method></asyxml>*/
+ this.relativetodefault = rtd;
+ this.defaulttorelative = dtr;
+ this.polar = polar;
+ this.dot = dot;
+ this.abs = new real(pair m){return sqrt(dot(m, m));};;
+ this.O = rtd((0, 0));
+ this.i = rtd((1, 0)) - O;
+ this.j = rtd((0, 1)) - O;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><operator type = "bool" signature="==(coordsys,coordsys)"><code></asyxml>*/
+bool operator ==(coordsys c1, coordsys c2)
+{/*<asyxml></code><documentation>Return true iff the coordinate system have the same origin and units vector.</documentation></operator></asyxml>*/
+ return c1.O == c2.O && c1.i == c2.i && c1.j == c2.j;
+}
+
+/*<asyxml><function type="coordsys" signature="cartesiansystem(pair,pair,pair)"><code></asyxml>*/
+coordsys cartesiansystem(pair O = (0, 0), pair i, pair j)
+{/*<asyxml></code><documentation>Return the Cartesian coordinate system (O, i, j).</documentation></function></asyxml>*/
+ coordsys R;
+ real[][] P = {{0, 0}, {0, 0}};
+ real[][] iP;
+ P[0][0] = i.x;
+ P[0][1] = j.x;
+ P[1][0] = i.y;
+ P[1][1] = j.y;
+ iP = inverse(P);
+ real ni = abs(i);
+ real nj = abs(j);
+ real ij = angle(j) - angle(i);
+
+ pair rtd(pair m)
+ {
+ return O + (P[0][0] * m.x + P[0][1] * m.y, P[1][0] * m.x + P[1][1] * m.y);
+ }
+
+ pair dtr(pair m)
+ {
+ m-=O;
+ return (iP[0][0] * m.x + iP[0][1] * m.y, iP[1][0] * m.x + iP[1][1] * m.y);
+ }
+
+ pair polar(real r, real a)
+ {
+ real ca = sin(ij - a)/(ni * sin(ij));
+ real sa = sin(a)/(nj * sin(ij));
+ return r * (ca, sa);
+ }
+
+ real tdot(pair m, pair n)
+ {
+ return m.x * n.x * ni^2 + m.y * n.y * nj^2 + (m.x * n.y + n.x * m.y) * dot(i, j);
+ }
+
+ R.init(rtd, dtr, polar, tdot);
+ return R;
+}
+
+
+/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,coordsys,pen,pen,pen,pen,pen)"><code></asyxml>*/
+void show(picture pic = currentpicture, Label lo = "$O$",
+ Label li = "$\vec{\imath}$",
+ Label lj = "$\vec{\jmath}$",
+ coordsys R,
+ pen dotpen = currentpen, pen xpen = currentpen, pen ypen = xpen,
+ pen ipen = red,
+ pen jpen = ipen,
+ arrowbar arrow = Arrow)
+{/*<asyxml></code><documentation>Draw the components (O, i, j, x - axis, y - axis) of 'R'.</documentation></function></asyxml>*/
+ unravel R;
+ dot(pic, O, dotpen);
+ drawline(pic, O, O + i, xpen);
+ drawline(pic, O, O + j, ypen);
+ draw(pic, li, O--(O + i), ipen, arrow);
+ Label lj = lj.copy();
+ lj.align(lj.align, unit(I * j));
+ draw(pic, lj, O--(O + j), jpen, arrow);
+ draw(pic, lj, O--(O + j), jpen, arrow);
+ Label lo = lo.copy();
+ lo.align(lo.align, -2 * dir(O--O + i, O--O + j));
+ lo.p(dotpen);
+ label(pic, lo, O);
+}
+
+/*<asyxml><operator type = "pair" signature="/(pair,coordsys)"><code></asyxml>*/
+pair operator /(pair p, coordsys R)
+{/*<asyxml></code><documentation>Return the xy - coordinates of 'p' relatively to
+ the coordinate system 'R'.
+ For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), (0, 0)/R is (-1, -2).</documentation></operator></asyxml>*/
+ return R.defaulttorelative(p);
+}
+
+/*<asyxml><operator type = "pair" signature="*(coordsys,pair)"><code></asyxml>*/
+pair operator *(coordsys R, pair p)
+{/*<asyxml></code><documentation>Return the coordinates of 'p' given in the
+ xy - coordinates 'R'.
+ For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), R * (0, 0) is (1, 2).</documentation></operator></asyxml>*/
+ return R.relativetodefault(p);
+}
+
+/*<asyxml><operator type = "path" signature="*(coordsys,path)"><code></asyxml>*/
+path operator *(coordsys R, path g)
+{/*<asyxml></code><documentation>Return the reconstructed path applying R * pair to each node, pre and post control point of 'g'.</documentation></operator></asyxml>*/
+ guide og = R * point(g, 0);
+ real l = length(g);
+ for(int i = 1; i <= l; ++i)
+ {
+ pair P = R * point(g, i);
+ pair post = R * postcontrol(g, i - 1);
+ pair pre = R * precontrol(g, i);
+ if(i == l && (cyclic(g)))
+ og = og..controls post and pre..cycle;
+ else
+ og = og..controls post and pre..P;
+ }
+ return og;
+}
+
+/*<asyxml><operator type = "coordsys" signature="*(transform,coordsys)"><code></asyxml>*/
+coordsys operator *(transform t,coordsys R)
+{/*<asyxml></code><documentation>Provide transform * coordsys.
+ Note that shiftless(t) is applied to R.i and R.j.</documentation></operator></asyxml>*/
+ coordsys oc;
+ oc = cartesiansystem(t * R.O, shiftless(t) * R.i, shiftless(t) * R.j);
+ return oc;
+}
+
+/*<asyxml><constant type = "coordsys" signature="defaultcoordsys"><code></asyxml>*/
+restricted coordsys defaultcoordsys = cartesiansystem(0, (1, 0), (0, 1));/*<asyxml></code><documentation>One can always refer to the default coordinate system using this constant.</documentation></constant></asyxml>*/
+/*<asyxml><variable type="coordsys" signature="currentcoordsys"><code></asyxml>*/
+coordsys currentcoordsys = defaultcoordsys;/*<asyxml></code><documentation>The coordinate system used by default.</documentation></variable></asyxml>*/
+
+/*<asyxml><struct signature="point"><code></asyxml>*/
+struct point
+{/*<asyxml></code><documentation>This structure replaces the pair to embed its coordinate system.
+ For example, if 'P = point(cartesiansystem((1, 2), i, j), (0, 0))',
+ P is equal to the pair (1, 2).</documentation></asyxml>*/
+ /*<asyxml><property type = "coordsys" signature="coordsys"><code></asyxml>*/
+ coordsys coordsys;/*<asyxml></code><documentation>The coordinate system of this point.</documentation></property><property type = "pair" signature="coordinates"><code></asyxml>*/
+ restricted pair coordinates;/*<asyxml></code><documentation>The coordinates of this point relatively to the coordinate system 'coordsys'.</documentation></property><property type = "real" signature="x, y"><code></asyxml>*/
+ restricted real x, y;/*<asyxml></code><documentation>The xpart and the ypart of 'coordinates'.</documentation></property></asyxml>*/
+ /*<asyxml><method type = "" signature="init(coordsys,pair)"><code><property type = "real" signature="m"><code></asyxml>*/
+ real m = 1;/*<asyxml></code><documentation>Used to cast mass<->point.</documentation></property></asyxml>*/
+ void init(coordsys R, pair coordinates, real mass)
+ {/*<asyxml></code><documentation>The constructor.</documentation></method></asyxml>*/
+ this.coordsys = R;
+ this.coordinates = coordinates;
+ this.x = coordinates.x;
+ this.y = coordinates.y;
+ this.m = mass;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="point" signature="point(coordsys,pair,real)"><code></asyxml>*/
+point point(coordsys R, pair p, real m = 1)
+{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the
+ coordinate system 'R' and the mass 'm'.</documentation></function></asyxml>*/
+ point op;
+ op.init(R, p, m);
+ return op;
+}
+
+/*<asyxml><function type="point" signature="point(explicit pair,real)"><code></asyxml>*/
+point point(explicit pair p, real m)
+{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the current
+ coordinate system and the mass 'm'.</documentation></function></asyxml>*/
+ point op;
+ op.init(currentcoordsys, p, m);
+ return op;
+}
+
+/*<asyxml><function type="point" signature="point(coordsys,explicit point,real)"><code></asyxml>*/
+point point(coordsys R, explicit point M, real m = M.m)
+{/*<asyxml></code><documentation>Return the point of 'R' which has the coordinates of 'M' and the mass 'm'.
+ Do not confuse this routine with the further routine 'changecoordsys'.</documentation></function></asyxml>*/
+ point op;
+ op.init(R, M.coordinates, M.m);
+ return op;
+}
+
+/*<asyxml><function type="point" signature="changecoordsys(coordsys,point)"><code></asyxml>*/
+point changecoordsys(coordsys R, point M)
+{/*<asyxml></code><documentation>Return the point 'M' in the coordinate system 'coordsys'.
+ In other words, the returned point marks the same plot as 'M' does.</documentation></function></asyxml>*/
+ point op;
+ coordsys mco = M.coordsys;
+ op.init(R, R.defaulttorelative(mco.relativetodefault(M.coordinates)), M.m);
+ return op;
+}
+
+/*<asyxml><function type="pair" signature="pair coordinates(point)"><code></asyxml>*/
+pair coordinates(point M)
+{/*<asyxml></code><documentation>Return the coordinates of 'M' in its coordinate system.</documentation></function></asyxml>*/
+ return M.coordinates;
+}
+
+/*<asyxml><function type="bool" signature="bool samecoordsys(bool...point[])"><code></asyxml>*/
+bool samecoordsys(bool warn = true ... point[] M)
+{/*<asyxml></code><documentation>Return true iff all the points have the same coordinate system.
+ If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/
+ bool ret = true;
+ coordsys t = M[0].coordsys;
+ for (int i = 1; i < M.length; ++i) {
+ ret = (t == M[i].coordsys);
+ if(!ret) break;
+ t = M[i].coordsys;
+ }
+ if(warn && !ret)
+ warning("coodinatesystem",
+ "the coordinate system of two objects are not the same.
+The operation will be done relative to the default coordinate system.");
+ return ret;
+}
+
+/*<asyxml><function type="point[]" signature="standardizecoordsys(coordsys,bool...point[])"><code></asyxml>*/
+point[] standardizecoordsys(coordsys R = currentcoordsys,
+ bool warn = true ... point[] M)
+{/*<asyxml></code><documentation>Return the points with the same coordinate system 'R'.
+ If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/
+ point[] op = new point[];
+ op = M;
+ if(!samecoordsys(warn ... M))
+ for (int i = 1; i < M.length; ++i)
+ op[i] = changecoordsys(R, M[i]);
+ return op;
+}
+
+/*<asyxml><operator type = "pair" signature="cast(point)"><code></asyxml>*/
+pair operator cast(point P)
+{/*<asyxml></code><documentation>Cast point to pair.</documentation></operator></asyxml>*/
+ return P.coordsys.relativetodefault(P.coordinates);
+}
+
+/*<asyxml><operator type = "pair[]" signature="cast(point[])"><code></asyxml>*/
+pair[] operator cast(point[] P)
+{/*<asyxml></code><documentation>Cast point[] to pair[].</documentation></operator></asyxml>*/
+ pair[] op;
+ for (int i = 0; i < P.length; ++i) {
+ op.push((pair)P[i]);
+ }
+ return op;
+}
+
+/*<asyxml><operator type = "point" signature="cast(pair)"><code></asyxml>*/
+point operator cast(pair p)
+{/*<asyxml></code><documentation>Cast pair to point relatively to the current coordinate
+ system 'currentcoordsys'.</documentation></operator></asyxml>*/
+ return point(currentcoordsys, p);
+}
+
+/*<asyxml><operator type = "point[]" signature="cast(pair[])"><code></asyxml>*/
+point[] operator cast(pair[] p)
+{/*<asyxml></code><documentation>Cast pair[] to point[] relatively to the current coordinate
+ system 'currentcoordsys'.</documentation></operator></asyxml>*/
+ pair[] op;
+ for (int i = 0; i < p.length; ++i) {
+ op.push((point)p[i]);
+ }
+ return op;
+}
+
+/*<asyxml><function type="pair" signature="locate(point)"><code></asyxml>*/
+pair locate(point P)
+{/*<asyxml></code><documentation>Return the coordinates of 'P' in the default coordinate system.</documentation></function></asyxml>*/
+ return P.coordsys * P.coordinates;
+}
+
+/*<asyxml><function type="point" signature="locate(pair)"><code></asyxml>*/
+point locate(pair p)
+{/*<asyxml></code><documentation>Return the point in the current coordinate system 'currentcoordsys'.</documentation></function></asyxml>*/
+ return p; //automatic casting 'pair to point'.
+}
+
+/*<asyxml><operator type = "point" signature="*(real,explicit point)"><code></asyxml>*/
+point operator *(real x, explicit point P)
+{/*<asyxml></code><documentation>Multiply the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/
+ return point(P.coordsys, x * P.coordinates, P.m);
+}
+
+/*<asyxml><operator type = "point" signature="/(explicit point,real)"><code></asyxml>*/
+point operator /(explicit point P, real x)
+{/*<asyxml></code><documentation>Divide the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/
+ return point(P.coordsys, P.coordinates/x, P.m);
+}
+
+/*<asyxml><operator type = "point" signature="/(real,explicit point)"><code></asyxml>*/
+point operator /(real x, explicit point P)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return point(P.coordsys, x/P.coordinates, P.m);
+}
+
+/*<asyxml><operator type = "point" signature="-(explicit point)"><code></asyxml>*/
+point operator -(explicit point P)
+{/*<asyxml></code><documentation>-P. The mass is inchanged.</documentation></operator></asyxml>*/
+ return point(P.coordsys, -P.coordinates, P.m);
+}
+
+/*<asyxml><operator type = "point" signature="+(explicit point,explicit point)"><code></asyxml>*/
+point operator +(explicit point P1, explicit point P2)
+{/*<asyxml></code><documentation>Provide 'point + point'.
+ If the two points haven't the same coordinate system, a warning is sent and the
+ returned point has the default coordinate system 'defaultcoordsys'.
+ The masses are added.</documentation></operator></asyxml>*/
+ point[] P = standardizecoordsys(P1, P2);
+ coordsys R = P[0].coordsys;
+ return point(R, P[0].coordinates + P[1].coordinates, P1.m + P2.m);
+}
+
+/*<asyxml><operator type = "point" signature="+(explicit point,explicit pair)"><code></asyxml>*/
+point operator +(explicit point P1, explicit pair p2)
+{/*<asyxml></code><documentation>Provide 'point + pair'.
+ The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.
+ The mass is not changed.</documentation></operator></asyxml>*/
+ coordsys R = currentcoordsys;
+ return point(R, P1.coordinates + point(R, p2).coordinates, P1.m);
+}
+point operator +(explicit pair p1, explicit point p2)
+{
+ return p2 + p1;
+}
+
+/*<asyxml><operator type = "point" signature="-(explicit point,explicit point)"><code></asyxml>*/
+point operator -(explicit point P1, explicit point P2)
+{/*<asyxml></code><documentation>Provide 'point - point'.</documentation></operator></asyxml>*/
+ return P1 + (-P2);
+}
+
+/*<asyxml><operator type = "point" signature="-(explicit point,explicit pair)"><code></asyxml>*/
+point operator -(explicit point P1, explicit pair p2)
+{/*<asyxml></code><documentation>Provide 'point - pair'.
+ The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.</documentation></operator></asyxml>*/
+ return P1 + (-p2);
+}
+point operator -(explicit pair p1, explicit point P2)
+{
+ return p1 + (-P2);
+}
+
+/*<asyxml><operator type = "point" signature="*(transform,explicit point)"><code></asyxml>*/
+point operator *(transform t, explicit point P)
+{/*<asyxml></code><documentation>Provide 'transform * point'.
+ Note that the transforms scale, xscale, yscale and rotate are carried out relatively
+ the default coordinate system 'defaultcoordsys' which is not desired for point
+ defined in an other coordinate system.
+ On can use scale(real, point), xscale(real, point), yscale(real, point), rotate(real, point),
+ scaleO(real), xscaleO(real), yscaleO(real) and rotateO(real) (described further)
+ to change the coordinate system of reference.</documentation></operator></asyxml>*/
+ coordsys R = P.coordsys;
+ return point(R, (t * locate(P))/R, P.m);
+}
+
+/*<asyxml><operator type = "point" signature="*(explicit point,explicit point)"><code></asyxml>*/
+point operator *(explicit point P1, explicit point P2)
+{/*<asyxml></code><documentation>Provide 'point * point'.
+ The resulted mass is the mass of P2</documentation></operator></asyxml>*/
+ point[] P = standardizecoordsys(P1, P2);
+ coordsys R = P[0].coordsys;
+ return point(R, P[0].coordinates * P[1].coordinates, P2.m);
+}
+
+/*<asyxml><operator type = "point" signature="*(explicit point,explicit pair)"><code></asyxml>*/
+point operator *(explicit point P1, explicit pair p2)
+{/*<asyxml></code><documentation>Provide 'point * pair'.
+ The pair 'p2' is supposed to be the coordinates of
+ the point in the coordinates system of 'P1'.
+ 'pair * point' is also defined.</documentation></operator></asyxml>*/
+ point P = point(P1.coordsys, p2, P1.m);
+ return P1 * P;
+}
+point operator *(explicit pair p1, explicit point p2)
+{
+ return p2 * p1;
+}
+
+/*<asyxml><operator type = "bool" signature="==(explicit point,explicit point)"><code></asyxml>*/
+bool operator ==(explicit point M, explicit point N)
+{/*<asyxml></code><documentation>Provide the test 'M == N' wish returns true iff MN < EPS</documentation></operator></asyxml>*/
+ return abs(locate(M) - locate(N)) < EPS;
+}
+
+/*<asyxml><operator type = "bool" signature="!=(explicit point,explicit point)"><code></asyxml>*/
+bool operator !=(explicit point M, explicit point N)
+{/*<asyxml></code><documentation>Provide the test 'M != N' wish return true iff MN >= EPS</documentation></operator></asyxml>*/
+ return !(M == N);
+}
+
+/*<asyxml><operator type = "guide" signature="cast(point)"><code></asyxml>*/
+guide operator cast(point p)
+{/*<asyxml></code><documentation>Cast point to guide.</documentation></operator></asyxml>*/
+ return locate(p);
+}
+
+/*<asyxml><operator type = "path" signature="cast(point)"><code></asyxml>*/
+path operator cast(point p)
+{/*<asyxml></code><documentation>Cast point to path.</documentation></operator></asyxml>*/
+ return locate(p);
+}
+
+/*<asyxml><function type="void" signature="dot(picture,Label,explicit point,align,string,pen)"><code></asyxml>*/
+void dot(picture pic = currentpicture, Label L, explicit point Z,
+ align align = NoAlign,
+ string format = defaultformat, pen p = currentpen)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ Label L = L.copy();
+ L.position(locate(Z));
+ if(L.s == "") {
+ if(format == "") format = defaultformat;
+ L.s = "("+format(format, Z.x)+", "+format(format, Z.y)+")";
+ }
+ L.align(align, E);
+ L.p(p);
+ dot(pic, locate(Z), p);
+ add(pic, L);
+}
+
+/*<asyxml><function type="real" signature="abs(coordsys,pair)"><code></asyxml>*/
+real abs(coordsys R, pair m)
+{/*<asyxml></code><documentation>Return the modulus |m| in the coordinate system 'R'.</documentation></function></asyxml>*/
+ return R.abs(m);
+}
+
+/*<asyxml><function type="real" signature="abs(explicit point)"><code></asyxml>*/
+real abs(explicit point M)
+{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system.</documentation></function></asyxml>*/
+ return M.coordsys.abs(M.coordinates);
+}
+
+/*<asyxml><function type="real" signature="length(explicit point)"><code></asyxml>*/
+real length(explicit point M)
+{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system (same as 'abs').</documentation></function></asyxml>*/
+ return M.coordsys.abs(M.coordinates);
+}
+
+/*<asyxml><function type="point" signature="conj(explicit point)"><code></asyxml>*/
+point conj(explicit point M)
+{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
+ return point(M.coordsys, conj(M.coordinates), M.m);
+}
+
+/*<asyxml><function type="real" signature="degrees(explicit point,coordsys,bool)"><code></asyxml>*/
+real degrees(explicit point M, coordsys R = M.coordsys, bool warn = true)
+{/*<asyxml></code><documentation>Return the angle of M (in degrees) relatively to 'R'.</documentation></function></asyxml>*/
+ return (degrees(locate(M) - R.O, warn) - degrees(R.i))%360;
+}
+
+/*<asyxml><function type="real" signature="angle(explicit point,coordsys,bool)"><code></asyxml>*/
+real angle(explicit point M, coordsys R = M.coordsys, bool warn = true)
+{/*<asyxml></code><documentation>Return the angle of M (in radians) relatively to 'R'.</documentation></function></asyxml>*/
+ return radians(degrees(M, R, warn));
+}
+
+/*<asyxml><function type="bool" signature="finite(explicit point)"><code></asyxml>*/
+bool finite(explicit point p)
+{/*<asyxml></code><documentation>Avoid to compute 'finite((pair)(infinite_point))'.</documentation></function></asyxml>*/
+ return finite(p.coordinates);
+}
+
+/*<asyxml><function type="real" signature="dot(point,point)"><code></asyxml>*/
+real dot(point A, point B)
+{/*<asyxml></code><documentation>Return the dot product in the coordinate system of 'A'.</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A.coordsys, A, B);
+ return P[0].coordsys.dot(P[0].coordinates, P[1].coordinates);
+}
+
+/*<asyxml><function type="real" signature="dot(point,explicit pair)"><code></asyxml>*/
+real dot(point A, explicit pair B)
+{/*<asyxml></code><documentation>Return the dot product in the default coordinate system.
+ dot(explicit pair, point) is also defined.</documentation></function></asyxml>*/
+ return dot(locate(A), B);
+}
+real dot(explicit pair A, point B)
+{
+ return dot(A, locate(B));
+}
+
+/*<asyxml><function type="transforms" signature="rotateO(real)"><code></asyxml>*/
+transform rotateO(real a)
+{/*<asyxml></code><documentation>Rotation around the origin of the current coordinate system.</documentation></function></asyxml>*/
+ return rotate(a, currentcoordsys.O);
+};
+
+/*<asyxml><function type="transform" signature="projection(point,point)"><code></asyxml>*/
+transform projection(point A, point B)
+{/*<asyxml></code><documentation>Return the orthogonal projection on the line (AB).</documentation></function></asyxml>*/
+ pair dir = unit(locate(A) - locate(B));
+ pair a = locate(A);
+ real cof = dir.x * a.x + dir.y * a.y;
+ real tx = a.x - dir.x * cof;
+ real txx = dir.x^2;
+ real txy = dir.x * dir.y;
+ real ty = a.y - dir.y * cof;
+ real tyx = txy;
+ real tyy = dir.y^2;
+ transform t = (tx, ty, txx, txy, tyx, tyy);
+ return t;
+}
+
+/*<asyxml><function type="transform" signature="projection(point,point,point,point,bool)"><code></asyxml>*/
+transform projection(point A, point B, point C, point D, bool safe = false)
+{/*<asyxml></code><documentation>Return the (CD) parallel projection on (AB).
+ If 'safe = true' and (AB)//(CD) return the identity.
+ If 'safe = false' and (AB)//(CD) return an infinity scaling.</documentation></function></asyxml>*/
+ pair a = locate(A);
+ pair u = unit(locate(B) - locate(A));
+ pair v = unit(locate(D) - locate(C));
+ real c = u.x * a.y - u.y * a.x;
+ real d = (conj(u) * v).y;
+ if (abs(d) < epsgeo) {
+ return safe ? identity() : scale(infinity);
+ }
+ real tx = c * v.x/d;
+ real ty = c * v.y/d;
+ real txx = u.x * v.y/d;
+ real txy = -u.x * v.x/d;
+ real tyx = u.y * v.y/d;
+ real tyy = -u.y * v.x/d;
+ transform t = (tx, ty, txx, txy, tyx, tyy);
+ return t;
+}
+
+/*<asyxml><function type="transform" signature="scale(real,point)"><code></asyxml>*/
+transform scale(real k, point M)
+{/*<asyxml></code><documentation>Homothety.</documentation></function></asyxml>*/
+ pair P = locate(M);
+ return shift(P) * scale(k) * shift(-P);
+}
+
+/*<asyxml><function type="transform" signature="xscale(real,point)"><code></asyxml>*/
+transform xscale(real k, point M)
+{/*<asyxml></code><documentation>xscale from 'M' relatively to the x - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/
+ pair P = locate(M);
+ real a = degrees(M.coordsys.i);
+ return (shift(P) * rotate(a)) * xscale(k) * (rotate(-a) * shift(-P));
+}
+
+/*<asyxml><function type="transform" signature="yscale(real,point)"><code></asyxml>*/
+transform yscale(real k, point M)
+{/*<asyxml></code><documentation>yscale from 'M' relatively to the y - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/
+ pair P = locate(M);
+ real a = degrees(M.coordsys.j) - 90;
+ return (shift(P) * rotate(a)) * yscale(k) * (rotate(-a) * shift(-P));
+}
+
+/*<asyxml><function type="transform" signature="scale(real,point,point,point,point,bool)"><code></asyxml>*/
+transform scale(real k, point A, point B, point C, point D, bool safe = false)
+{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Affinit%C3%A9_%28math%C3%A9matiques%29"/>
+ (help me for English translation...)
+ If 'safe = true' and (AB)//(CD) return the identity.
+ If 'safe = false' and (AB)//(CD) return a infinity scaling.</documentation></function></asyxml>*/
+ pair a = locate(A);
+ pair u = unit(locate(B) - locate(A));
+ pair v = unit(locate(D) - locate(C));
+ real c = u.x * a.y - u.y * a.x;
+ real d = (conj(u) * v).y;
+ real d = (conj(u) * v).y;
+ if (abs(d) < epsgeo) {
+ return safe ? identity() : scale(infinity);
+ }
+ real tx = (1 - k) * c * v.x/d;
+ real ty = (1 - k) * c * v.y/d;
+ real txx = (1 - k) * u.x * v.y/d + k;
+ real txy = (k - 1) * u.x * v.x/d;
+ real tyx = (1 - k) * u.y * v.y/d;
+ real tyy = (k - 1) * u.y * v.x/d + k;
+ transform t = (tx, ty, txx, txy, tyx, tyy);
+ return t;
+}
+
+/*<asyxml><function type="transform" signature="scaleO(real)"><code></asyxml>*/
+transform scaleO(real x)
+{/*<asyxml></code><documentation>Homothety from the origin of the current coordinate system.</documentation></function></asyxml>*/
+ return scale(x, (0, 0));
+}
+
+/*<asyxml><function type="transform" signature="xscaleO(real)"><code></asyxml>*/
+transform xscaleO(real x)
+{/*<asyxml></code><documentation>xscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/
+ return scale(x, (0, 0), (0, 1), (0, 0), (1, 0));
+}
+
+/*<asyxml><function type="transform" signature="yscaleO(real)"><code></asyxml>*/
+transform yscaleO(real x)
+{/*<asyxml></code><documentation>yscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/
+ return scale(x, (0, 0), (1, 0), (0, 0), (0, 1));
+}
+
+/*<asyxml><struct signature="vector"><code></asyxml>*/
+struct vector
+{/*<asyxml></code><documentation>Like a point but casting to pair, adding etc does not take account
+ of the origin of the coordinate system.</documentation><property type = "point" signature="v"><code></asyxml>*/
+ point v;/*<asyxml></code><documentation>Coordinates as a point (embed coordinate system and pair).</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><operator type = "point" signature="cast(vector)"><code></asyxml>*/
+point operator cast(vector v)
+{/*<asyxml></code><documentation>Cast vector 'v' to point 'M' so that OM = v.</documentation></operator></asyxml>*/
+ return v.v;
+}
+
+/*<asyxml><operator type = "vector" signature="cast(pair)"><code></asyxml>*/
+vector operator cast(pair v)
+{/*<asyxml></code><documentation>Cast pair to vector relatively to the current coordinate
+ system 'currentcoordsys'.</documentation></operator></asyxml>*/
+ vector ov;
+ ov.v = point(currentcoordsys, v);
+ return ov;
+}
+
+/*<asyxml><operator type = "vector" signature="cast(explicit point)"><code></asyxml>*/
+vector operator cast(explicit point v)
+{/*<asyxml></code><documentation>A point can be interpreted like a vector using the code
+ '(vector)a_point'.</documentation></operator></asyxml>*/
+ vector ov;
+ ov.v = v;
+ return ov;
+}
+
+/*<asyxml><operator type = "pair" signature="cast(explicit vector)"><code></asyxml>*/
+pair operator cast(explicit vector v)
+{/*<asyxml></code><documentation>Cast vector to pair (the coordinates of 'v' in the default coordinate system).</documentation></operator></asyxml>*/
+ return locate(v.v) - v.v.coordsys.O;
+}
+
+/*<asyxml><operator type = "align" signature="cast(vector)"><code></asyxml>*/
+align operator cast(vector v)
+{/*<asyxml></code><documentation>Cast vector to align.</documentation></operator></asyxml>*/
+ return (pair)v;
+}
+
+/*<asyxml><function type="vector" signature="vector(coordsys, pair)"><code></asyxml>*/
+vector vector(coordsys R = currentcoordsys, pair v)
+{/*<asyxml></code><documentation>Return the vector of 'R' which has the coordinates 'v'.</documentation></function></asyxml>*/
+ vector ov;
+ ov.v = point(R, v);
+ return ov;
+}
+
+/*<asyxml><function type="vector" signature="vector(point)"><code></asyxml>*/
+vector vector(point M)
+{/*<asyxml></code><documentation>Return the vector OM, where O is the origin of the coordinate system of 'M'.
+ Useful to write 'vector(P - M);' instead of '(vector)(P - M)'.</documentation></function></asyxml>*/
+ return M;
+}
+
+/*<asyxml><function type="point" signature="point(explicit vector)"><code></asyxml>*/
+point point(explicit vector u)
+{/*<asyxml></code><documentation>Return the point M so that OM = u, where O is the origin of the coordinate system of 'u'.</documentation></function></asyxml>*/
+ return u.v;
+}
+
+/*<asyxml><function type="pair" signature="locate(explicit vector)"><code></asyxml>*/
+pair locate(explicit vector v)
+{/*<asyxml></code><documentation>Return the coordinates of 'v' in the default coordinate system (like casting vector to pair).</documentation></function></asyxml>*/
+ return (pair)v;
+}
+
+/*<asyxml><function type="void" signature="show(Label,pen,arrowbar)"><code></asyxml>*/
+void show(Label L, vector v, pen p = currentpen, arrowbar arrow = Arrow)
+{/*<asyxml></code><documentation>Draw the vector v (from the origin of its coordinate system).</documentation></function></asyxml>*/
+ coordsys R = v.v.coordsys;
+ draw(L, R.O--v.v, p, arrow);
+}
+
+/*<asyxml><function type="vector" signature="changecoordsys(coordsys,vector)"><code></asyxml>*/
+vector changecoordsys(coordsys R, vector v)
+{/*<asyxml></code><documentation>Return the vector 'v' relatively to coordinate system 'R'.</documentation></function></asyxml>*/
+ vector ov;
+ ov.v = point(R, (locate(v) + R.O)/R);
+ return ov;
+}
+
+/*<asyxml><operator type = "vector" signature="*(real,explicit vector)"><code></asyxml>*/
+vector operator *(real x, explicit vector v)
+{/*<asyxml></code><documentation>Provide real * vector.</documentation></operator></asyxml>*/
+ return x * v.v;
+}
+
+/*<asyxml><operator type = "vector" signature="/(explicit vector,real)"><code></asyxml>*/
+vector operator /(explicit vector v, real x)
+{/*<asyxml></code><documentation>Provide vector/real</documentation></operator></asyxml>*/
+ return v.v/x;
+}
+
+/*<asyxml><operator type = "vector" signature="*(transform t,explicit vector)"><code></asyxml>*/
+vector operator *(transform t, explicit vector v)
+{/*<asyxml></code><documentation>Provide transform * vector.</documentation></operator></asyxml>*/
+ return t * v.v;
+}
+
+/*<asyxml><operator type = "vector" signature="*(explicit point,explicit vector)"><code></asyxml>*/
+vector operator *(explicit point M, explicit vector v)
+{/*<asyxml></code><documentation>Provide point * vector</documentation></operator></asyxml>*/
+ return M * v.v;
+}
+
+/*<asyxml><operator type = "point" signature="+(explicit point,explicit vector)"><code></asyxml>*/
+point operator +(point M, explicit vector v)
+{/*<asyxml></code><documentation>Return 'M' shifted by 'v'.</documentation></operator></asyxml>*/
+ return shift(locate(v)) * M;
+}
+
+/*<asyxml><operator type = "point" signature="-(explicit point,explicit vector)"><code></asyxml>*/
+point operator -(point M, explicit vector v)
+{/*<asyxml></code><documentation>Return 'M' shifted by '-v'.</documentation></operator></asyxml>*/
+ return shift(-locate(v)) * M;
+}
+
+/*<asyxml><operator type = "vector" signature="-(explicit vector)"><code></asyxml>*/
+vector operator -(explicit vector v)
+{/*<asyxml></code><documentation>Provide -v.</documentation></operator></asyxml>*/
+ return -v.v;
+}
+
+/*<asyxml><operator type = "point" signature="+(explicit pair,explicit vector)"><code></asyxml>*/
+point operator +(explicit pair m, explicit vector v)
+{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of
+ a point in the current coordinates system 'currentcoordsys'.
+ Return this point shifted by the vector 'v'.</documentation></operator></asyxml>*/
+ return locate(m) + v;
+}
+
+/*<asyxml><operator type = "point" signature="-(explicit pair,explicit vector)"><code></asyxml>*/
+point operator -(explicit pair m, explicit vector v)
+{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of
+ a point in the current coordinates system 'currentcoordsys'.
+ Return this point shifted by the vector '-v'.</documentation></operator></asyxml>*/
+ return m + (-v);
+}
+
+/*<asyxml><operator type = "vector" signature="+(explicit vector,explicit vector)"><code></asyxml>*/
+vector operator +(explicit vector v1, explicit vector v2)
+{/*<asyxml></code><documentation>Provide vector + vector.
+ If the two vector haven't the same coordinate system, the returned
+ vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/
+ coordsys R = v1.v.coordsys;
+ if(samecoordsys(false, v1, v2)){R = defaultcoordsys;}
+ return vector(R, (locate(v1) + locate(v2))/R);
+}
+
+/*<asyxml><operator type = "vector" signature="-(explicit vector, explicit vector)"><code></asyxml>*/
+vector operator -(explicit vector v1, explicit vector v2)
+{/*<asyxml></code><documentation>Provide vector - vector.
+ If the two vector haven't the same coordinate system, the returned
+ vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/
+ return v1 + (-v2);
+}
+
+/*<asyxml><operator type = "bool" signature="==(explicit vector,explicit vector)"><code></asyxml>*/
+bool operator ==(explicit vector u, explicit vector v)
+{/*<asyxml></code><documentation>Return true iff |u - v|<EPS.</documentation></operator></asyxml>*/
+ return abs(u - v) < EPS;
+}
+
+/*<asyxml><function type="bool" signature="collinear(vector,vector)"><code></asyxml>*/
+bool collinear(vector u, vector v)
+{/*<asyxml></code><documentation>Return 'true' iff the vectors 'u' and 'v' are collinear.</documentation></function></asyxml>*/
+ return abs(ypart((conj((pair)u) * (pair)v))) < EPS;
+}
+
+/*<asyxml><function type="vector" signature="unit(point)"><code></asyxml>*/
+vector unit(point M)
+{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/
+ return M/abs(M);
+}
+
+/*<asyxml><function type="vector" signature="unit(vector)"><code></asyxml>*/
+vector unit(vector u)
+{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/
+ return u.v/abs(u.v);
+}
+
+/*<asyxml><function type="real" signature="degrees(vector,coordsys,bool)"><code></asyxml>*/
+real degrees(vector v,
+ coordsys R = v.v.coordsys,
+ bool warn = true)
+{/*<asyxml></code><documentation>Return the angle of 'v' (in degrees) relatively to 'R'.</documentation></function></asyxml>*/
+ return (degrees(locate(v), warn) - degrees(R.i))%360;
+}
+
+/*<asyxml><function type="real" signature="angle(vector,coordsys,bool)"><code></asyxml>*/
+real angle(explicit vector v,
+ coordsys R = v.v.coordsys,
+ bool warn = true)
+{/*<asyxml></code><documentation>Return the angle of 'v' (in radians) relatively to 'R'.</documentation></function></asyxml>*/
+ return radians(degrees(v, R, warn));
+}
+
+/*<asyxml><function type="vector" signature="conj(explicit vector)"><code></asyxml>*/
+vector conj(explicit vector u)
+{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
+ return conj(u.v);
+}
+
+/*<asyxml><function type="transform" signature="rotate(explicit vector)"><code></asyxml>*/
+transform rotate(explicit vector dir)
+{/*<asyxml></code><documentation>A rotation in the direction 'dir' limited to [-90, 90]
+ This is useful for rotating text along a line in the direction dir.
+ rotate(explicit point dir) is also defined.
+ </documentation></function></asyxml>*/
+ return rotate(locate(dir));
+}
+transform rotate(explicit point dir){return rotate(locate(vector(dir)));}
+// *......................COORDINATES......................*
+// *=======================================================*
+
+// *=======================================================*
+// *.........................BASES.........................*
+/*<asyxml><variable type="point" signature="origin"><code></asyxml>*/
+point origin = point(defaultcoordsys, (0, 0));/*<asyxml></code><documentation>The origin of the current coordinate system.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="point" signature="origin(coordsys)"><code></asyxml>*/
+point origin(coordsys R = currentcoordsys)
+{/*<asyxml></code><documentation>Return the origin of the coordinate system 'R'.</documentation></function></asyxml>*/
+ return point(R, (0, 0)); //use automatic casting;
+}
+
+/*<asyxml><variable type="real" signature="linemargin"><code></asyxml>*/
+real linemargin = 0;/*<asyxml></code><documentation>Margin used to draw lines.</documentation></variable></asyxml>*/
+/*<asyxml><function type="real" signature="linemargin()"><code></asyxml>*/
+real linemargin()
+{/*<asyxml></code><documentation>Return the margin used to draw lines.</documentation></function></asyxml>*/
+ return linemargin;
+}
+
+/*<asyxml><variable type="pen" signature="addpenline"><code></asyxml>*/
+pen addpenline = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of "finish" lines.</documentation></variable></asyxml>*/
+pen addpenline(pen p) {
+ return addpenline + p;
+}
+
+/*<asyxml><variable type="pen" signature="addpenarc"><code></asyxml>*/
+pen addpenarc = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of arcs.</documentation></variable></asyxml>*/
+pen addpenarc(pen p) {return addpenarc + p;}
+
+/*<asyxml><variable type="string" signature="defaultmassformat"><code></asyxml>*/
+string defaultmassformat = "$\left(%L;%.4g\right)$";/*<asyxml></code><documentation>Format used to construct the default label of masses.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="int" signature="sgnd(real)"><code></asyxml>*/
+int sgnd(real x)
+{/*<asyxml></code><documentation>Return the -1 if x < 0, 1 if x >= 0.</documentation></function></asyxml>*/
+ return (x == 0) ? 1 : sgn(x);
+}
+int sgnd(int x)
+{
+ return (x == 0) ? 1 : sgn(x);
+}
+
+/*<asyxml><function type="bool" signature="defined(pair)"><code></asyxml>*/
+bool defined(point P)
+{/*<asyxml></code><documentation>Return true iff the coordinates of 'P' are finite.</documentation></function></asyxml>*/
+ return finite(P.coordinates);
+}
+
+/*<asyxml><function type="bool" signature="onpath(picture,path,point,pen)"><code></asyxml>*/
+bool onpath(picture pic = currentpicture, path g, point M, pen p = currentpen)
+{/*<asyxml></code><documentation>Return true iff 'M' is on the path drawn with the pen 'p' in 'pic'.</documentation></function></asyxml>*/
+ transform t = inverse(pic.calculateTransform());
+ return intersect(g, shift(locate(M)) * scale(linewidth(p)/2) * t * unitcircle).length > 0;
+}
+
+/*<asyxml><function type="bool" signature="sameside(point,point,point)"><code></asyxml>*/
+bool sameside(point M, point N, point O)
+{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the point 'O'.</documentation></function></asyxml>*/
+ pair m = M, n = N, o = O;
+ return dot(m - o, n - o) >= -epsgeo;
+}
+
+/*<asyxml><function type="bool" signature="between(point,point,point)"><code></asyxml>*/
+bool between(point M, point O, point N)
+{/*<asyxml></code><documentation>Return 'true' iff 'O' is between 'M' and 'N'.</documentation></function></asyxml>*/
+ return (!sameside(N, M, O) || M == O || N == O);
+}
+
+
+typedef path pathModifier(path);
+pathModifier NoModifier = new path(path g){return g;};
+
+private void Drawline(picture pic = currentpicture, Label L = "", pair P, bool dirP = true, pair Q, bool dirQ = true,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None,
+ Label legend = "", marker marker = nomarker,
+ pathModifier pathModifier = NoModifier)
+{/* Add the two parameters 'dirP' and 'dirQ' to the native routine
+ 'drawline' of the module 'math'.
+ Segment [PQ] will be prolonged in direction of P if 'dirP = true', in
+ direction of Q if 'dirQ = true'.
+ If 'dirP = dirQ = true', the behavior is that of the native 'drawline'.
+ Add all the other parameters of 'Draw'.*/
+ pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
+ picture opic;
+ // Reduce the bounds by the size of the pen.
+ m -= min(p) - (linemargin(), linemargin()); M -= max(p) + (linemargin(), linemargin());
+
+ // Calculate the points and direction vector in the transformed space.
+ t = t * T;
+ pair z = t * P;
+ pair q = t * Q;
+ pair v = q - z;
+ // path g;
+ pair ptp, ptq;
+ real cp = dirP ? 1:0;
+ real cq = dirQ ? 1:0;
+ // Handle horizontal and vertical lines.
+ if(v.x == 0) {
+ if(m.x <= z.x && z.x <= M.x)
+ if (dot(v, m - z) < 0) {
+ ptp = (z.x, z.y + cp * (m.y - z.y));
+ ptq = (z.x, q.y + cq * (M.y - q.y));
+ } else {
+ ptq = (z.x, q.y + cq * (m.y - q.y));
+ ptp = (z.x, z.y + cp * (M.y - z.y));
+ }
+ } else if(v.y == 0) {
+ if (dot(v, m - z) < 0) {
+ ptp = (z.x + cp * (m.x - z.x), z.y);
+ ptq = (q.x + cq * (M.x - q.x), z.y);
+ } else {
+ ptq = (q.x + cq * (m.x - q.x), z.y);
+ ptp = (z.x + cp * (M.x - z.x), z.y);
+ }
+ } else {
+ // Calculate the maximum and minimum t values allowed for the
+ // parametric equation z + t * v
+ real mx = (m.x - z.x)/v.x, Mx = (M.x - z.x)/v.x;
+ real my = (m.y - z.y)/v.y, My = (M.y - z.y)/v.y;
+ real tmin = max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My);
+ real tmax = min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my);
+ pair pmin = z + tmin * v;
+ pair pmax = z + tmax * v;
+ if(tmin <= tmax) {
+ ptp = z + cp * tmin * v;
+ ptq = z + (cq == 0 ? v:tmax * v);
+ }
+ }
+ path g = ptp--ptq;
+ if (length(g)>0)
+ {
+ if(L.s != "") {
+ Label lL = L.copy();
+ if(L.defaultposition) lL.position(Relative(.9));
+ lL.p(p);
+ lL.out(opic, g);
+ }
+ g = pathModifier(g);
+ if(linetype(p).length == 0){
+ pair m = midpoint(g);
+ pen tp;
+ tp = dirP ? p : addpenline(p);
+ draw(opic, pathModifier(m--ptp), tp);
+ tp = dirQ ? p : addpenline(p);
+ draw(opic, pathModifier(m--ptq), tp);
+ } else {
+ draw(opic, g, p);
+ }
+ marker.markroutine(opic, marker.f, g);
+ arrow(opic, g, p, NoMargin);
+ add(f, opic.fit());
+ }
+ });
+}
+
+/*<asyxml><function type="void" signature="clipdraw(picture,Label,path,align,pen,arrowbar,arrowbar,real,real,Label,marker)"><code></asyxml>*/
+void clipdraw(picture pic = currentpicture, Label L = "", path g,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None,
+ real xmargin = 0, real ymargin = xmargin,
+ Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation>Draw the path 'g' on 'pic' clipped to the bounding box of 'pic'.</documentation></function></asyxml>*/
+ if(L.s != "") {
+ picture tmp;
+ label(tmp, L, g, p);
+ add(pic, tmp);
+ }
+ pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
+ // Reduce the bounds by the size of the pen and the margins.
+ m += min(p) + (xmargin, ymargin); M -= max(p) + (xmargin, ymargin);
+ path bound = box(m, M);
+ picture tmp;
+ draw(tmp, "", t * T * g, align, p, arrow, bar, NoMargin, legend, marker);
+ clip(tmp, bound);
+ add(f, tmp.fit());
+ });
+}
+
+/*<asyxml><function type="void" signature="distance(picture pic,Label,point,point,bool,real,pen,pen,arrow)"><code></asyxml>*/
+void distance(picture pic = currentpicture, Label L = "", point A, point B,
+ bool rotated = true, real offset = 3mm,
+ pen p = currentpen, pen joinpen = invisible,
+ arrowbar arrow = Arrows(NoFill))
+{/*<asyxml></code><documentation>Draw arrow between A and B (from FAQ).</documentation></function></asyxml>*/
+ pair A = A, B = B;
+ path g = A--B;
+ transform Tp = shift(-offset * unit(B - A) * I);
+ pic.add(new void(frame f, transform t) {
+ picture opic;
+ path G = Tp * t * g;
+ transform id = identity();
+ transform T = rotated ? rotate(B - A) : id;
+ Label L = L.copy();
+ L.align(L.align, Center);
+ if(abs(ypart((conj(A - B) * L.align.dir))) < epsgeo && L.filltype == NoFill)
+ L.filltype = UnFill(1);
+ draw(opic, T * L, G, p, arrow, Bars, PenMargins);
+ pair Ap = t * A, Bp = t * B;
+ draw(opic, (Ap--Tp * Ap)^^(Bp--Tp * Bp), joinpen);
+ add(f, opic.fit());
+ }, true);
+ pic.addBox(min(g), max(g), Tp * min(p), Tp * max(p));
+}
+
+/*<asyxml><variable type="real" signature="perpfactor"><code></asyxml>*/
+real perpfactor = 1;/*<asyxml></code><documentation>Factor for drawing perpendicular symbol.</documentation></variable></asyxml>*/
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,explicit pair,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic = currentpicture, point z,
+ explicit pair align,
+ explicit pair dir = E, real size = 0,
+ pen p = currentpen,
+ margin margin = NoMargin,
+ filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z + dir.
+ dir(45 + n * 90), where n in N*, are common values for 'align'.</documentation></function></asyxml>*/
+ p = squarecap + p;
+ if(size == 0) size = perpfactor * 3mm + sqrt(1 + linewidth(p)) - 1;
+ frame apic;
+ pair d1 = size * align * unit(dir) * dir(-45);
+ pair d2 = I * d1;
+ path g = d1--d1 + d2--d2;
+ g = margin(g, p).g;
+ draw(apic, g, p);
+ if(filltype != NoFill) filltype.fill(apic, (relpoint(g, 0) - relpoint(g, 0.5)+
+ relpoint(g, 1))--g--cycle, p + solid);
+ add(pic, apic, locate(z));
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,vector,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic = currentpicture, point z,
+ vector align,
+ vector dir = E, real size = 0,
+ pen p = currentpen,
+ margin margin = NoMargin,
+ filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z + dir.
+ dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
+ perpendicularmark(pic, z, (pair)align, (pair)dir, size,
+ p, margin, filltype);
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,path,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic = currentpicture, point z, explicit pair align, path g,
+ real size = 0, pen p = currentpen,
+ margin margin = NoMargin,
+ filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z + dir(g, 0).
+ dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
+ perpendicularmark(pic, z, align, dir(g, 0), size, p, margin, filltype);
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,path,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic = currentpicture, point z, vector align, path g,
+ real size = 0, pen p = currentpen,
+ margin margin = NoMargin,
+ filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z + dir(g, 0).
+ dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
+ perpendicularmark(pic, z, (pair)align, dir(g, 0), size, p, margin, filltype);
+}
+
+/*<asyxml><function type="void" signature="markrightangle(picture,point,point,point,real,pen,margin,filltype)"><code></asyxml>*/
+void markrightangle(picture pic = currentpicture, point A, point O,
+ point B, real size = 0, pen p = currentpen,
+ margin margin = NoMargin,
+ filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Mark the angle AOB with a perpendicular symbol.</documentation></function></asyxml>*/
+ pair Ap = A, Bp = B, Op = O;
+ pair dir = Ap - Op;
+ real a1 = degrees(dir);
+ pair align = rotate(-a1) * unit(dir(Op--Ap, Op--Bp));
+ if (margin == NoMargin)
+ margin = TrueMargin(linewidth(currentpen)/2, linewidth(currentpen)/2);
+ perpendicularmark(pic = pic, z = O, align = align,
+ dir = dir, size = size, p = p,
+ margin = margin, filltype = filltype);
+}
+
+/*<asyxml><function type="bool" signature="simeq(point,point,real)"><code></asyxml>*/
+bool simeq(point A, point B, real fuzz = epsgeo)
+{/*<asyxml></code><documentation>Return true iff abs(A - B) < fuzz.
+ This routine is used internally to know if two points are equal, in particular by the operator == in 'point == point'.</documentation></function></asyxml>*/
+ return (abs(A - B) < fuzz);
+}
+bool simeq(point a, real b, real fuzz = epsgeo)
+{
+ coordsys R = a.coordsys;
+ return (abs(a - point(R, ((pair)b)/R)) < fuzz);
+}
+
+/*<asyxml><function type="pair" signature="attract(pair,path,real)"><code></asyxml>*/
+pair attract(pair m, path g, real fuzz = 0)
+{/*<asyxml></code><documentation>Return the nearest point (A PAIR) of 'm' which is on the path g.
+ 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
+ if(intersect(m, g, fuzz).length > 0) return m;
+ pair p;
+ real step = 1, r = 0;
+ real[] t;
+ static real eps = sqrt(realEpsilon);
+ do {// Find a radius for intersection
+ r += step;
+ t = intersect(shift(m) * scale(r) * unitcircle, g);
+ } while(t.length <= 0);
+ p = point(g, t[1]);
+ real rm = 0, rM = r;
+ while(rM - rm > eps) {
+ r = (rm + rM)/2;
+ t = intersect(shift(m) * scale(r) * unitcircle, g, fuzz);
+ if(t.length <= 0) {
+ rm = r;
+ } else {
+ rM = r;
+ p = point(g, t[1]);
+ }
+ }
+ return p;
+}
+
+/*<asyxml><function type="point" signature="attract(point,path,real)"><code></asyxml>*/
+point attract(point M, path g, real fuzz = 0)
+{/*<asyxml></code><documentation>Return the nearest point (A POINT) of 'M' which is on the path g.
+ 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
+ return point(M.coordsys, attract(locate(M), g)/M.coordsys);
+}
+
+/*<asyxml><function type="real[]" signature="intersect(path,explicit pair)"><code></asyxml>*/
+real[] intersect(path g, explicit pair p, real fuzz = 0)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ fuzz = fuzz <= 0 ? sqrt(realEpsilon) : fuzz;
+ real[] or;
+ real r = realEpsilon;
+ do{
+ or = intersect(g, shift(p) * scale(r) * unitcircle, fuzz);
+ r *= 2;
+ } while(or.length == 0);
+ return or;
+}
+
+/*<asyxml><function type="real[]" signature="intersect(path,explicit point)"><code></asyxml>*/
+real[] intersect(path g, explicit point P, real fuzz = epsgeo)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersect(g, locate(P), fuzz);
+}
+// *.........................BASES.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.........................LINES.........................*
+/*<asyxml><struct signature="line"><code></asyxml>*/
+struct line
+{/*<asyxml></code><documentation>This structure provides the objects line, semi - line and segment oriented from A to B.
+ All the calculus with this structure will be as exact as Asymptote can do.
+ For a full precision, you must not cast 'line' to 'path' excepted for drawing routines.</documentation></asyxml>*/
+ /*<asyxml><property type = "point" signature="A,B"><code></asyxml>*/
+ restricted point A,B;/*<asyxml></code><documentation>Two line's points with same coordinate system.</documentation></property><property type = "bool" signature="extendA,extendB"><code></asyxml>*/
+ bool extendA,extendB;/*<asyxml></code><documentation>If true,extend 'l' in direction of A (resp. B).</documentation></property><property type = "vector" signature="u,v"><code></asyxml>*/
+ restricted vector u,v;/*<asyxml></code><documentation>u = unit(AB) = direction vector,v = normal vector.</documentation></property><property type = "real" signature="a,b,c"><code></asyxml>*/
+ restricted real a,b,c;/*<asyxml></code><documentation>Coefficients of the equation ax + by + c = 0 in the coordinate system of 'A'.</documentation></property><property type = "real" signature="slope,origin"><code></asyxml>*/
+ restricted real slope, origin;/*<asyxml></code><documentation>Slope and ordinate at the origin.</documentation></property></asyxml>*/
+ /*<asyxml><method type = "line" signature="copy()"><code></asyxml>*/
+ line copy()
+ {/*<asyxml></code><documentation>Copy a line in a new instance.</documentation></method></asyxml>*/
+ line l = new line;
+ l.A = A;
+ l.B = B;
+ l.a = a;
+ l.b = b;
+ l.c = c;
+ l.slope = slope;
+ l.origin = origin;
+ l.u = u;
+ l.v = v;
+ l.extendA = extendA;
+ l.extendB = extendB;
+ return l;
+ }
+
+ /*<asyxml><method type = "void" signature="init(point,bool,point,bool)"><code></asyxml>*/
+ void init(point A, bool extendA = true, point B, bool extendB = true)
+ {/*<asyxml></code><documentation>Initialize line.
+ If 'extendA' is true, the "line" is infinite in the direction of A.</documentation></method></asyxml>*/
+ point[] P = standardizecoordsys(A, B);
+ this.A = P[0];
+ this.B = P[1];
+ this.a = B.y - A.y;
+ this.b = A.x - B.x;
+ this.c = A.y * B.x - A.x * B.y;
+ this.slope= (this.b == 0) ? infinity : -this.a/this.b;
+ this.origin = (this.b == 0) ? (this.c == 0) ? 0:infinity : -this.c/this.b;
+ this.u = unit(P[1]-P[0]);
+ // int tmp = sgnd(this.slope);
+ // this.u = (dot((pair)this.u, N) >= 0) ? tmp * this.u : -tmp * this.u;
+ this.v = rotate(90, point(P[0].coordsys, (0, 0))) * this.u;
+ this.extendA = extendA;
+ this.extendB = extendB;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="line" signature="line(point,bool,point,bool)"><code></asyxml>*/
+line line(point A, bool extendA = true, point B, bool extendB = true)
+{/*<asyxml></code><documentation>Return the line passing through 'A' and 'B'.
+ If 'extendA' is true, the "line" is infinite in the direction of A.
+ A "line" can be half-line or segment.</documentation></function></asyxml>*/
+ if (A == B) abort("line: the points must be distinct.");
+ line l;
+ l.init(A, extendA, B, extendB);
+ return l;
+}
+
+/*<asyxml><struct signature="segment"><code></asyxml>*/
+struct segment
+{/*<asyxml></code><documentation><look href = "struct line"/>.</documentation></asyxml>*/
+ restricted point A, B;// Extremity.
+ restricted vector u, v;// u = direction vector, v = normal vector.
+ restricted real a, b, c;// Coefficients of the équation ax + by + c = 0
+ restricted real slope, origin;
+ segment copy()
+ {
+ segment s = new segment;
+ s.A = A;
+ s.B = B;
+ s.a = a;
+ s.b = b;
+ s.c = c;
+ s.slope = slope;
+ s.origin = origin;
+ s.u = u;
+ s.v = v;
+ return s;
+ }
+
+ void init(point A, point B)
+ {
+ line l;
+ l.init(A, B);
+ this.A = l.A; this.B = l.B;
+ this.a = l.a; this.b = l.b; this.c = l.c;
+ this.slope = l.slope; this.origin = l.origin;
+ this.u = l.u; this.v = l.v;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="segment" signature="segment(point,point)"><code></asyxml>*/
+segment segment(point A, point B)
+{/*<asyxml></code><documentation>Return the segment whose the extremities are A and B.</documentation></function></asyxml>*/
+ segment s;
+ s.init(A, B);
+ return s;
+}
+
+/*<asyxml><function type="real" signature="length(segment)"><code></asyxml>*/
+real length(segment s)
+{/*<asyxml></code><documentation>Return the length of 's'.</documentation></function></asyxml>*/
+ return abs(s.A - s.B);
+}
+
+/*<asyxml><operator type = "line" signature="cast(segment)"><code></asyxml>*/
+line operator cast(segment s)
+{/*<asyxml></code><documentation>A segment is casted to a "finite line".</documentation></operator></asyxml>*/
+ return line(s.A, false, s.B, false);
+}
+
+/*<asyxml><operator type = "segment" signature="cast(line)"><code></asyxml>*/
+segment operator cast(line l)
+{/*<asyxml></code><documentation>Cast line 'l' to segment [l.A l.B].</documentation></operator></asyxml>*/
+ return segment(l.A, l.B);
+}
+
+/*<asyxml><operator type = "line" signature="*(transform,line)"><code></asyxml>*/
+line operator *(transform t, line l)
+{/*<asyxml></code><documentation>Provide transform * line</documentation></operator></asyxml>*/
+ return line(t * l.A, l.extendA, t * l.B, l.extendB);
+}
+/*<asyxml><operator type = "line" signature="/(line,real)"><code></asyxml>*/
+line operator /(line l, real x)
+{/*<asyxml></code><documentation>Provide l/x.
+ Return the line passing through l.A/x and l.B/x.</documentation></operator></asyxml>*/
+ return line(l.A/x, l.extendA, l.B/x, l.extendB);
+}
+line operator /(line l, int x){return line(l.A/x, l.B/x);}
+/*<asyxml><operator type = "line" signature="*(real,line)"><code></asyxml>*/
+line operator *(real x, line l)
+{/*<asyxml></code><documentation>Provide x * l.
+ Return the line passing through x * l.A and x * l.B.</documentation></operator></asyxml>*/
+ return line(x * l.A, l.extendA, x * l.B, l.extendB);
+}
+line operator *(int x, line l){return line(x * l.A, l.extendA, x * l.B, l.extendB);}
+
+/*<asyxml><operator type = "line" signature="*(point,line)"><code></asyxml>*/
+line operator *(point M, line l)
+{/*<asyxml></code><documentation>Provide point * line.
+ Return the line passing through unit(M) * l.A and unit(M) * l.B.</documentation></operator></asyxml>*/
+ return line(unit(M) * l.A, l.extendA, unit(M) * l.B, l.extendB);
+}
+/*<asyxml><operator type = "line" signature="+(line,point)"><code></asyxml>*/
+line operator +(line l, vector u)
+{/*<asyxml></code><documentation>Provide line + vector (and so line + point).
+ Return the line 'l' shifted by 'u'.</documentation></operator></asyxml>*/
+ return line(l.A + u, l.extendA, l.B + u, l.extendB);
+}
+/*<asyxml><operator type = "line" signature="-(line,vector)"><code></asyxml>*/
+line operator -(line l, vector u)
+{/*<asyxml></code><documentation>Provide line - vector (and so line - point).
+ Return the line 'l' shifted by '-u'.</documentation></operator></asyxml>*/
+ return line(l.A - u, l.extendA, l.B - u, l.extendB);
+}
+
+/*<asyxml><operator type = "line[]" signature="^^(line,line)"><code></asyxml>*/
+line[] operator ^^(line l1, line l2)
+{/*<asyxml></code><documentation>Provide line^^line.
+ Return the line array {l1, l2}.</documentation></operator></asyxml>*/
+ line[] ol;
+ ol.push(l1); ol.push(l2);
+ return ol;
+}
+
+/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/
+line[] operator ^^(line l1, line[] l2)
+{/*<asyxml></code><documentation>Provide line^^line[].
+ Return the line array {l1, l2[0], l2[1]...}.
+ line[]^^line is also defined.</documentation></operator></asyxml>*/
+ line[] ol;
+ ol.push(l1);
+ for (int i = 0; i < l2.length; ++i) {
+ ol.push(l2[i]);
+ }
+ return ol;
+}
+line[] operator ^^(line[] l2, line l1)
+{
+ line[] ol = l2;
+ ol.push(l1);
+ return ol;
+}
+
+/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/
+line[] operator ^^(line l1[], line[] l2)
+{/*<asyxml></code><documentation>Provide line[]^^line[].
+ Return the line array {l1[0], l1[1], ..., l2[0], l2[1], ...}.</documentation></operator></asyxml>*/
+ line[] ol = l1;
+ for (int i = 0; i < l2.length; ++i) {
+ ol.push(l2[i]);
+ }
+ return ol;
+}
+
+/*<asyxml><function type="bool" signature="sameside(point,point,line)"><code></asyxml>*/
+bool sameside(point M, point P, line l)
+{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the line (or on the line) 'l'.</documentation></function></asyxml>*/
+ pair A = l.A, B = l.B, m = M, p = P;
+ pair mil = (A + B)/2;
+ pair mA = rotate(90, mil) * A;
+ pair mB = rotate(-90, mil) * A;
+ return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB));
+ // transform proj = projection(l.A, l.B);
+ // point Mp = proj * M;
+ // point Pp = proj * P;
+ // dot(Mp);dot(Pp);
+ // return dot(locate(Mp - M), locate(Pp - P)) >= 0;
+}
+
+/*<asyxml><function type="line" signature="line(segment)"><code></asyxml>*/
+line line(segment s)
+{/*<asyxml></code><documentation>Return the line passing through 's.A'
+ and 's.B'.</documentation></function></asyxml>*/
+ return line(s.A, s.B);
+}
+/*<asyxml><function type="segment" signature="segment(line)"><code></asyxml>*/
+segment segment(line l)
+{/*<asyxml></code><documentation>Return the segment whose extremities
+ are 'l.A' and 'l.B'.</documentation></function></asyxml>*/
+ return segment(l.A, l.B);
+}
+
+/*<asyxml><function type="point" signature="midpoint(segment)"><code></asyxml>*/
+point midpoint(segment s)
+{/*<asyxml></code><documentation>Return the midpoint of 's'.</documentation></function></asyxml>*/
+ return 0.5 * (s.A + s.B);
+}
+
+/*<asyxml><function type="void" signature="write(line)"><code></asyxml>*/
+void write(explicit line l)
+{/*<asyxml></code><documentation>Write some informations about 'l'.</documentation></function></asyxml>*/
+ write("A = "+(string)((pair)l.A));
+ write("Extend A = "+(l.extendA ? "true" : "false"));
+ write("B = "+(string)((pair)l.B));
+ write("Extend B = "+(l.extendB ? "true" : "false"));
+ write("u = "+(string)((pair)l.u));
+ write("v = "+(string)((pair)l.v));
+ write("a = "+(string) l.a);
+ write("b = "+(string) l.b);
+ write("c = "+(string) l.c);
+ write("slope = "+(string) l.slope);
+ write("origin = "+(string) l.origin);
+}
+
+/*<asyxml><function type="void" signature="write(explicit segment)"><code></asyxml>*/
+void write(explicit segment s)
+{/*<asyxml></code><documentation>Write some informations about 's'.</documentation></function></asyxml>*/
+ write("A = "+(string)((pair)s.A));
+ write("B = "+(string)((pair)s.B));
+ write("u = "+(string)((pair)s.u));
+ write("v = "+(string)((pair)s.v));
+ write("a = "+(string) s.a);
+ write("b = "+(string) s.b);
+ write("c = "+(string) s.c);
+ write("slope = "+(string) s.slope);
+ write("origin = "+(string) s.origin);
+}
+
+/*<asyxml><operator type = "bool" signature="==(line,line)"><code></asyxml>*/
+bool operator ==(line l1, line l2)
+{/*<asyxml></code><documentation>Provide the test 'line == line'.</documentation></operator></asyxml>*/
+ return (collinear(l1.u, l2.u) &&
+ abs(ypart((locate(l1.A) - locate(l1.B))/(locate(l1.A) - locate(l2.B)))) < epsgeo &&
+ l1.extendA == l2.extendA && l1.extendB == l2.extendB);
+}
+
+/*<asyxml><operator type = "bool" signature="!=(line,line)"><code></asyxml>*/
+bool operator !=(line l1, line l2)
+{/*<asyxml></code><documentation>Provide the test 'line != line'.</documentation></operator></asyxml>*/
+ return !(l1 == l2);
+}
+
+/*<asyxml><operator type = "bool" signature="@(point,line)"><code></asyxml>*/
+bool operator @(point m, line l)
+{/*<asyxml></code><documentation>Provide the test 'point @ line'.
+ Return true iff 'm' is on the 'l'.</documentation></operator></asyxml>*/
+ point M = changecoordsys(l.A.coordsys, m);
+ if (abs(l.a * M.x + l.b * M.y + l.c) >= epsgeo) return false;
+ if (l.extendA && l.extendB) return true;
+ if (!l.extendA && !l.extendB) return between(l.A, M, l.B);
+ if (l.extendA) return sameside(M, l.A, l.B);
+ return sameside(M, l.B, l.A);
+}
+
+/*<asyxml><function type="coordsys" signature="coordsys(line)"><code></asyxml>*/
+coordsys coordsys(line l)
+{/*<asyxml></code><documentation>Return the coordinate system in which 'l' is defined.</documentation></function></asyxml>*/
+ return l.A.coordsys;
+}
+
+/*<asyxml><function type="line" signature="reverse(line)"><code></asyxml>*/
+line reverse(line l)
+{/*<asyxml></code><documentation>Permute the points 'A' and 'B' of 'l' and so its orientation.</documentation></function></asyxml>*/
+ return line(l.B, l.extendB, l.A, l.extendA);
+}
+
+/*<asyxml><function type="line" signature="extend(line)"><code></asyxml>*/
+line extend(line l)
+{/*<asyxml></code><documentation>Return the infinite line passing through 'l.A' and 'l.B'.</documentation></function></asyxml>*/
+ line ol = l.copy();
+ ol.extendA = true;
+ ol.extendB = true;
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="complementary(explicit line)"><code></asyxml>*/
+line complementary(explicit line l)
+{/*<asyxml></code><documentation>Return the complementary of a half-line with respect of
+ the full line 'l'.</documentation></function></asyxml>*/
+ if (l.extendA && l.extendB)
+ abort("complementary: the parameter is not a half-line.");
+ point origin = l.extendA ? l.B : l.A;
+ point ptdir = l.extendA ?
+ rotate(180, l.B) * l.A : rotate(180, l.A) * l.B;
+ return line(origin, false, ptdir);
+}
+
+/*<asyxml><function type="line[]" signature="complementary(explicit segment)"><code></asyxml>*/
+line[] complementary(explicit segment s)
+{/*<asyxml></code><documentation>Return the two half-lines of origin 's.A' and 's.B' respectively.</documentation></function></asyxml>*/
+ line[] ol = new line[2];
+ ol[0] = complementary(line(s.A, false, s.B));
+ ol[1] = complementary(line(s.A, s.B, false));
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="Ox(coordsys)"><code></asyxml>*/
+line Ox(coordsys R = currentcoordsys)
+{/*<asyxml></code><documentation>Return the x-axis of 'R'.</documentation></function></asyxml>*/
+ return line(point(R, (0, 0)), point(R, E));
+}
+/*<asyxml><constant type = "line" signature="Ox"><code></asyxml>*/
+restricted line Ox = Ox();/*<asyxml></code><documentation>the x-axis of
+ the default coordinate system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="Oy(coordsys)"><code></asyxml>*/
+line Oy(coordsys R = currentcoordsys)
+{/*<asyxml></code><documentation>Return the y-axis of 'R'.</documentation></function></asyxml>*/
+ return line(point(R, (0, 0)), point(R, N));
+}
+/*<asyxml><constant type = "line" signature="Oy"><code></asyxml>*/
+restricted line Oy = Oy();/*<asyxml></code><documentation>the y-axis of
+ the default coordinate system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="line(real,point)"><code></asyxml>*/
+line line(real a, point A = point(currentcoordsys, (0, 0)))
+{/*<asyxml></code><documentation>Return the line passing through 'A' with an
+ angle (in the coordinate system of A) 'a' in degrees.
+ line(point, real) is also defined.</documentation></function></asyxml>*/
+ return line(A, A + point(A.coordsys, A.coordsys.polar(1, radians(a))));
+}
+line line(point A = point(currentcoordsys, (0, 0)), real a)
+{
+ return line(a, A);
+}
+line line(int a, point A = point(currentcoordsys, (0, 0)))
+{
+ return line((real)a, A);
+}
+
+/*<asyxml><function type="line" signature="line(coordsys,real,real)"><code></asyxml>*/
+line line(coordsys R = currentcoordsys, real slope, real origin)
+{/*<asyxml></code><documentation>Return the line defined by slope and y-intercept relative to 'R'.</documentation></function></asyxml>*/
+ if (slope == infinity || slope == -infinity)
+ abort("The slope is infinite. Please, use the routine 'vline'.");
+ return line(point(R, (0, origin)), point(R, (1, origin + slope)));
+}
+
+/*<asyxml><function type="line" signature="line(coordsys,real,real,real)"><code></asyxml>*/
+line line(coordsys R = currentcoordsys, real a, real b, real c)
+{/*<asyxml></code><documentation>Retrun the line defined by equation relative to 'R'.</documentation></function></asyxml>*/
+ if (a == 0 && b == 0) abort("line: inconsistent equation...");
+ pair M;
+ M = (a == 0) ? (0, -c/b) : (-c/a, 0);
+ return line(point(R, M), point(R, M + (-b, a)));
+}
+
+/*<asyxml><function type="line" signature="vline(coordsys)"><code></asyxml>*/
+line vline(coordsys R = currentcoordsys)
+{/*<asyxml></code><documentation>Return a vertical line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/
+ point P = point(R, (0, 0));
+ point PP = point(R, (R.O + N)/R);
+ return line(P, PP);
+}
+/*<asyxml><constant type = "line" signature="vline"><code></asyxml>*/
+restricted line vline = vline();/*<asyxml></code><documentation>The vertical line in the current coordinate system passing
+ through the origin of this system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="hline(coordsys)"><code></asyxml>*/
+line hline(coordsys R = currentcoordsys)
+{/*<asyxml></code><documentation>Return a horizontal line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/
+ point P = point(R, (0, 0));
+ point PP = point(R, (R.O + E)/R);
+ return line(P, PP);
+}
+/*<asyxml><constant type = "line" signature="hline"><code></asyxml>*/
+line hline = hline();/*<asyxml></code><documentation>The horizontal line in the current coordinate system passing
+ through the origin of this system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="changecoordsys(coordsys,line)"><code></asyxml>*/
+line changecoordsys(coordsys R, line l)
+{/*<asyxml></code><documentation>Return the line 'l' in the coordinate system 'R'.</documentation></function></asyxml>*/
+ point A = changecoordsys(R, l.A);
+ point B = changecoordsys(R, l.B);
+ return line(A, B);
+}
+
+/*<asyxml><function type="transform" signature="scale(real,line,line,bool)"><code></asyxml>*/
+transform scale(real k, line l1, line l2, bool safe = false)
+{/*<asyxml></code><documentation>Return the dilatation with respect to
+ 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/
+ return scale(k, l1.A, l1.B, l2.A, l2.B, safe);
+}
+
+/*<asyxml><function type="transform" signature="reflect(line)"><code></asyxml>*/
+transform reflect(line l)
+{/*<asyxml></code><documentation>Return the reflect about the line 'l'.</documentation></function></asyxml>*/
+ return reflect((pair)l.A, (pair)l.B);
+}
+
+/*<asyxml><function type="transform" signature="reflect(line,line)"><code></asyxml>*/
+transform reflect(line l1, line l2, bool safe = false)
+{/*<asyxml></code><documentation>Return the reflect about the line
+ 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/
+ return scale(-1.0, l1, l2, safe);
+}
+
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,path)"><code></asyxml>*/
+point[] intersectionpoints(line l, path g)
+{/*<asyxml></code><documentation>Return all points of intersection of the line 'l' with the path 'g'.</documentation></function></asyxml>*/
+ // TODO utiliser la version 1.44 de intersections(path g, pair p, pair q)
+ // real [] t = intersections(g, l.A, l.B);
+ // coordsys R = coordsys(l);
+ // return sequence(new point(int n){return point(R, point(g, t[n])/R);}, t.length);
+ real [] t;
+ pair[] op;
+ pair A = l.A;
+ pair B = l.B;
+ real dy = B.y - A.y,
+ dx = A.x - B.x,
+ lg = length(g);
+
+ for (int i = 0; i < lg; ++i)
+ {
+ pair z0 = point(g, i),
+ z1 = point(g, i + 1),
+ c0 = postcontrol(g, i),
+ c1 = precontrol(g, i + 1),
+ t3 = z1 - z0 - 3 * c1 + 3 * c0,
+ t2 = 3 * z0 + 3 * c1 - 6 * c0,
+ t1 = 3 * c0 - 3z0;
+ real a = dy * t3.x + dx * t3.y,
+ b = dy * t2.x + dx * t2.y,
+ c = dy * t1.x + dx * t1.y,
+ d = dy * z0.x + dx * z0.y + A.y * B.x - A.x * B.y;
+
+ t = cubicroots(a, b, c, d);
+ for (int j = 0; j < t.length; ++j)
+ if (
+ t[j]>=0
+ && (
+ t[j]<1
+ || (
+ t[j] == 1
+ && (i == lg - 1)
+ && !cyclic(g)
+ )
+ )
+ ) {
+ op.push(point(g, i + t[j]));
+ }
+ }
+
+ point[] opp;
+ for (int i = 0; i < op.length; ++i)
+ opp.push(point(coordsys(l), op[i]/coordsys(l)));
+ return opp;
+}
+
+/*<asyxml><function type="point" signature="intersectionpoint(line,line)"><code></asyxml>*/
+point intersectionpoint(line l1, line l2)
+{/*<asyxml></code><documentation>Return the point of intersection of line 'l1' with 'l2'.
+ If 'l1' and 'l2' have an infinity or none point of intersection,
+ this routine return (infinity, infinity).</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(l1.A, l1.B, l2.A, l2.B);
+ coordsys R = P[0].coordsys;
+ pair p = extension(P[0], P[1], P[2], P[3]);
+ if(finite(p)){
+ point p = point(R, p/R);
+ if (p @ l1 && p @ l2) return p;
+ }
+ return point(R, (infinity, infinity));
+}
+
+/*<asyxml><function type="line" signature="parallel(point,line)"><code></asyxml>*/
+line parallel(point M, line l)
+{/*<asyxml></code><documentation>Return the line parallel to 'l' passing through 'M'.</documentation></function></asyxml>*/
+ point A, B;
+ if (M.coordsys != coordsys(l))
+ {
+ A = changecoordsys(M.coordsys, l.A);
+ B = changecoordsys(M.coordsys, l.B);
+ } else {A = l.A;B = l.B;}
+ return line(M, M - A + B);
+}
+
+/*<asyxml><function type="line" signature="parallel(point,explicit vector)"><code></asyxml>*/
+line parallel(point M, explicit vector dir)
+{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/
+ return line(M, M + locate(dir));
+}
+
+/*<asyxml><function type="line" signature="parallel(point,explicit pair)"><code></asyxml>*/
+line parallel(point M, explicit pair dir)
+{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/
+ return line(M, M + vector(currentcoordsys, dir));
+}
+
+/*<asyxml><function type="bool" signature="parallel(line,line)"><code></asyxml>*/
+bool parallel(line l1, line l2, bool strictly = false)
+{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are (strictly ?) parallel.</documentation></function></asyxml>*/
+ bool coll = collinear(l1.u, l2.u);
+ return strictly ? coll && (l1 != l2) : coll;
+}
+
+/*<asyxml><function type="bool" signature="concurrent(...line[])"><code></asyxml>*/
+bool concurrent(... line[] l)
+{/*<asyxml></code><documentation>Returns true if all the lines 'l' are concurrent.</documentation></function></asyxml>*/
+ if (l.length < 3) abort("'concurrent' needs at least for three lines ...");
+ pair point = intersectionpoint(l[0], l[1]);
+ bool conc;
+ for (int i = 2; i < l.length; ++i) {
+ pair pt = intersectionpoint(l[i - 1], l[i]);
+ conc = simeq(pt, point);
+ if (!conc) break;
+ }
+ return conc;
+}
+
+/*<asyxml><function type="transform" signature="projection(line)"><code></asyxml>*/
+transform projection(line l)
+{/*<asyxml></code><documentation>Return the orthogonal projection on 'l'.</documentation></function></asyxml>*/
+ return projection(l.A, l.B);
+}
+
+/*<asyxml><function type="transform" signature="projection(line,line,bool)"><code></asyxml>*/
+transform projection(line l1, line l2, bool safe = false)
+{/*<asyxml></code><documentation>Return the projection on (AB) in parallel of (CD).
+ If 'safe = true' and (l1)//(l2) return the identity.
+ If 'safe = false' and (l1)//(l2) return a infinity scaling.</documentation></function></asyxml>*/
+ return projection(l1.A, l1.B, l2.A, l2.B, safe);
+}
+
+/*<asyxml><function type="transform" signature="vprojection(line,bool)"><code></asyxml>*/
+transform vprojection(line l, bool safe = false)
+{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of N--S.
+ If 'safe' is 'true' the projected point keeps the same place if 'l'
+ is vertical.</documentation></function></asyxml>*/
+ coordsys R = defaultcoordsys;
+ return projection(l, line(point(R, N), point(R, S)), safe);
+}
+
+/*<asyxml><function type="transform" signature="hprojection(line,bool)"><code></asyxml>*/
+transform hprojection(line l, bool safe = false)
+{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of E--W.
+ If 'safe' is 'true' the projected point keeps the same place if 'l'
+ is horizontal.</documentation></function></asyxml>*/
+ coordsys R = defaultcoordsys;
+ return projection(l, line(point(R, E), point(R, W)), safe);
+}
+
+/*<asyxml><function type="line" signature="perpendicular(point,line)"><code></asyxml>*/
+line perpendicular(point M, line l)
+{/*<asyxml></code><documentation>Return the perpendicular line of 'l' passing through 'M'.</documentation></function></asyxml>*/
+ point Mp = projection(l) * M;
+ point A = Mp == l.A ? l.B : l.A;
+ return line(Mp, rotate(90, Mp) * A);
+}
+
+/*<asyxml><function type="line" signature="perpendicular(point,explicit vector)"><code></asyxml>*/
+line perpendicular(point M, explicit vector normal)
+{/*<asyxml></code><documentation>Return the line passing through 'M'
+ whose normal is \param{normal}.</documentation></function></asyxml>*/
+ return perpendicular(M, line(M, M + locate(normal)));
+}
+
+/*<asyxml><function type="line" signature="perpendicular(point,explicit pair)"><code></asyxml>*/
+line perpendicular(point M, explicit pair normal)
+{/*<asyxml></code><documentation>Return the line passing through 'M'
+ whose normal is \param{normal} (given in the currentcoordsys).</documentation></function></asyxml>*/
+ return perpendicular(M, line(M, M + vector(currentcoordsys, normal)));
+}
+
+/*<asyxml><function type="bool" signature="perpendicular(line,line)"><code></asyxml>*/
+bool perpendicular(line l1, line l2)
+{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are perpendicular.</documentation></function></asyxml>*/
+ return abs(dot(locate(l1.u), locate(l2.u))) < epsgeo ;
+}
+
+/*<asyxml><function type="real" signature="angle(line,coordsys)"><code></asyxml>*/
+real angle(line l, coordsys R = coordsys(l))
+{/*<asyxml></code><documentation>Return the angle of the oriented line 'l',
+ in radian, in the interval ]-pi, pi] and relatively to 'R'.</documentation></function></asyxml>*/
+ return angle(l.u, R, false);
+}
+
+/*<asyxml><function type="real" signature="degrees(line,coordsys,bool)"><code></asyxml>*/
+real degrees(line l, coordsys R = coordsys(l))
+{/*<asyxml></code><documentation>Returns the angle of the oriented line 'l' in degrees,
+ in the interval [0, 360[ and relatively to 'R'.</documentation></function></asyxml>*/
+ return degrees(angle(l, R));
+}
+
+/*<asyxml><function type="real" signature="sharpangle(line,line)"><code></asyxml>*/
+real sharpangle(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in radians of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/
+ vector u1 = l1.u;
+ vector u2 = (dot(l1.u, l2.u) < 0) ? -l2.u : l2.u;
+ real a12 = angle(locate(u2)) - angle(locate(u1));
+ a12 = a12%(sgnd(a12) * pi);
+ if (a12 <= -pi/2) {
+ a12 += pi;
+ } else if (a12 > pi/2) {
+ a12 -= pi;
+ }
+ return a12;
+}
+
+/*<asyxml><function type="real" signature="angle(line,line)"><code></asyxml>*/
+real angle(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in radians of oriented angle (l1.u, l2.u).</documentation></function></asyxml>*/
+ return angle(locate(l2.u)) - angle(locate(l1.u));
+}
+
+/*<asyxml><function type="real" signature="degrees(line,line)"><code></asyxml>*/
+real degrees(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in degrees of the
+ angle formed by the oriented lines 'l1' and 'l2'.</documentation></function></asyxml>*/
+ return degrees(angle(l1, l2));
+}
+
+/*<asyxml><function type="real" signature="sharpdegrees(line,line)"><code></asyxml>*/
+real sharpdegrees(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in degrees of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/
+ return degrees(sharpangle(l1, l2));
+}
+
+/*<asyxml><function type="line" signature="bisector(line,line,real,bool)"><code></asyxml>*/
+line bisector(line l1, line l2, real angle = 0, bool sharp = true)
+{/*<asyxml></code><documentation>Return the bisector of the angle formed by 'l1' and 'l2'
+ rotated by the angle 'angle' (in degrees) around intersection point of 'l1' with 'l2'.
+ If 'sharp' is true (the default), this routine returns the bisector of the sharp angle.
+ Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/
+ line ol;
+ if (l1 == l2) return l1;
+ point A = intersectionpoint(l1, l2);
+ if (finite(A)) {
+ if(sharp) ol = rotate(sharpdegrees(l1, l2)/2 + angle, A) * l1;
+ else {
+ coordsys R = coordsys(l1);
+ pair a = A, b = A + l1.u, c = A + l2.u;
+ pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c));
+ return rotate(angle, A) * line(A, point(R, pp/R));
+ }
+ } else {
+ ol = l1;
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="sector(int,int,line,line,real,bool)"><code></asyxml>*/
+line sector(int n = 2, int p = 1, line l1, line l2, real angle = 0, bool sharp = true)
+{/*<asyxml></code><documentation>Return the p-th nth-sector of the angle
+ formed by the oriented line 'l1' and 'l2'
+ rotated by the angle 'angle' (in degrees) around the intersection point of 'l1' with 'l2'.
+ If 'sharp' is true (the default), this routine returns the bisector of the sharp angle.
+ Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/
+ line ol;
+ if (l1 == l2) return l1;
+ point A = intersectionpoint(l1, l2);
+ if (finite(A)) {
+ if(sharp) ol = rotate(p * sharpdegrees(l1, l2)/n + angle, A) * l1;
+ else {
+ ol = rotate(p * degrees(l1, l2)/n + angle, A) * l1;
+ }
+ } else {
+ ol = l1;
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="bisector(point,point,point,point,real)"><code></asyxml>*/
+line bisector(point A, point B, point C, point D, real angle = 0, bool sharp = true)
+{/*<asyxml></code><documentation>Return the bisector of the angle formed by the lines (AB) and (CD).
+ <look href = "#bisector(line, line, real, bool)"/>.</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B, C, D);
+ return bisector(line(P[0], P[1]), line(P[2], P[3]), angle, sharp);
+}
+
+/*<asyxml><function type="line" signature="bisector(segment,real)"><code></asyxml>*/
+line bisector(segment s, real angle = 0)
+{/*<asyxml></code><documentation>Return the bisector of the segment line 's' rotated by 'angle' (in degrees) around the
+ midpoint of 's'.</documentation></function></asyxml>*/
+ coordsys R = coordsys(s);
+ point m = midpoint(s);
+ vector dir = rotateO(90) * unit(s.A - m);
+ return rotate(angle, m) * line(m + dir, m - dir);
+}
+
+/*<asyxml><function type="line" signature="bisector(point,point,real)"><code></asyxml>*/
+line bisector(point A, point B, real angle = 0)
+{/*<asyxml></code><documentation>Return the bisector of the segment line [AB] rotated by 'angle' (in degrees) around the
+ midpoint of [AB].</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B);
+ return bisector(segment(P[0], P[1]), angle);
+}
+
+/*<asyxml><function type="real" signature="distance(point,line)"><code></asyxml>*/
+real distance(point M, line l)
+{/*<asyxml></code><documentation>Return the distance from 'M' to 'l'.
+ distance(line, point) is also defined.</documentation></function></asyxml>*/
+ point A = changecoordsys(defaultcoordsys, l.A);
+ point B = changecoordsys(defaultcoordsys, l.B);
+ line ll = line(A, B);
+ pair m = locate(M);
+ return abs(ll.a * m.x + ll.b * m.y + ll.c)/sqrt(ll.a^2 + ll.b^2);
+}
+
+real distance(line l, point M)
+{
+ return distance(M, l);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,line,bool,bool,align,pen,arrowbar,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "",
+ line l, bool dirA = l.extendA, bool dirB = l.extendB,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None,
+ Label legend = "", marker marker = nomarker,
+ pathModifier pathModifier = NoModifier)
+{/*<asyxml></code><documentation>Draw the line 'l' without altering the size of picture pic.
+ The boolean parameters control the infinite section.
+ The global variable 'linemargin' (default value is 0) allows to modify
+ the bounding box in which the line must be drawn.</documentation></function></asyxml>*/
+ if(!(dirA || dirB)) draw(l.A--l.B, invisible);// l is a segment.
+ Drawline(pic, L, l.A, dirP = dirA, l.B, dirQ = dirB,
+ align, p, arrow,
+ legend, marker, pathModifier);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen[], arrowbar,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l,
+ align align = NoAlign, pen[] p = new pen[],
+ arrowbar arrow = None,
+ Label[] legend = new Label[], marker marker = nomarker,
+ pathModifier pathModifier = NoModifier)
+{/*<asyxml></code><documentation>Draw each lines with the corresponding pen.</documentation></function></asyxml>*/
+ for (int i = 0; i < l.length; ++i) {
+ draw(pic, L.length>0 ? L[i] : "", l[i],
+ align, p = p.length>0 ? p[i] : currentpen,
+ arrow, legend.length>0 ? legend[i] : "", marker,
+ pathModifier);
+ }
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen,arrowbar,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l,
+ align align = NoAlign, pen p,
+ arrowbar arrow = None,
+ Label[] legend = new Label[], marker marker = nomarker,
+ pathModifier pathModifier = NoModifier)
+{/*<asyxml></code><documentation>Draw each lines with the same pen 'p'.</documentation></function></asyxml>*/
+ pen[] tp = sequence(new pen(int i){return p;}, l.length);
+ draw(pic, L, l, align, tp, arrow, legend, marker, pathModifier);
+}
+
+/*<asyxml><function type="void" signature="show(picture,line,pen)"><code></asyxml>*/
+void show(picture pic = currentpicture, line l, pen p = red)
+{/*<asyxml></code><documentation>Draw some informations of 'l'.</documentation></function></asyxml>*/
+ dot("$A$", (pair)l.A, align = -locate(l.v), p);
+ dot("$B$", (pair)l.B, align = -locate(l.v), p);
+ draw(l, dotted);
+ draw("$\vec{u}$", locate(l.A)--locate(l.A + l.u), p, Arrow);
+ draw("$\vec{v}$", locate(l.A)--locate(l.A + l.v), p, Arrow);
+}
+
+/*<asyxml><function type="point[]" signature="sameside(point,line,line)"><code></asyxml>*/
+point[] sameside(point M, line l1, line l2)
+{/*<asyxml></code><documentation>Return two points on 'l1' and 'l2' respectively.
+ The first point is from the same side of M relatively to 'l2',
+ the second point is from the same side of M relatively to 'l1'.</documentation></function></asyxml>*/
+ point[] op;
+ coordsys R1 = coordsys(l1);
+ coordsys R2 = coordsys(l2);
+ if (parallel(l1, l2)) {
+ op.push(projection(l1) * M);
+ op.push(projection(l2) * M);
+ } else {
+ point O = intersectionpoint(l1, l2);
+ if (M @ l2) op.push((sameside(M, O + l1.u, l2)) ? O + l1.u : rotate(180, O) * (O + l1.u));
+ else op.push(projection(l1, l2) * M);
+ if (M @ l1) op.push((sameside(M, O + l2.u, l1)) ? O + l2.u : rotate(180, O) * (O + l2.u));
+ else {op.push(projection(l2, l1) * M);}
+ }
+ return op;
+}
+
+/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit pair,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
+void markangle(picture pic = currentpicture,
+ Label L = "", int n = 1, real radius = 0, real space = 0,
+ explicit line l1, explicit line l2, explicit pair align = dir(1),
+ arrowbar arrow = None, pen p = currentpen,
+ filltype filltype = NoFill,
+ margin margin = NoMargin, marker marker = nomarker)
+{/*<asyxml></code><documentation>Mark the angle (l1, l2) aligned in the direction 'align' relative to 'l1'.
+ Commune values for 'align' are dir(real).</documentation></function></asyxml>*/
+ if (parallel(l1, l2, true)) return;
+ real al = degrees(l1, defaultcoordsys);
+ pair O, A, B;
+ if (radius == 0) radius = markangleradius(p);
+ real d = degrees(locate(l1.u));
+ align = rotate(d) * align;
+ if (l1 == l2) {
+ O = midpoint(segment(l1.A, l1.B));
+ A = l1.A;B = l1.B;
+ if (sameside(rotate(sgn(angle(B-A)) * 45, O) * A, O + align, l1)) {radius = -radius;}
+ } else {
+ O = intersectionpoint(extend(l1), extend(l2));
+ pair R = O + align;
+ point [] ss = sameside(point(coordsys(l1), R/coordsys(l1)), l1, l2);
+ A = ss[0];
+ B = ss[1];
+ }
+ markangle(pic = pic, L = L, n = n, radius = radius, space = space,
+ O = O, A = A, B = B,
+ arrow = arrow, p = p, filltype = filltype,
+ margin = margin, marker = marker);
+}
+
+/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit vector,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
+void markangle(picture pic = currentpicture,
+ Label L = "", int n = 1, real radius = 0, real space = 0,
+ explicit line l1, explicit line l2, explicit vector align,
+ arrowbar arrow = None, pen p = currentpen,
+ filltype filltype = NoFill,
+ margin margin = NoMargin, marker marker = nomarker)
+{/*<asyxml></code><documentation>Mark the angle (l1, l2) in the direction 'dir' given relatively to 'l1'.</documentation></function></asyxml>*/
+ markangle(pic, L, n, radius, space, l1, l2, (pair)align, arrow,
+ p, filltype, margin, marker);
+}
+
+/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
+// void markangle(picture pic = currentpicture,
+// Label L = "", int n = 1, real radius = 0, real space = 0,
+// explicit line l1, explicit line l2,
+// arrowbar arrow = None, pen p = currentpen,
+// filltype filltype = NoFill,
+// margin margin = NoMargin, marker marker = nomarker)
+// {/*<asyxml></code><documentation>Mark the oriented angle (l1, l2).</documentation></function></asyxml>*/
+// if (parallel(l1, l2, true)) return;
+// real al = degrees(l1, defaultcoordsys);
+// pair O, A, B;
+// if (radius == 0) radius = markangleradius(p);
+// real d = degrees(locate(l1.u));
+// if (l1 == l2) {
+// O = midpoint(segment(l1.A, l1.B));
+// } else {
+// O = intersectionpoint(extend(l1), extend(l2));
+// }
+// A = O + locate(l1.u);
+// B = O + locate(l2.u);
+// markangle(pic = pic, L = L, n = n, radius = radius, space = space,
+// O = O, A = A, B = B,
+// arrow = arrow, p = p, filltype = filltype,
+// margin = margin, marker = marker);
+// }
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,line,line,real,pen,int,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic = currentpicture, line l1, line l2,
+ real size = 0, pen p = currentpen, int quarter = 1,
+ margin margin = NoMargin, filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw a right angle at the intersection point of lines and
+ aligned in the 'quarter' nth quarter of circle formed by 'l1.u' and
+ 'l2.u'.</documentation></function></asyxml>*/
+ point P = intersectionpoint(l1, l2);
+ pair align = rotate(90 * (quarter - 1)) * dir(45);
+ perpendicularmark(P, align, locate(l1.u), size, p, margin, filltype);
+}
+// *.........................LINES.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *........................CONICS.........................*
+/*<asyxml><struct signature="bqe"><code></asyxml>*/
+struct bqe
+{/*<asyxml></code><documentation>Bivariate Quadratic Equation.</documentation></asyxml>*/
+ /*<asyxml><property type = "real[]" signature="a"><code></asyxml>*/
+ real[] a;/*<asyxml></code><documentation>a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0</documentation></property><property type = "coordsys" signature="coordsys"><code></asyxml>*/
+ coordsys coordsys;/*<asyxml></code></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="bqe" signature="bqe(coordsys,real,real,real,real,real,real)"><code></asyxml>*/
+bqe bqe(coordsys R = currentcoordsys,
+ real a, real b, real c, real d, real e, real f)
+{/*<asyxml></code><documentation>Return the bivariate quadratic equation
+ a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0
+ relatively to the coordinate system R.</documentation></function></asyxml>*/
+ bqe obqe;
+ obqe.coordsys = R;
+ obqe.a = new real[] {a, b, c, d, e, f};
+ return obqe;
+}
+
+/*<asyxml><function type="bqe" signature="changecoordsys(coordsys,bqe)"><code></asyxml>*/
+bqe changecoordsys(coordsys R, bqe bqe)
+{/*<asyxml></code><documentation>Returns the bivariate quadratic equation relatively to 'R'.</documentation></function></asyxml>*/
+ pair i = coordinates(changecoordsys(R, vector(defaultcoordsys,
+ bqe.coordsys.i)));
+ pair j = coordinates(changecoordsys(R, vector(defaultcoordsys,
+ bqe.coordsys.j)));
+ pair O = coordinates(changecoordsys(R, point(defaultcoordsys,
+ bqe.coordsys.O)));
+ real a = bqe.a[0], b = bqe.a[1], c = bqe.a[2], d = bqe.a[3], f = bqe.a[4], g = bqe.a[5];
+ real ux = i.x, uy = i.y;
+ real vx = j.x, vy = j.y;
+ real ox = O.x, oy = O.y;
+ real D = ux * vy - uy * vx;
+ real ap = (a * vy^2 - b * uy * vy + c * uy^2)/D^2;
+ real bpp = (-2 * a * vx * vy + b * ux * vy + b * uy * vx - 2 * c * ux * uy)/D^2;
+ real cp = (a * vx^2 - b * ux * vx + c * ux^2)/D^2;
+ real dp = (-2a * ox * vy^2 + 2a * oy * vx * vy + 2b * ox * uy * vy-
+ b * oy * ux * vy - b * oy * uy * vx - 2c * ox * uy^2 + 2c * oy * uy * ux)/D^2+
+ (d * vy - f * uy)/D;
+ real fp = (2a * ox * vx * vy - b * ox * ux * vy - 2a * oy * vx^2-
+ b * ox * uy * vx + 2 * b * oy * ux * vx + 2c * ox * ux * uy - 2c * oy * ux^2)/D^2+
+ (f * ux - d * vx)/D;
+ g = (a * ox^2 * vy^2 - 2a * ox * oy * vx * vy - b * ox^2 * uy * vy + b * ox * oy * ux * vy+
+ a * oy^2 * vx^2 + b * ox * oy * uy * vx - b * oy^2 * ux * vx + c * ox^2 * uy^2-
+ 2 * c * ox * oy * ux * uy + c * oy^2 * ux^2)/D^2+
+ (d * oy * vx + f * ox * uy - d * ox * vy - f * oy * ux)/D + g;
+ bqe obqe;
+ obqe.a = approximate(new real[] {ap, bpp, cp, dp, fp, g});
+ obqe.coordsys = R;
+ return obqe;
+}
+
+/*<asyxml><function type="bqe" signature="bqe(point,point,point,point,point)"><code></asyxml>*/
+bqe bqe(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the bqe of conic passing through the five points (if possible).</documentation></function></asyxml>*/
+ coordsys R;
+ pair[] pts;
+ if (samecoordsys(M1, M2, M3, M4, M5)) {
+ R = M1.coordsys;
+ pts= new pair[] {M1.coordinates, M2.coordinates, M3.coordinates, M4.coordinates, M5.coordinates};
+ } else {
+ R = defaultcoordsys;
+ pts= new pair[] {M1, M2, M3, M4, M5};
+ }
+ real[][] M;
+ real[] x;
+ bqe bqe;
+ bqe.coordsys = R;
+ for (int i = 0; i < 5; ++i) {// Try a = -1
+ M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1};
+ x[i] = pts[i].x^2;
+ }
+ if(abs(determinant(M)) < 1e-5) {// Try c = -1
+ for (int i = 0; i < 5; ++i) {
+ M[i] = new real[] {pts[i].x^2, pts[i].x * pts[i].y, pts[i].x, pts[i].y, 1};
+ x[i] = pts[i].y^2;
+ }
+ real[] coef = solve(M, x);
+ bqe.a = new real[] {coef[0], coef[1], -1, coef[2], coef[3], coef[4]};
+ } else {
+ real[] coef = solve(M, x);
+ bqe.a = new real[] {-1, coef[0], coef[1], coef[2], coef[3], coef[4]};
+ }
+ bqe.a = approximate(bqe.a);
+ return bqe;
+}
+
+/*<asyxml><function type="bool" signature="samecoordsys(bool...bqe[])"><code></asyxml>*/
+bool samecoordsys(bool warn = true ... bqe[] bqes)
+{/*<asyxml></code><documentation>Return true if all the bivariate quadratic equations have the same coordinate system.</documentation></function></asyxml>*/
+ bool ret = true;
+ coordsys t = bqes[0].coordsys;
+ for (int i = 1; i < bqes.length; ++i) {
+ ret = (t == bqes[i].coordsys);
+ if(!ret) break;
+ t = bqes[i].coordsys;
+ }
+ if(warn && !ret)
+ warning("coodinatesystem",
+ "the coordinate system of two bivariate quadratic equations are not
+the same. The operation will be done relatively to the default coordinate
+system.");
+ return ret;
+}
+
+/*<asyxml><function type="real[]" signature="realquarticroots(real,real,real,real,real)"><code></asyxml>*/
+real[] realquarticroots(real a, real b, real c, real d, real e)
+{/*<asyxml></code><documentation>Return the real roots of the quartic equation ax^4 + b^x3 + cx^2 + dx = 0.</documentation></function></asyxml>*/
+ static real Fuzz = sqrt(realEpsilon);
+ pair[] zroots = quarticroots(a, b, c, d, e);
+ real[] roots;
+ real p(real x){return a * x^4 + b * x^3 + c * x^2 + d * x + e;}
+ real prime(real x){return 4 * a * x^3 + 3 * b * x^2 + 2 * c * x + d;}
+ real x;
+ bool search = true;
+ int n;
+ void addroot(real x)
+ {
+ bool exist = false;
+ for (int i = 0; i < roots.length; ++i) {
+ if(abs(roots[i]-x) < 1e-5) {exist = true; break;}
+ }
+ if(!exist) roots.push(x);
+ }
+ for(int i = 0; i < zroots.length; ++i) {
+ if(zroots[i].y == 0 || abs(p(zroots[i].x)) < Fuzz) addroot(zroots[i].x);
+ else {
+ if(abs(zroots[i].y) < 1e-3) {
+ x = zroots[i].x;
+ search = true;
+ n = 200;
+ while(search) {
+ real tx = abs(p(x)) < Fuzz ? x : newton(iterations = n, p, prime, x);
+ if(tx < realMax) {
+ if(abs(p(tx)) < Fuzz) {
+ addroot(tx);
+ search = false;
+ } else if(n < 200) n *=2;
+ else {
+ search = false;
+ }
+ } else search = false; //It's not a real root.
+ }
+ }
+ }
+ }
+ return roots;
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(bqe,bqe)"><code></asyxml>*/
+point[] intersectionpoints(bqe bqe1, bqe bqe2)
+{/*<asyxml></code><documentation>Return the interscetion of the two conic sections whose equations are 'bqe1' and 'bqe2'.</documentation></function></asyxml>*/
+ coordsys R = bqe1.coordsys;
+ bqe lbqe1, lbqe2;
+ real[] a, b;
+ if(R != bqe2.coordsys) {
+ R = currentcoordsys;
+ a = changecoordsys(R, bqe1).a;
+ b = changecoordsys(R, bqe2).a;
+ } else {
+ a = bqe1.a;
+ b = bqe2.a;
+ }
+ static real e = 100 * sqrt(realEpsilon);
+ real[] x, y, c;
+ point[] P;
+ if(abs(a[0]-b[0]) > e || abs(a[1]-b[1]) > e || abs(a[2]-b[2]) > e) {
+ c = new real[] {-2 * a[0]*a[2]*b[0]*b[2]+a[0]*a[2]*b[1]^2 - a[0]*a[1]*b[2]*b[1]+a[1]^2 * b[0]*b[2]-
+ a[2]*a[1]*b[0]*b[1]+a[0]^2 * b[2]^2 + a[2]^2 * b[0]^2,
+ -a[2]*a[1]*b[0]*b[4]-a[2]*a[4]*b[0]*b[1]-a[1]*a[3]*b[2]*b[1]+2 * a[0]*a[2]*b[1]*b[4]-
+ a[0]*a[1]*b[2]*b[4]+a[1]^2 * b[2]*b[3]-2 * a[2]*a[3]*b[0]*b[2]-2 * a[0]*a[2]*b[2]*b[3]+
+ a[2]*a[3]*b[1]^2 - a[2]*a[1]*b[1]*b[3]+2 * a[1]*a[4]*b[0]*b[2]+2 * a[2]^2 * b[0]*b[3]-
+ a[0]*a[4]*b[2]*b[1]+2 * a[0]*a[3]*b[2]^2,
+ -a[3]*a[4]*b[2]*b[1]+a[2]*a[5]*b[1]^2 - a[1]*a[5]*b[2]*b[1]-a[1]*a[3]*b[2]*b[4]+
+ a[1]^2 * b[2]*b[5]-2 * a[2]*a[3]*b[2]*b[3]+2 * a[2]^2 * b[0]*b[5]+2 * a[0]*a[5]*b[2]^2 + a[3]^2 * b[2]^2-
+ 2 * a[2]*a[5]*b[0]*b[2]+2 * a[1]*a[4]*b[2]*b[3]-a[2]*a[4]*b[1]*b[3]-2 * a[0]*a[2]*b[2]*b[5]+
+ a[2]^2 * b[3]^2 + 2 * a[2]*a[3]*b[1]*b[4]-a[2]*a[4]*b[0]*b[4]+a[4]^2 * b[0]*b[2]-a[2]*a[1]*b[3]*b[4]-
+ a[2]*a[1]*b[1]*b[5]-a[0]*a[4]*b[2]*b[4]+a[0]*a[2]*b[4]^2,
+ -a[4]*a[5]*b[2]*b[1]+a[2]*a[3]*b[4]^2 + 2 * a[3]*a[5]*b[2]^2 - a[2]*a[1]*b[4]*b[5]-
+ a[2]*a[4]*b[3]*b[4]+2 * a[2]^2 * b[3]*b[5]-2 * a[2]*a[3]*b[2]*b[5]-a[3]*a[4]*b[2]*b[4]-
+ 2 * a[2]*a[5]*b[2]*b[3]-a[2]*a[4]*b[1]*b[5]+2 * a[1]*a[4]*b[2]*b[5]-a[1]*a[5]*b[2]*b[4]+
+ a[4]^2 * b[2]*b[3]+2 * a[2]*a[5]*b[1]*b[4],
+ -2 * a[2]*a[5]*b[2]*b[5]+a[4]^2 * b[2]*b[5]+a[5]^2 * b[2]^2 - a[4]*a[5]*b[2]*b[4]+a[2]*a[5]*b[4]^2+
+ a[2]^2 * b[5]^2 - a[2]*a[4]*b[4]*b[5]};
+ x = realquarticroots(c[0], c[1], c[2], c[3], c[4]);
+ } else {
+ if(abs(b[4]-a[4]) > e){
+ real D = (b[4]-a[4])^2;
+ c = new real[] {(a[0]*b[4]^2 + (-a[1]*b[3]-2 * a[0]*a[4]+a[1]*a[3]) * b[4]+a[2]*b[3]^2+
+ (a[1]*a[4]-2 * a[2]*a[3]) * b[3]+a[0]*a[4]^2 - a[1]*a[3]*a[4]+a[2]*a[3]^2)/D,
+ -((a[1]*b[4]-2 * a[2]*b[3]-a[1]*a[4]+2 * a[2]*a[3]) * b[5]-a[3]*b[4]^2 + (a[4]*b[3]-a[1]*a[5]+a[3]*a[4]) * b[4]+(2 * a[2]*a[5]-a[4]^2) * b[3]+(a[1]*a[4]-2 * a[2]*a[3]) * a[5])/D,
+ a[2]*(a[5]-b[5])^2/D + a[4]*(a[5]-b[5])/(b[4]-a[4]) + a[5]};
+ x = quadraticroots(c[0], c[1], c[2]);
+ } else {
+ if(abs(a[3]-b[3]) > e) {
+ real D = b[3]-a[3];
+ c = new real[] {a[2], (-a[1]*b[5] + a[4]*b[3] + a[1]*a[5] - a[3]*a[4])/D,
+ a[0]*(a[5]-b[5])^2/D^2 + a[3]*(a[5]-b[5])/D + a[5]};
+ y = quadraticroots(c[0], c[1], c[2]);
+ for (int i = 0; i < y.length; ++i) {
+ c = new real[] {a[0], a[1]*y[i]+a[3], a[2]*y[i]^2 + a[4]*y[i]+a[5]};
+ x = quadraticroots(c[0], c[1], c[2]);
+ for (int j = 0; j < x.length; ++j) {
+ if(abs(b[0]*x[j]^2 + b[1]*x[j]*y[i]+b[2]*y[i]^2 + b[3]*x[j]+b[4]*y[i]+b[5]) < 1e-5)
+ P.push(point(R, (x[j], y[i])));
+ }
+ }
+ return P;
+ } else {
+ if(abs(a[5]-b[5]) < e) abort("intersectionpoints: intersection of identical conics.");
+ }
+ }
+ }
+ for (int i = 0; i < x.length; ++i) {
+ c = new real[] {a[2], a[1]*x[i]+a[4], a[0]*x[i]^2 + a[3]*x[i]+a[5]};
+ y = quadraticroots(c[0], c[1], c[2]);
+ for (int j = 0; j < y.length; ++j) {
+ if(abs(b[0]*x[i]^2 + b[1]*x[i]*y[j]+b[2]*y[j]^2 + b[3]*x[i]+b[4]*y[j]+b[5]) < 1e-5)
+ P.push(point(R, (x[i], y[j])));
+ }
+ }
+ return P;
+}
+
+/*<asyxml><struct signature="conic"><code></asyxml>*/
+struct conic
+{/*<asyxml></code><documentation></documentation><property type = "real" signature="e,p,h"><code></asyxml>*/
+ real e, p, h;/*<asyxml></code><documentation>BE CAREFUL: h = distance(F, D) and p = h * e (http://en.wikipedia.org/wiki/Ellipse)
+ While http://mathworld.wolfram.com/ takes p = distance(F,D).</documentation></property><property type = "point" signature="F"><code></asyxml>*/
+ point F;/*<asyxml></code><documentation>Focus.</documentation></property><property type = "line" signature="D"><code></asyxml>*/
+ line D;/*<asyxml></code><documentation>Directrix.</documentation></property><property type = "line" signature="l"><code></asyxml>*/
+ line[] l;/*<asyxml></code><documentation>Case of degenerated conic (not yet implemented !).</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+bool degenerate(conic c)
+{
+ return !finite(c.p) || !finite(c.h);
+}
+
+/*ANCconic conic(point, line, real)ANC*/
+conic conic(point F, line l, real e)
+{/*DOC
+ The conic section define by the eccentricity 'e', the focus 'F'
+ and the directrix 'l'.
+ Note that an eccentricity equal to 0 defines a circle centered at F,
+ with a radius equal at the distance from 'F' to 'l'.
+ If the coordinate system of 'F' and 'l' are not identical, the conic is
+ attached to 'defaultcoordsys'.
+ DOC*/
+ if(e < 0) abort("conic: 'e' can't be negative.");
+ conic oc;
+ point[] P = standardizecoordsys(F, l.A, l.B);
+ line ll;
+ ll = line(P[1], P[2]);
+ oc.e = e < epsgeo ? 0 : e; // Handle case of circle.
+ oc.F = P[0];
+ oc.D = ll;
+ oc.h = distance(P[0], ll);
+ oc.p = abs(e) < epsgeo ? oc.h : e * oc.h;
+ return oc;
+}
+
+/*<asyxml><struct signature="circle"><code></asyxml>*/
+struct circle
+{/*<asyxml></code><documentation>All the calculus with this structure will be as exact as Asymptote can do.
+ For a full precision, you must not cast 'circle' to 'path' excepted for drawing routines.</documentation></asyxml>*/
+ /*<asyxml><property type = "point" signature="C"><code></asyxml>*/
+ point C;/*<asyxml></code><documentation>Center</documentation></property><property><code></asyxml>*/
+ real r;/*<asyxml></code><documentation>Radius</documentation></property><property><code></asyxml>*/
+ line l;/*<asyxml></code><documentation>If the radius is infinite, this line is used instead of circle.</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+bool degenerate(circle c)
+{
+ return !finite(c.r);
+}
+
+line line(circle c){
+ if(finite(c.r)) abort("Circle can not be casted to line here.");
+ return c.l;
+}
+
+/*<asyxml><struct signature="ellipse"><code></asyxml>*/
+struct ellipse
+{/*<asyxml></code><documentation>Look at <html><a href = "http://mathworld.wolfram.com/Ellipse.html">http://mathworld.wolfram.com/Ellipse.html</a></html></documentation></asyxml>*/
+ /*<asyxml><property type = "point" signature="F1,F2,C"><code></asyxml>*/
+ restricted point F1,F2,C;/*<asyxml></code><documentation>Foci and center.</documentation></property><property type = "real" signature="a,b,c,e,p"><code></asyxml>*/
+ restricted real a,b,c,e,p;/*<asyxml></code></property><property type = "real" signature="angle"><code></asyxml>*/
+ restricted real angle;/*<asyxml></code><documentation>Value is degrees(F1 - F2).</documentation></property><property type = "line" signature="D1,D2"><code></asyxml>*/
+ restricted line D1,D2;/*<asyxml></code><documentation>Directrices.</documentation></property><property type = "line" signature="l"><code></asyxml>*/
+ line l;/*<asyxml></code><documentation>If one axis is infinite, this line is used instead of ellipse.</documentation></property></asyxml>*/
+ /*<asyxml><method type = "void" signature="init(point,point,real)"><code></asyxml>*/
+ void init(point f1, point f2, real a)
+ {/*<asyxml></code><documentation>Ellipse given by foci and semimajor axis</documentation></method></asyxml>*/
+ point[] P = standardizecoordsys(f1, f2);
+ this.F1 = P[0];
+ this.F2 = P[1];
+ this.angle = abs(P[1]-P[0]) < 10 * epsgeo ? 0 : degrees(P[1]-P[0]);
+ this.C = (P[0] + P[1])/2;
+ this.a = a;
+ if(!finite(a)) {
+ this.l = line(P[0], P[1]);
+ this.b = infinity;
+ this.e = 0;
+ this.c = 0;
+ } else {
+ this.c = abs(C - P[0]);
+ this.b = this.c < epsgeo ? a : sqrt(a^2 - c^2); // Handle case of circle.
+ this.e = this.c < epsgeo ? 0 : this.c/a; // Handle case of circle.
+ if(this.e >= 1) abort("ellipse.init: wrong parameter: e >= 1.");
+ this.p = a * (1 - this.e^2);
+ if (this.c != 0) {// directrix is not set for a circle.
+ point A = this.C + (a^2/this.c) * unit(P[0]-this.C);
+ this.D1 = line(A, A + rotateO(90) * unit(A - this.C));
+ this.D2 = reverse(rotate(180, C) * D1);
+ }
+ }
+ }
+}/*<asyxml></struct></asyxml>*/
+
+bool degenerate(ellipse el)
+{
+ return (!finite(el.a) || !finite(el.b));
+}
+
+/*<asyxml><struct signature="parabola"><code></asyxml>*/
+struct parabola
+{/*<asyxml></code><documentation>Look at <html><a href = "http://mathworld.wolfram.com/Parabola.html">http://mathworld.wolfram.com/Parabola.html</a></html></documentation><property type = "point" signature="F,V"><code></asyxml>*/
+ restricted point F,V;/*<asyxml></code><documentation>Focus and vertex</documentation></property><property type = "real" signature="a,p,e = 1"><code></asyxml>*/
+ restricted real a,p,e = 1;/*<asyxml></code></property><property type = "real" signature="angle"><code></asyxml>*/
+ restricted real angle;/*<asyxml></code><documentation>Angle, in degrees, of the line (FV).</documentation></property><property type = "line" signature="D"><code></asyxml>*/
+ restricted line D;/*<asyxml></code><documentation>Directrix</documentation></property><property type = "pair" signature="bmin,bmax"><code></asyxml>*/
+ pair bmin, bmax;/*<asyxml></code><documentation>The (left, bottom) and (right, top) coordinates of region bounding box for drawing the parabola.
+ If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/
+
+ /*<asyxml><method type = "void" signature="init(point,line)"><code></asyxml>*/
+ void init(point F, line directrix)
+ {/*<asyxml></code><documentation>Parabola given by focus and directrix.</documentation></method></asyxml>*/
+ point[] P = standardizecoordsys(F, directrix.A, directrix.B);
+ line l = line(P[1], P[2]);
+ this.F = P[0];
+ this.D = l;
+ this.a = distance(P[0], l)/2;
+ this.p = 2 * a;
+ this.V = 0.5 * (F + projection(D) * P[0]);
+ this.angle = degrees(F - V);
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><struct signature="hyperbola"><code></asyxml>*/
+struct hyperbola
+{/*<asyxml></code><documentation><html>Look at <a href = "http://mathworld.wolfram.com/Hyperbola.html">http://mathworld.wolfram.com/Hyperbola.html</a></html></documentation><property type = "point" signature="F1,F2"><code></asyxml>*/
+ restricted point F1,F2;/*<asyxml></code><documentation>Foci.</documentation></property><property type = "point" signature="C,V1,V2"><code></asyxml>*/
+ restricted point C,V1,V2;/*<asyxml></code><documentation>Center and vertices.</documentation></property><property type = "real" signature="a,b,c,e,p"><code></asyxml>*/
+ restricted real a,b,c,e,p;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="angle"><code></asyxml>*/
+ restricted real angle;/*<asyxml></code><documentation>Angle,in degrees,of the line (F1F2).</documentation></property><property type = "line" signature="D1,D2,A1,A2"><code></asyxml>*/
+ restricted line D1,D2,A1,A2;/*<asyxml></code><documentation>Directrices and asymptotes.</documentation></property><property type = "pair" signature="bmin,bmax"><code></asyxml>*/
+ pair bmin, bmax; /*<asyxml></code><documentation>The (left, bottom) and (right, top) coordinates of region bounding box for drawing the hyperbola.
+ If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/
+
+ /*<asyxml><method type = "void" signature="init(point,point,real)"><code></asyxml>*/
+ void init(point f1, point f2, real a)
+ {/*<asyxml></code><documentation>Hyperbola given by foci and semimajor axis.</documentation></method></asyxml>*/
+ point[] P = standardizecoordsys(f1, f2);
+ this.F1 = P[0];
+ this.F2 = P[1];
+ this.angle = degrees(F2 - F1);
+ this.a = a;
+ this.C = (P[0] + P[1])/2;
+ this.c = abs(C - P[0]);
+ this.e = this.c/a;
+ if(this.e <= 1) abort("hyperbola.init: wrong parameter: e <= 1.");
+ this.b = a * sqrt(this.e^2 - 1);
+ this.p = a * (this.e^2 - 1);
+ point A = this.C + (a^2/this.c) * unit(P[0]-this.C);
+ this.D1 = line(A, A + rotateO(90) * unit(A - this.C));
+ this.D2 = reverse(rotate(180, C) * D1);
+ this.V1 = C + a * unit(F1 - C);
+ this.V2 = C + a * unit(F2 - C);
+ this.A1 = line(C, V1 + b * unit(rotateO(-90) * (C - V1)));
+ this.A2 = line(C, V1 + b * unit(rotateO(90) * (C - V1)));
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><variable type="int" signature="conicnodesfactor"><code></asyxml>*/
+int conicnodesfactor = 1;/*<asyxml></code><documentation>Factor for the node number of all conics.</documentation></variable></asyxml>*/
+
+/*<asyxml><variable type="int" signature="circlenodesnumberfactor"><code></asyxml>*/
+int circlenodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the node number of circles.</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="circlenodesnumber(real)"><code></asyxml>*/
+int circlenodesnumber(real r)
+{/*<asyxml></code><documentation>Return the number of nodes for drawing a circle of radius 'r'.</documentation></function></asyxml>*/
+ if (circlenodesnumberfactor < 100)
+ warning("circlenodesnumberfactor",
+ "variable 'circlenodesnumberfactor' may be too small.");
+ int oi = ceil(circlenodesnumberfactor * abs(r)^0.1);
+ oi = 45 * floor(oi/45);
+ return oi == 0 ? 4 : conicnodesfactor * oi;
+}
+
+/*<asyxml><function type="int" signature="circlenodesnumber(real,real,real)"><code></asyxml>*/
+int circlenodesnumber(real r, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the number of nodes to draw a circle arc.</documentation></function></asyxml>*/
+ return (r > 0) ?
+ ceil(circlenodesnumber(r) * abs(angle1 - angle2)/360) :
+ ceil(circlenodesnumber(r) * abs((1 - abs(angle1 - angle2)/360)));
+}
+
+/*<asyxml><variable type="int" signature="ellispenodesnumberfactor"><code></asyxml>*/
+int ellipsenodesnumberfactor = 250;/*<asyxml></code><documentation>Factor for the node number of ellispe (non-circle).</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real)"><code></asyxml>*/
+int ellipsenodesnumber(real a, real b)
+{/*<asyxml></code><documentation>Return the number of nodes to draw a ellipse of axis 'a' and 'b'.</documentation></function></asyxml>*/
+ if (ellipsenodesnumberfactor < 250)
+ write("ellipsenodesnumberfactor",
+ "variable 'ellipsenodesnumberfactor' maybe too small.");
+ int tmp = circlenodesnumberfactor;
+ circlenodesnumberfactor = ellipsenodesnumberfactor;
+ int oi = circlenodesnumber(max(abs(a), abs(b))/min(abs(a), abs(b)));
+ circlenodesnumberfactor = tmp;
+ return conicnodesfactor * oi;
+}
+
+/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real,real)"><code></asyxml>*/
+int ellipsenodesnumber(real a, real b, real angle1, real angle2, bool dir)
+{/*<asyxml></code><documentation>Return the number of nodes to draw an ellipse arc.</documentation></function></asyxml>*/
+ real d;
+ real da = angle2 - angle1;
+ if(dir) {
+ d = angle1 < angle2 ? da : 360 + da;
+ } else {
+ d = angle1 < angle2 ? -360 + da : da;
+ }
+ int n = floor(ellipsenodesnumber(a, b) * abs(d)/360);
+ return n < 5 ? 5 : n;
+}
+
+/*<asyxml><variable type="int" signature="parabolanodesnumberfactor"><code></asyxml>*/
+int parabolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of parabolas.</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="parabolanodesnumber(parabola,real,real)"><code></asyxml>*/
+int parabolanodesnumber(parabola p, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the number of nodes for drawing a parabola.</documentation></function></asyxml>*/
+ return conicnodesfactor * floor(0.01 * parabolanodesnumberfactor * abs(angle1 - angle2));
+}
+
+/*<asyxml><variable type="int" signature="hyperbolanodesnumberfactor"><code></asyxml>*/
+int hyperbolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of hyperbolas.</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="hyperbolanodesnumber(hyperbola,real,real)"><code></asyxml>*/
+int hyperbolanodesnumber(hyperbola h, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the number of nodes for drawing an hyperbola.</documentation></function></asyxml>*/
+ return conicnodesfactor * floor(0.01 * hyperbolanodesnumberfactor * abs(angle1 - angle2)/h.e);
+}
+
+/*<asyxml><operator type = "conic" signature="+(conic,explicit point)"><code></asyxml>*/
+conic operator +(conic c, explicit point M)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F + M, c.D + M, c.e);
+}
+/*<asyxml><operator type = "conic" signature="-(conic,explicit point)"><code></asyxml>*/
+conic operator -(conic c, explicit point M)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F - M, c.D - M, c.e);
+}
+/*<asyxml><operator type = "conic" signature="+(conic,explicit pair)"><code></asyxml>*/
+conic operator +(conic c, explicit pair m)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ point M = point(c.F.coordsys, m);
+ return conic(c.F + M, c.D + M, c.e);
+}
+/*<asyxml><operator type = "conic" signature="-(conic,explicit pair)"><code></asyxml>*/
+conic operator -(conic c, explicit pair m)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ point M = point(c.F.coordsys, m);
+ return conic(c.F - M, c.D - M, c.e);
+}
+/*<asyxml><operator type = "conic" signature="+(conic,vector)"><code></asyxml>*/
+conic operator +(conic c, vector v)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F + v, c.D + v, c.e);
+}
+/*<asyxml><operator type = "conic" signature="-(conic,vector)"><code></asyxml>*/
+conic operator -(conic c, vector v)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F - v, c.D - v, c.e);
+}
+
+/*<asyxml><function type="coordsys" signature="coordsys(conic)"><code></asyxml>*/
+coordsys coordsys(conic co)
+{/*<asyxml></code><documentation>Return the coordinate system of 'co'.</documentation></function></asyxml>*/
+ return co.F.coordsys;
+}
+
+/*<asyxml><function type="conic" signature="changecoordsys(coordsys,conic)"><code></asyxml>*/
+conic changecoordsys(coordsys R, conic co)
+{/*<asyxml></code><documentation>Change the coordinate system of 'co' to 'R'</documentation></function></asyxml>*/
+ line l = changecoordsys(R, co.D);
+ point F = changecoordsys(R, co.F);
+ return conic(F, l, co.e);
+}
+
+/*<asyxml><typedef type = "polarconicroutine" return = "path" params = "conic, real, real, int, bool"><code></asyxml>*/
+typedef path polarconicroutine(conic co, real angle1, real angle2, int n, bool direction);/*<asyxml></code><documentation>Routine type used to draw conics from 'angle1' to 'angle2'</documentation></typedef></asyxml>*/
+
+/*<asyxml><function type="path" signature="arcfromfocus(conic,real,real,int,bool)"><code></asyxml>*/
+path arcfromfocus(conic co, real angle1, real angle2, int n = 400, bool direction = CCW)
+{/*<asyxml></code><documentation>Return the path of the conic section 'co' from angle1 to angle2 in degrees,
+ drawing in the given direction, with n nodes.</documentation></function></asyxml>*/
+ guide op;
+ if (n < 1) return op;
+ if (angle1 > angle2) {
+ path g = arcfromfocus(co, angle2, angle1, n, !direction);
+ return g == nullpath ? g : reverse(g);
+ }
+ point O = projection(co.D) * co.F;
+ pair i = unit(locate(co.F) - locate(O));
+ pair j = rotate(90) * i;
+ coordsys Rp = cartesiansystem(co.F, i, j);
+ real a1 = direction ? radians(angle1) : radians(angle2);
+ real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi;
+ real step = n == 1 ? 0 : (a2 - a1)/(n - 1);
+ real a, r;
+ for (int i = 0; i < n; ++i) {
+ a = a1 + i * step;
+ if(co.e >= 1) {
+ r = 1 - co.e * cos(a);
+ if(r > epsgeo) {
+ r = co.p/r;
+ op = op--Rp * Rp.polar(r, a);
+ }
+ } else {
+ r = co.p/(1 - co.e * cos(a));
+ op = op..Rp * Rp.polar(r, a);
+ }
+ }
+ if(co.e < 1 && abs(abs(a2 - a1) - 2 * pi) < epsgeo) op = (path)op..cycle;
+
+ return (direction ? op : op == nullpath ? op :reverse(op));
+}
+
+/*<asyxml><variable type="polarconicroutine" signature="currentpolarconicroutine"><code></asyxml>*/
+polarconicroutine currentpolarconicroutine = arcfromfocus;/*<asyxml></code><documentation>Default routine used to cast conic section to path.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="point" signature="angpoint(conic,real)"><code></asyxml>*/
+point angpoint(conic co, real angle)
+{/*<asyxml></code><documentation>Return the point of 'co' whose the angular (in degrees)
+ coordinate is 'angle' (mesured from the focus of 'co', relatively
+ to its 'natural coordinate system').</documentation></function></asyxml>*/
+ coordsys R = coordsys(co);
+ return point(R, point(arcfromfocus(co, angle, angle, 1, CCW), 0)/R);
+}
+
+/*<asyxml><operator type = "bool" signature="@(point,conic)"><code></asyxml>*/
+bool operator @(point M, conic co)
+{/*<asyxml></code><documentation>Return true iff 'M' on 'co'.</documentation></operator></asyxml>*/
+ if(co.e == 0) return abs(abs(co.F - M) - co.p) < 10 * epsgeo;
+ return abs(co.e * distance(M, co.D) - abs(co.F - M)) < 10 * epsgeo;
+}
+
+/*<asyxml><function type="coordsys" signature="coordsys(ellipse)"><code></asyxml>*/
+coordsys coordsys(ellipse el)
+{/*<asyxml></code><documentation>Return the coordinate system of 'el'.</documentation></function></asyxml>*/
+ return el.F1.coordsys;
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(ellipse)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(ellipse el)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of the ellipse 'el'.</documentation></function></asyxml>*/
+ if(degenerate(el)) return cartesiansystem(el.l.A, el.l.u, el.l.v);
+ pair O = locate(el.C);
+ pair i = el.e == 0 ? el.C.coordsys.i : unit(locate(el.F1) - O);
+ pair j = rotate(90) * i;
+ return cartesiansystem(O, i, j);
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(parabola)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(parabola p)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of a parabola,
+ so that Origin = vertex of 'p' and directrix: x = -a.</documentation></function></asyxml>*/
+ point A = projection(p.D) * p.F;
+ pair O = locate((A + p.F)/2);
+ pair i = unit(locate(p.F) - O);
+ pair j = rotate(90) * i;
+ return cartesiansystem(O, i, j);
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(hyperbola h)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of an hyperbola.</documentation></function></asyxml>*/
+ pair O = locate(h.C);
+ pair i = unit(locate(h.F2) - O);
+ pair j = rotate(90) * i;
+ return cartesiansystem(O, i, j);
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,point,real)"><code></asyxml>*/
+ellipse ellipse(point F1, point F2, real a)
+{/*<asyxml></code><documentation>Return the ellipse whose the foci are 'F1' and 'F2'
+ and the semimajor axis is 'a'.</documentation></function></asyxml>*/
+ ellipse oe;
+ oe.init(F1, F2, a);
+ return oe;
+}
+
+/*<asyxml><constant type = "bool" signature="byfoci,byvertices"><code></asyxml>*/
+restricted bool byfoci = true, byvertices = false;/*<asyxml></code><documentation>Constants useful for the routine 'hyperbola(point P1, point P2, real ae, bool byfoci = byfoci)'</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,real,bool)"><code></asyxml>*/
+hyperbola hyperbola(point P1, point P2, real ae, bool byfoci = byfoci)
+{/*<asyxml></code><documentation>if 'byfoci = true':
+ return the hyperbola whose the foci are 'P1' and 'P2'
+ and the semimajor axis is 'ae'.
+ else return the hyperbola whose vertexes are 'P1' and 'P2' with eccentricity 'ae'.</documentation></function></asyxml>*/
+ hyperbola oh;
+ point[] P = standardizecoordsys(P1, P2);
+ if(byfoci) {
+ oh.init(P[0], P[1], ae);
+ } else {
+ real a = abs(P[0]-P[1])/2;
+ vector V = unit(P[0]-P[1]);
+ point F1 = P[0] + a * (ae - 1) * V;
+ point F2 = P[1]-a * (ae - 1) * V;
+ oh.init(F1, F2, a);
+ }
+ return oh;
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,point,point)"><code></asyxml>*/
+ellipse ellipse(point F1, point F2, point M)
+{/*<asyxml></code><documentation>Return the ellipse passing through 'M' whose the foci are 'F1' and 'F2'.</documentation></function></asyxml>*/
+ point P[] = standardizecoordsys(false, F1, F2, M);
+ real a = abs(F1 - M) + abs(F2 - M);
+ return ellipse(F1, F2, finite(a) ? a/2 : a);
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,real,real,real)"><code></asyxml>*/
+ellipse ellipse(point C, real a, real b, real angle = 0)
+{/*<asyxml></code><documentation>Return the ellipse centered at 'C' with semimajor axis 'a' along C--C + dir(angle),
+ semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/
+ ellipse oe;
+ coordsys R = C.coordsys;
+ angle += degrees(R.i);
+ if(a < b) {angle += 90; real tmp = a; a = b; b = tmp;}
+ if(finite(a) && finite(b)) {
+ real c = sqrt(abs(a^2 - b^2));
+ point f1, f2;
+ if(abs(a - b) < epsgeo) {
+ f1 = C; f2 = C;
+ } else {
+ f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R);
+ f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R);
+ }
+ oe.init(f1, f2, a);
+ } else {
+ if(finite(b) || !finite(a)) oe.init(C, C + R.polar(1, angle), infinity);
+ else oe.init(C, C + R.polar(1, 90 + angle), infinity);
+ }
+ return oe;
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(bqe)"><code></asyxml>*/
+ellipse ellipse(bqe bqe)
+{/*<asyxml></code><documentation>Return the ellipse a[0] * x^2 + a[1] * xy + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0
+ given in the coordinate system of 'bqe' with a[i] = bque.a[i].
+ <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/>
+ <url href = "http://mathworld.wolfram.com/Ellipse.html"/>.</documentation></function></asyxml>*/
+ bqe lbqe = changecoordsys(defaultcoordsys, bqe);
+ real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
+ coordsys R = bqe.coordsys;
+ string message = "ellipse: the given equation is not an equation of an ellipse.";
+ real u = b^2 * g + d^2 * c + f^2 * a;
+ real delta = a * c * g + b * f * d + d * b * f - u;
+ if(abs(delta) < epsgeo) abort(message);
+ real j = b^2 - a * c;
+ real i = a + c;
+ real dd = j * (sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a);
+ real ddd = j * (-sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a);
+
+ if(abs(ddd) < epsgeo || abs(dd) < epsgeo ||
+ j >= -epsgeo || delta/sgnd(i) > 0) abort(message);
+
+ real x = (c * d - b * f)/j, y = (a * f - b * d)/j;
+ // real dir = abs(b) < epsgeo ? 0 : pi/2-0.5 * acot(0.5 * (c-a)/b);
+ real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b);
+ if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2;
+ real cd = cos(dir), sd = sin(dir);
+ real t = a * cd^2 - 2 * b * cd * sd + c * sd^2;
+ real tt = a * sd^2 + 2 * b * cd * sd + c * cd^2;
+ real gg = -g + ((d * cd - f * sd)^2)/t + ((d * sd + f * cd)^2)/tt;
+ t = t/gg; tt = tt/gg;
+ // The equation of the ellipse is t * (x - center.x)^2 + tt * (y - center.y)^2 = 1;
+ real aa, bb;
+ aa = sqrt(2 * (u - 2 * b * d * f - a * c * g)/dd);
+ bb = sqrt(2 * (u - 2 * b * d * f - a * c * g)/ddd);
+ a = t > tt ? max(aa, bb) : min(aa, bb);
+ b = t > tt ? min(aa, bb) : max(aa, bb);
+ return ellipse(point(R, (x, y)/R),
+ a, b, degrees(pi/2 - dir - angle(R.i)));
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,point,point,point,point)"><code></asyxml>*/
+ellipse ellipse(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the ellipse passing through the five points (if possible)</documentation></function></asyxml>*/
+ return ellipse(bqe(M1, M2, M3, M4, M5));
+}
+
+/*<asyxml><function type="bool" signature="inside(ellipse,point)"><code></asyxml>*/
+bool inside(ellipse el, point M)
+{/*<asyxml></code><documentation>Return 'true' iff 'M' is inside 'el'.</documentation></function></asyxml>*/
+ return abs(el.F1 - M) + abs(el.F2 - M) - 2 * el.a < -epsgeo;
+}
+
+/*<asyxml><function type="bool" signature="inside(parabola,point)"><code></asyxml>*/
+bool inside(parabola p, point M)
+{/*<asyxml></code><documentation>Return 'true' if 'M' is inside 'p'.</documentation></function></asyxml>*/
+ return distance(p.D, M) - abs(p.F - M) > epsgeo;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,line)"><code></asyxml>*/
+parabola parabola(point F, line l)
+{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and directrix is 'l'.</documentation></function></asyxml>*/
+ parabola op;
+ op.init(F, l);
+ return op;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,point)"><code></asyxml>*/
+parabola parabola(point F, point vertex)
+{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and vertex is 'vertex'.</documentation></function></asyxml>*/
+ parabola op;
+ point[] P = standardizecoordsys(F, vertex);
+ point A = rotate(180, P[1]) * P[0];
+ point B = A + rotateO(90) * unit(P[1]-A);
+ op.init(P[0], line(A, B));
+ return op;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,real,real)"><code></asyxml>*/
+parabola parabola(point F, real a, real angle)
+{/*<asyxml></code><documentation>Return the parabola whose focus is F, latus rectum is 4a and
+ the angle of the axis of symmetry (in the coordinate system of F) is 'angle'.</documentation></function></asyxml>*/
+ parabola op;
+ coordsys R = F.coordsys;
+ point A = F - point(R, R.polar(2a, radians(angle)));
+ point B = A + point(R, R.polar(1, radians(90 + angle)));
+ op.init(F, line(A, B));
+ return op;
+}
+
+/*<asyxml><function type="bool" signature="isparabola(bqe)"><code></asyxml>*/
+bool isparabola(bqe bqe)
+{/*<asyxml></code><documentation>Return true iff 'bqe' is the equation of a parabola.</documentation></function></asyxml>*/
+ bqe lbqe = changecoordsys(defaultcoordsys, bqe);
+ real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
+ real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
+ return (abs(delta) > epsgeo && abs(b^2 - a * c) < epsgeo);
+}
+
+/*<asyxml><function type="parabola" signature="parabola(bqe)"><code></asyxml>*/
+parabola parabola(bqe bqe)
+{/*<asyxml></code><documentation>Return the parabola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]).
+ <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/>
+ <url href = "http://mathworld.wolfram.com/Parabola.html"/></documentation></function></asyxml>*/
+ bqe lbqe = changecoordsys(defaultcoordsys, bqe);
+ real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
+ string message = "parabola: the given equation is not an equation of a parabola.";
+ real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
+ if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) > 10 * epsgeo) abort(message);
+ real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b);
+ if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2;
+ real cd = cos(dir), sd = sin(dir);
+ real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2;
+ real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2;
+ real dp = d * cd - f * sd;
+ real fp = d * sd + f * cd;
+ real gp = g;
+ parabola op;
+ coordsys R = bqe.coordsys;
+ // The equation of the parabola is ap * x'^2 + cp * y'^2 + 2dp * x'+2fp * y'+gp = 0
+ if (abs(ap) < epsgeo) {/* directrix parallel to the rotated(dir) y-axis
+ equation: (y-vertex.y)^2 = 4 * a * (x-vertex)
+ */
+ pair pvertex = rotate(degrees(-dir)) * (0.5(-gp + fp^2/cp)/dp, -fp/cp);
+ real a = -0.5 * dp/cp;
+ point vertex = point(R, pvertex/R);
+ point focus = point(R, (pvertex + a * expi(-dir))/R);
+ op = parabola(focus, vertex);
+
+ } else {/* directrix parallel to the rotated(dir) x-axis
+ equation: (x-vertex)^2 = 4 * a * (y-vertex.y)
+ */
+ pair pvertex = rotate(degrees(-dir)) * (-dp/ap, 0.5 * (-gp + dp^2/ap)/fp);
+ real a = -0.5 * fp/ap;
+ point vertex = point(R, pvertex/R);
+ point focus = point(R, (pvertex + a * expi(pi/2 - dir))/R);
+ op = parabola(focus, vertex);
+ }
+ return op;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,point,point,line)"><code></asyxml>*/
+parabola parabola(point M1, point M2, point M3, line l)
+{/*<asyxml></code><documentation>Return the parabola passing through the three points with its directix
+ parallel to the line 'l'.</documentation></function></asyxml>*/
+ coordsys R;
+ pair[] pts;
+ if (samecoordsys(M1, M2, M3)) {
+ R = M1.coordsys;
+ } else {
+ R = defaultcoordsys;
+ }
+ real gle = degrees(l);
+ coordsys Rp = cartesiansystem(R.O, rotate(gle) * R.i, rotate(gle) * R.j);
+ pts = new pair[] {coordinates(changecoordsys(Rp, M1)),
+ coordinates(changecoordsys(Rp, M2)),
+ coordinates(changecoordsys(Rp, M3))};
+ real[][] M;
+ real[] x;
+ for (int i = 0; i < 3; ++i) {
+ M[i] = new real[] {pts[i].x, pts[i].y, 1};
+ x[i] = -pts[i].x^2;
+ }
+ real[] coef = solve(M, x);
+ return parabola(changecoordsys(R, bqe(Rp, 1, 0, 0, coef[0], coef[1], coef[2])));
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,point,point,point,point)"><code></asyxml>*/
+parabola parabola(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the parabola passing through the five points.</documentation></function></asyxml>*/
+ return parabola(bqe(M1, M2, M3, M4, M5));
+}
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(point,real,real,real)"><code></asyxml>*/
+hyperbola hyperbola(point C, real a, real b, real angle = 0)
+{/*<asyxml></code><documentation>Return the hyperbola centered at 'C' with semimajor axis 'a' along C--C + dir(angle),
+ semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/
+ hyperbola oh;
+ coordsys R = C.coordsys;
+ angle += degrees(R.i);
+ real c = sqrt(a^2 + b^2);
+ point f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R);
+ point f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R);
+ oh.init(f1, f2, a);
+ return oh;
+}
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(bqe)"><code></asyxml>*/
+hyperbola hyperbola(bqe bqe)
+{/*<asyxml></code><documentation>Return the hyperbola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]).
+ <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/>
+ <url href = "http://mathworld.wolfram.com/Hyperbola.html"/></documentation></function></asyxml>*/
+ bqe lbqe = changecoordsys(defaultcoordsys, bqe);
+ real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
+ string message = "hyperbola: the given equation is not an equation of a hyperbola.";
+ real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
+ if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) < 0) abort(message);
+ real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b);
+ real cd = cos(dir), sd = sin(dir);
+ real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2;
+ real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2;
+ real dp = d * cd - f * sd;
+ real fp = d * sd + f * cd;
+ real gp = -g + dp^2/ap + fp^2/cp;
+ hyperbola op;
+ coordsys R = bqe.coordsys;
+ real j = b^2 - a * c;
+ point C = point(R, ((c * d - b * f)/j, (a * f - b * d)/j)/R);
+ real aa = gp/ap, bb = gp/cp;
+ real a = sqrt(abs(aa)), b = sqrt(abs(bb));
+ if(aa < 0) {dir -= pi/2; aa = a; a = b; b = aa;}
+ return hyperbola(C, a, b, degrees(-dir - angle(R.i)));
+}
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,point,point,point)"><code></asyxml>*/
+hyperbola hyperbola(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the hyperbola passing through the five points (if possible).</documentation></function></asyxml>*/
+ return hyperbola(bqe(M1, M2, M3, M4, M5));
+}
+
+/*<asyxml><function type="hyperbola" signature="conj(hyperbola)"><code></asyxml>*/
+hyperbola conj(hyperbola h)
+{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
+ return hyperbola(h.C, h.b, h.a, 90 + h.angle);
+}
+
+/*<asyxml><function type="circle" signature="circle(explicit point,real)"><code></asyxml>*/
+circle circle(explicit point C, real r)
+{/*<asyxml></code><documentation>Circle given by center and radius.</documentation></function></asyxml>*/
+ circle oc = new circle;
+ oc.C = C;
+ oc.r = r;
+ if(!finite(r)) oc.l = line(C, C + vector(C.coordsys, (1, 0)));
+ return oc;
+}
+
+/*<asyxml><function type="circle" signature="circle(point,point)"><code></asyxml>*/
+circle circle(point A, point B)
+{/*<asyxml></code><documentation>Return the circle of diameter AB.</documentation></function></asyxml>*/
+ real r;
+ circle oc;
+ real a = abs(A), b = abs(B);
+ if(finite(a) && finite(b)) {
+ oc = circle((A + B)/2, abs(A - B)/2);
+ } else {
+ oc.r = infinity;
+ if(finite(abs(A))) oc.l = line(A, A + unit(B));
+ else {
+ if(finite(abs(B))) oc.l = line(B, B + unit(A));
+ else if(finite(abs(A - B)/2)) oc = circle((A + B)/2, abs(A - B)/2); else
+ oc.l = line(A, B);
+ }
+ }
+ return oc;
+}
+
+/*<asyxml><function type="circle" signature="circle(segment)"><code></asyxml>*/
+circle circle(segment s)
+{/*<asyxml></code><documentation>Return the circle of diameter 's'.</documentation></function></asyxml>*/
+ return circle(s.A, s.B);
+}
+
+/*<asyxml><function type="point" signature="circumcenter(point,point,point)"><code></asyxml>*/
+point circumcenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the circumcenter of triangle ABC.</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B, C);
+ coordsys R = P[0].coordsys;
+ pair a = A, b = B, c = C;
+ pair mAB = (a + b)/2;
+ pair mAC = (a + c)/2;
+ pair pp = extension(mAB, rotate(90, mAB) * a, mAC, rotate(90, mAC) * c);
+ return point(R, pp/R);
+}
+
+/*<asyxml><function type="circle" signature="circle(point,point,point)"><code></asyxml>*/
+circle circle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/
+ if(collinear(A - B, A - C)) {
+ circle oc;
+ oc.r = infinity;
+ oc.C = (A + B + C)/3;
+ oc.l = line(oc.C, oc.C == A ? B : A);
+ return oc;
+ }
+ point c = circumcenter(A, B, C);
+ return circle(c, abs(c - A));
+}
+
+/*<asyxml><function type="circle" signature="circumcircle(point,point,point)"><code></asyxml>*/
+circle circumcircle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/
+ return circle(A, B, C);
+}
+
+/*<asyxml><operator type = "circle" signature="*(real,explicit circle)"><code></asyxml>*/
+circle operator *(real x, explicit circle c)
+{/*<asyxml></code><documentation>Multiply the radius of 'c'.</documentation></operator></asyxml>*/
+ return finite(c.r) ? circle(c.C, x * c.r) : c;
+}
+circle operator *(int x, explicit circle c)
+{
+ return finite(c.r) ? circle(c.C, x * c.r) : c;
+}
+/*<asyxml><operator type = "circle" signature="/(explicit circle,real)"><code></asyxml>*/
+circle operator /(explicit circle c, real x)
+{/*<asyxml></code><documentation>Divide the radius of 'c'</documentation></operator></asyxml>*/
+ return finite(c.r) ? circle(c.C, c.r/x) : c;
+}
+circle operator /(explicit circle c, int x)
+{
+ return finite(c.r) ? circle(c.C, c.r/x) : c;
+}
+/*<asyxml><operator type = "circle" signature="+(explicit circle,explicit point)"><code></asyxml>*/
+circle operator +(explicit circle c, explicit point M)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C + M, c.r);
+}
+/*<asyxml><operator type = "circle" signature="-(explicit circle,explicit point)"><code></asyxml>*/
+circle operator -(explicit circle c, explicit point M)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C - M, c.r);
+}
+/*<asyxml><operator type = "circle" signature="+(explicit circle,pair)"><code></asyxml>*/
+circle operator +(explicit circle c, pair m)
+{/*<asyxml></code><documentation>Translation of 'c'.
+ 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/
+ return circle(c.C + m, c.r);
+}
+/*<asyxml><operator type = "circle" signature="-(explicit circle,pair)"><code></asyxml>*/
+circle operator -(explicit circle c, pair m)
+{/*<asyxml></code><documentation>Translation of 'c'.
+ 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/
+ return circle(c.C - m, c.r);
+}
+/*<asyxml><operator type = "circle" signature="+(explicit circle,vector)"><code></asyxml>*/
+circle operator +(explicit circle c, vector m)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C + m, c.r);
+}
+/*<asyxml><operator type = "circle" signature="-(explicit circle,vector)"><code></asyxml>*/
+circle operator -(explicit circle c, vector m)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C - m, c.r);
+}
+/*<asyxml><operator type = "real" signature="^(point,explicit circle)"><code></asyxml>*/
+real operator ^(point M, explicit circle c)
+{/*<asyxml></code><documentation>The power of 'M' with respect to the circle 'c'</documentation></operator></asyxml>*/
+ return xpart((abs(locate(M) - locate(c.C)), c.r)^2);
+}
+/*<asyxml><operator type = "bool" signature="@(point,explicit circle)"><code></asyxml>*/
+bool operator @(point M, explicit circle c)
+{/*<asyxml></code><documentation>Return true iff 'M' is on the circle 'c'.</documentation></operator></asyxml>*/
+ return finite(c.r) ?
+ abs(abs(locate(M) - locate(c.C)) - abs(c.r)) <= 10 * epsgeo :
+ M @ c.l;
+}
+
+/*<asyxml><operator type = "ellipse" signature="cast(circle)"><code></asyxml>*/
+ellipse operator cast(circle c)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return finite(c.r) ? ellipse(c.C, c.r, c.r, 0) : ellipse(c.l.A, c.l.B, infinity);
+}
+
+/*<asyxml><operator type = "circle" signature="cast(ellipse)"><code></asyxml>*/
+circle operator cast(ellipse el)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ circle oc;
+ bool infb = (!finite(el.a) || !finite(el.b));
+ if(!infb && abs(el.a - el.b) > epsgeo)
+ abort("Can not cast ellipse with different axis values to circle");
+ oc = circle(el.C, infb ? infinity : el.a);
+ oc.l = el.l.copy();
+ return oc;
+}
+
+/*<asyxml><operator type = "ellipse" signature="cast(conic)"><code></asyxml>*/
+ellipse operator cast(conic co)
+{/*<asyxml></code><documentation>Cast a conic to an ellipse (can be a circle).</documentation></operator></asyxml>*/
+ if(degenerate(co) && co.e < 1) return ellipse(co.l[0].A, co.l[0].B, infinity);
+ ellipse oe;
+ if(co.e < 1) {
+ real a = co.p/(1 - co.e^2);
+ real c = co.e * a;
+ vector v = co.D.v;
+ if(!sameside(co.D.A + v, co.F, co.D)) v = -v;
+ point f2 = co.F + 2 * c * v;
+ f2 = changecoordsys(co.F.coordsys, f2);
+ oe = a == 0 ? ellipse(co.F, co.p, co.p, 0) : ellipse(co.F, f2, a);
+ } else
+ abort("casting: The conic section is not an ellipse.");
+ return oe;
+}
+
+/*<asyxml><operator type = "parabola" signature="cast(conic)"><code></asyxml>*/
+parabola operator cast(conic co)
+{/*<asyxml></code><documentation>Cast a conic to a parabola.</documentation></operator></asyxml>*/
+ parabola op;
+ if(abs(co.e - 1) > epsgeo) abort("casting: The conic section is not a parabola.");
+ op.init(co.F, co.D);
+ return op;
+}
+
+/*<asyxml><operator type = "conic" signature="cast(parabola)"><code></asyxml>*/
+conic operator cast(parabola p)
+{/*<asyxml></code><documentation>Cast a parabola to a conic section.</documentation></operator></asyxml>*/
+ return conic(p.F, p.D, 1);
+}
+
+/*<asyxml><operator type = "hyperbola" signature="cast(conic)"><code></asyxml>*/
+hyperbola operator cast(conic co)
+{/*<asyxml></code><documentation>Cast a conic section to an hyperbola.</documentation></operator></asyxml>*/
+ hyperbola oh;
+ if(co.e > 1) {
+ real a = co.p/(co.e^2 - 1);
+ real c = co.e * a;
+ vector v = co.D.v;
+ if(sameside(co.D.A + v, co.F, co.D)) v = -v;
+ point f2 = co.F + 2 * c * v;
+ f2 = changecoordsys(co.F.coordsys, f2);
+ oh = hyperbola(co.F, f2, a);
+ } else
+ abort("casting: The conic section is not an hyperbola.");
+ return oh;
+}
+
+/*<asyxml><operator type = "conic" signature="cast(hyperbola)"><code></asyxml>*/
+conic operator cast(hyperbola h)
+{/*<asyxml></code><documentation>Hyperbola to conic section.</documentation></operator></asyxml>*/
+ return conic(h.F1, h.D1, h.e);
+}
+
+/*<asyxml><operator type = "conic" signature="cast(ellipse)"><code></asyxml>*/
+conic operator cast(ellipse el)
+{/*<asyxml></code><documentation>Ellipse to conic section.</documentation></operator></asyxml>*/
+ conic oc;
+ if(abs(el.c) > epsgeo) {
+ real x = el.a^2/el.c;
+ point O = (el.F1 + el.F2)/2;
+ point A = O + x * unit(el.F1 - el.F2);
+ oc = conic(el.F1, perpendicular(A, line(el.F1, el.F2)), el.e);
+ } else {//The ellipse is a circle
+ coordsys R = coordsys(el);
+ point M = el.F1 + point(R, R.polar(el.a, 0));
+ line l = line(rotate(90, M) * el.F1, M);
+ oc = conic(el.F1, l, 0);
+ }
+ if(degenerate(el)) {
+ oc.p = infinity;
+ oc.h = infinity;
+ oc.l = new line[]{el.l};
+ }
+ return oc;
+}
+
+/*<asyxml><operator type = "conic" signature="cast(circle)"><code></asyxml>*/
+conic operator cast(circle c)
+{/*<asyxml></code><documentation>Circle to conic section.</documentation></operator></asyxml>*/
+ return (conic)((ellipse)c);
+}
+
+/*<asyxml><operator type = "circle" signature="cast(conic)"><code></asyxml>*/
+circle operator cast(conic c)
+{/*<asyxml></code><documentation>Conic section to circle.</documentation></operator></asyxml>*/
+ ellipse el = (ellipse)c;
+ circle oc;
+ if(abs(el.a - el.b) < epsgeo) {
+ oc = circle(el.C, el.a);
+ if(degenerate(c)) oc.l = c.l[0];
+ }
+ else abort("Can not cast this conic to a circle");
+ return oc;
+}
+
+/*<asyxml><operator type = "ellipse" signature="*(transform,ellipse)"><code></asyxml>*/
+ellipse operator *(transform t, ellipse el)
+{/*<asyxml></code><documentation>Provide transform * ellipse.</documentation></operator></asyxml>*/
+ if(!degenerate(el)) {
+ point[] ep;
+ for (int i = 0; i < 360; i += 72) {
+ ep.push(t * angpoint(el, i));
+ }
+ ellipse oe = ellipse(ep[0], ep[1], ep[2], ep[3], ep[4]);
+ if(angpoint(oe, 0) != ep[0]) return ellipse(oe.F2, oe.F1, oe.a);
+ return oe;
+ }
+ return ellipse(t * el.l.A, t * el.l.B, infinity);
+}
+
+/*<asyxml><operator type = "parabola" signature="*(transform,parabola)"><code></asyxml>*/
+parabola operator *(transform t, parabola p)
+{/*<asyxml></code><documentation>Provide transform * parabola.</documentation></operator></asyxml>*/
+ point[] P;
+ P.push(t * angpoint(p, 45));
+ P.push(t * angpoint(p, -45));
+ P.push(t * angpoint(p, 180));
+ parabola op = parabola(P[0], P[1], P[2], t * p.D);
+ op.bmin = p.bmin;
+ op.bmax = p.bmax;
+
+ return op;
+}
+
+/*<asyxml><operator type = "ellipse" signature="*(transform,circle)"><code></asyxml>*/
+ellipse operator *(transform t, circle c)
+{/*<asyxml></code><documentation>Provide transform * circle.
+ For example, 'circle C = scale(2) * circle' and 'ellipse E = xscale(2) * circle' are valid
+ but 'circle C = xscale(2) * circle' is invalid.</documentation></operator></asyxml>*/
+ return t * ((ellipse)c);
+}
+
+/*<asyxml><operator type = "hyperbola" signature="*(transform,hyperbola)"><code></asyxml>*/
+hyperbola operator *(transform t, hyperbola h)
+{/*<asyxml></code><documentation>Provide transform * hyperbola.</documentation></operator></asyxml>*/
+ if (t == identity()) {
+ return h;
+ }
+
+ point[] ep;
+ for (int i = 90; i <= 270; i += 45) {
+ ep.push(t * angpoint(h, i));
+ }
+
+ hyperbola oe = hyperbola(ep[0], ep[1], ep[2], ep[3], ep[4]);
+ if(angpoint(oe, 90) != ep[0]) {
+ oe = hyperbola(oe.F2, oe.F1, oe.a);
+ }
+
+ oe.bmin = h.bmin;
+ oe.bmax = h.bmax;
+
+ return oe;
+}
+
+/*<asyxml><operator type = "conic" signature="*(transform,conic)"><code></asyxml>*/
+conic operator *(transform t, conic co)
+{/*<asyxml></code><documentation>Provide transform * conic.</documentation></operator></asyxml>*/
+ if(co.e < 1) return (t * ((ellipse)co));
+ if(co.e == 1) return (t * ((parabola)co));
+ return (t * ((hyperbola)co));
+}
+
+/*<asyxml><operator type = "ellipse" signature="*(real,ellipse)"><code></asyxml>*/
+ellipse operator *(real x, ellipse el)
+{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(x, el.C) * el'.</documentation></operator></asyxml>*/
+ return degenerate(el) ? el : ellipse(el.C, x * el.a, x * el.b, el.angle);
+}
+
+/*<asyxml><operator type = "ellipse" signature="/(ellipse,real)"><code></asyxml>*/
+ellipse operator /(ellipse el, real x)
+{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(1/x, el.C) * el'.</documentation></operator></asyxml>*/
+ return degenerate(el) ? el : ellipse(el.C, el.a/x, el.b/x, el.angle);
+}
+
+/*<asyxml><function type="path" signature="arcfromcenter(ellipse,real,real,int,bool)"><code></asyxml>*/
+path arcfromcenter(ellipse el, real angle1, real angle2,
+ bool direction=CCW,
+ int n=ellipsenodesnumber(el.a,el.b,angle1,angle2,direction))
+{/*<asyxml></code><documentation>Return the path of the ellipse 'el' from angle1 to angle2 in degrees,
+ drawing in the given direction, with n nodes.
+ The angles are mesured relatively to the axis (C,x-axis) where C is
+ the center of the ellipse.</documentation></function></asyxml>*/
+ if(degenerate(el)) abort("arcfromcenter: can not convert degenerated ellipse to path.");
+ if (angle1 > angle2)
+ return reverse(arcfromcenter(el, angle2, angle1, !direction, n));
+
+ guide op;
+ coordsys Rp=coordsys(el);
+ if (n < 1) return op;
+
+ interpolate join = operator ..;
+ real stretch = max(el.a/el.b, el.b/el.a);
+
+ if (stretch > 10) {
+ n *= floor(stretch/5);
+ join = operator --;
+ }
+
+ real a1 = direction ? radians(angle1) : radians(angle2);
+ real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi;
+ real step=(a2 - a1)/(n != 1 ? n-1 : 1);
+ real a, r;
+ real da = radians(el.angle);
+
+ for (int i=0; i < n; ++i) {
+ a = a1 + i * step;
+ r = el.b/sqrt(1 - (el.e * cos(a))^2);
+ op = join(op, Rp*Rp.polar(r, da + a));
+ }
+
+ return shift(el.C.x*Rp.i + el.C.y*Rp.j) * (direction ? op : reverse(op));
+}
+
+/*<asyxml><function type="path" signature="arcfromcenter(hyperbola,real,real,int,bool)"><code></asyxml>*/
+path arcfromcenter(hyperbola h, real angle1, real angle2,
+ int n = hyperbolanodesnumber(h, angle1, angle2),
+ bool direction = CCW)
+{/*<asyxml></code><documentation>Return the path of the hyperbola 'h' from angle1 to angle2 in degrees,
+ drawing in the given direction, with n nodes.
+ The angles are mesured relatively to the axis (C, x-axis) where C is
+ the center of the hyperbola.</documentation></function></asyxml>*/
+ guide op;
+ coordsys Rp = coordsys(h);
+ if (n < 1) return op;
+ if (angle1 > angle2) {
+ path g = reverse(arcfromcenter(h, angle2, angle1, n, !direction));
+ return g == nullpath ? g : reverse(g);
+ }
+ real a1 = direction ? radians(angle1) : radians(angle2);
+ real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi;
+ real step = (a2 - a1)/(n != 1 ? n - 1 : 1);
+ real a, r;
+ typedef guide interpolate(... guide[]);
+ interpolate join = operator ..;
+ real da = radians(h.angle);
+ for (int i = 0; i < n; ++i) {
+ a = a1 + i * step;
+ r = (h.b * cos(a))^2 - (h.a * sin(a))^2;
+ if(r > epsgeo) {
+ r = sqrt(h.a^2 * h.b^2/r);
+ op = join(op, Rp * Rp.polar(r, a + da));
+ join = operator ..;
+ } else join = operator --;
+ }
+ return shift(h.C.x * Rp.i + h.C.y * Rp.j)*
+ (direction ? op : op == nullpath ? op : reverse(op));
+}
+
+/*<asyxml><function type="path" signature="arcfromcenter(explicit conic,real,real,int,bool)"><code></asyxml>*/
+path arcfromcenter(explicit conic co, real angle1, real angle2,
+ int n, bool direction = CCW)
+{/*<asyxml></code><documentation>Use arcfromcenter(ellipse, ...) or arcfromcenter(hyperbola, ...) depending of
+ the eccentricity of 'co'.</documentation></function></asyxml>*/
+ path g;
+ if(co.e < 1)
+ g = arcfromcenter((ellipse)co, angle1,
+ angle2, direction, n);
+ else if(co.e > 1)
+ g = arcfromcenter((hyperbola)co, angle1,
+ angle2, n, direction);
+ else abort("arcfromcenter: does not exist for a parabola.");
+ return g;
+}
+
+/*<asyxml><constant type = "polarconicroutine" signature="fromCenter"><code></asyxml>*/
+restricted polarconicroutine fromCenter = arcfromcenter;/*<asyxml></code><documentation></documentation></constant></asyxml>*/
+/*<asyxml><constant type = "polarconicroutine" signature="fromFocus"><code></asyxml>*/
+restricted polarconicroutine fromFocus = arcfromfocus;/*<asyxml></code><documentation></documentation></constant></asyxml>*/
+
+/*<asyxml><function type="bqe" signature="equation(ellipse)"><code></asyxml>*/
+bqe equation(ellipse el)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of the ellipse in its coordinate system:
+ bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0.
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ pair[] pts;
+ for (int i = 0; i < 360; i += 72)
+ pts.push(locate(angpoint(el, i)));
+
+ real[][] M;
+ real[] x;
+ for (int i = 0; i < 5; ++i) {
+ M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1};
+ x[i] = -pts[i].x^2;
+ }
+ real[] coef = solve(M, x);
+ bqe bqe = changecoordsys(coordsys(el),
+ bqe(defaultcoordsys,
+ 1, coef[0], coef[1], coef[2], coef[3], coef[4]));
+ bqe.a = approximate(bqe.a);
+ return bqe;
+}
+
+/*<asyxml><function type="bqe" signature="equation(parabola)"><code></asyxml>*/
+bqe equation(parabola p)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of the parabola in its coordinate system.
+ bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ coordsys R = canonicalcartesiansystem(p);
+ parabola tp = changecoordsys(R, p);
+ point A = projection(tp.D) * point(R, (0, 0));
+ real a = abs(A);
+ return changecoordsys(coordsys(p),
+ bqe(R, 0, 0, 1, -4 * a, 0, 0));
+}
+
+/*<asyxml><function type="bqe" signature="equation(hyperbola)"><code></asyxml>*/
+bqe equation(hyperbola h)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of the hyperbola in its coordinate system.
+ bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ coordsys R = canonicalcartesiansystem(h);
+ return changecoordsys(coordsys(h),
+ bqe(R, 1/h.a^2, 0, -1/h.b^2, 0, 0, -1));
+}
+
+/*<asyxml><operator type = "path" signature="cast(ellipse)"><code></asyxml>*/
+path operator cast(ellipse el)
+{/*<asyxml></code><documentation>Cast ellipse to path.</documentation></operator></asyxml>*/
+ if(degenerate(el))
+ abort("Casting degenerated ellipse to path is not possible.");
+ int n = el.e == 0 ? circlenodesnumber(el.a) : ellipsenodesnumber(el.a, el.b);
+ return arcfromcenter(el, 0.0, 360, CCW, n)&cycle;
+}
+
+/*<asyxml><operator type = "path" signature="cast(circle)"><code></asyxml>*/
+path operator cast(circle c)
+{/*<asyxml></code><documentation>Cast circle to path.</documentation></operator></asyxml>*/
+ return (path)((ellipse)c);
+}
+
+/*<asyxml><function type="real[]" signature="bangles(picture,parabola)"><code></asyxml>*/
+real[] bangles(picture pic = currentpicture, parabola p)
+{/*<asyxml></code><documentation>Return the array {ma, Ma} where 'ma' and 'Ma' are respectively
+ the smaller and the larger angles for which the parabola 'p' is included
+ in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/
+ pair bmin, bmax;
+ pair[] b;
+ if (p.bmin == p.bmax) {
+ bmin = pic.userMin();
+ bmax = pic.userMax();
+ } else {
+ bmin = p.bmin;bmax = p.bmax;
+ }
+ if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax)))
+ return new real[] {0, 0};
+ b[0] = bmin;
+ b[1] = (bmax.x, bmin.y);
+ b[2] = bmax;
+ b[3] = (bmin.x, bmax.y);
+ real[] eq = changecoordsys(defaultcoordsys, equation(p)).a;
+ pair[] inter;
+ for (int i = 0; i < 4; ++i) {
+ pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq);
+ for (int j = 0; j < tmp.length; ++j) {
+ if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo)
+ inter.push(tmp[j]);
+ }
+ }
+ pair F = p.F, V = p.V;
+ real d = degrees(F - V);
+ real[] a = sequence(new real(int n){
+ return (360 - d + degrees(inter[n]-F))%360;
+ }, inter.length);
+ real ma = a.length != 0 ? min(a) : 0, Ma= a.length != 0 ? max(a) : 0;
+ return new real[] {ma, Ma};
+}
+
+/*<asyxml><function type="real[][]" signature="bangles(picture,hyperbola)"><code></asyxml>*/
+real[][] bangles(picture pic = currentpicture, hyperbola h)
+{/*<asyxml></code><documentation>Return the array {{ma1, Ma1}, {ma2, Ma2}} where 'maX' and 'MaX' are respectively
+ the smaller and the bigger angles (from h.FX) for which the hyperbola 'h' is included
+ in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/
+ pair bmin, bmax;
+ pair[] b;
+ if (h.bmin == h.bmax) {
+ bmin = pic.userMin();
+ bmax = pic.userMax();
+ } else {
+ bmin = h.bmin;bmax = h.bmax;
+ }
+ if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax)))
+ return new real[][] {{0, 0}, {0, 0}};
+ b[0] = bmin;
+ b[1] = (bmax.x, bmin.y);
+ b[2] = bmax;
+ b[3] = (bmin.x, bmax.y);
+ real[] eq = changecoordsys(defaultcoordsys, equation(h)).a;
+ pair[] inter0, inter1;
+ pair C = locate(h.C);
+ pair F1 = h.F1;
+ for (int i = 0; i < 4; ++i) {
+ pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq);
+ for (int j = 0; j < tmp.length; ++j) {
+ if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo) {
+ if(dot(F1 - C, tmp[j]-C) > 0) inter0.push(tmp[j]);
+ else inter1.push(tmp[j]);
+ }
+ }
+ }
+ real d = degrees(F1 - C);
+ real[] ma, Ma;
+ pair[][] inter = new pair[][] {inter0, inter1};
+ for (int i = 0; i < 2; ++i) {
+ real[] a = sequence(new real(int n){
+ return (360 - d + degrees(inter[i][n]-F1))%360;
+ }, inter[i].length);
+ ma[i] = a.length != 0 ? min(a) : 0;
+ Ma[i] = a.length != 0 ? max(a) : 0;
+ }
+ return new real[][] {{ma[0], Ma[0]}, {ma[1], Ma[1]}};
+}
+
+/*<asyxml><operator type = "path" signature="cast(parabola)"><code></asyxml>*/
+path operator cast(parabola p)
+{/*<asyxml></code><documentation>Cast parabola to path.
+ If possible, the returned path is restricted to the actual bounding box
+ of the current picture if the variables 'p.bmin' and 'p.bmax' are not set else
+ the bounding box of box(p.bmin, p.bmax) is used instead.</documentation></operator></asyxml>*/
+ real[] bangles = bangles(p);
+ int n = parabolanodesnumber(p, bangles[0], bangles[1]);
+ return arcfromfocus(p, bangles[0], bangles[1], n, CCW);
+}
+
+
+/*<asyxml><function type="void" signature="draw(picture,Label,circle,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "", circle c,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None,
+ margin margin = NoMargin, Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ if(degenerate(c)) draw(pic, L, c.l, align, p, arrow, legend, marker);
+ else draw(pic, L, (path)c, align, p, arrow, bar, margin, legend, marker);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,ellipse,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "", ellipse el,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None,
+ margin margin = NoMargin, Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation></documentation>Draw the ellipse 'el' if it is not degenerated else draw 'el.l'.</function></asyxml>*/
+ if(degenerate(el)) draw(pic, L, el.l, align, p, arrow, legend, marker);
+ else draw(pic, L, (path)el, align, p, arrow, bar, margin, legend, marker);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,parabola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "", parabola parabola,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None,
+ margin margin = NoMargin, Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation>Draw the parabola 'p' on 'pic' without (if possible) altering the
+ size of picture pic.</documentation></function></asyxml>*/
+ pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
+ // Reduce the bounds by the size of the pen and the margins.
+ m -= min(p); M -= max(p);
+ parabola.bmin = inverse(t) * m;
+ parabola.bmax = inverse(t) * M;
+ picture tmp;
+ path pp = t * ((path) (T * parabola));
+
+ if (pp != nullpath) {
+ draw(tmp, L, pp, align, p, arrow, bar, NoMargin, legend, marker);
+ add(f, tmp.fit());
+ }
+ }, true);
+
+ pair m = pic.userMin(), M = pic.userMax();
+ if(m != M) {
+ pic.addBox(truepoint(SW), truepoint(NE));
+ }
+}
+
+/*<asyxml><operator type = "path" signature="cast(hyperbola)"><code></asyxml>*/
+path operator cast(hyperbola h)
+{/*<asyxml></code><documentation>Cast hyperbola to path.
+ If possible, the returned path is restricted to the actual bounding box
+ of the current picture unless the variables 'h.bmin' and 'h.bmax'
+ are set; in this case the bounding box of box(h.bmin, h.bmax) is used instead.
+ Only the branch on the side of 'h.F1' is considered.</documentation></operator></asyxml>*/
+ real[][] bangles = bangles(h);
+ int n = hyperbolanodesnumber(h, bangles[0][0], bangles[0][1]);
+ return arcfromfocus(h, bangles[0][0], bangles[0][1], n, CCW);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,hyperbola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "", hyperbola h,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None,
+ margin margin = NoMargin, Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation>Draw the hyperbola 'h' on 'pic' without (if possible) altering the
+ size of the picture pic.</documentation></function></asyxml>*/
+ pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
+ // Reduce the bounds by the size of the pen and the margins.
+ m -= min(p); M -= max(p);
+ h.bmin = inverse(t) * m;
+ h.bmax = inverse(t) * M;
+ path hp;
+
+ picture tmp;
+ hp = t * ((path) (T * h));
+ if (hp != nullpath) {
+ draw(tmp, L, hp, align, p, arrow, bar, NoMargin, legend, marker);
+ }
+
+ hyperbola ht = hyperbola(h.F2, h.F1, h.a);
+ ht.bmin = h.bmin;
+ ht.bmax = h.bmax;
+
+ hp = t * ((path) (T * ht));
+ if (hp != nullpath) {
+ draw(tmp, "", hp, align, p, arrow, bar, NoMargin, marker);
+ }
+
+ add(f, tmp.fit());
+ }, true);
+
+ pair m = pic.userMin(), M = pic.userMax();
+ if(m != M)
+ pic.addBox(truepoint(SW), truepoint(NE));
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,explicit conic,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "", explicit conic co,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None,
+ margin margin = NoMargin, Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation>Use one of the routine 'draw(ellipse, ...)',
+ 'draw(parabola, ...)' or 'draw(hyperbola, ...)' depending of the value of eccentricity of 'co'.</documentation></function></asyxml>*/
+ if(co.e == 0)
+ draw(pic, L, (circle)co, align, p, arrow, bar, margin, legend, marker);
+ else
+ if(co.e < 1) draw(pic, L, (ellipse)co, align, p, arrow, bar, margin, legend, marker);
+ else
+ if(co.e == 1) draw(pic, L, (parabola)co, align, p, arrow, bar, margin, legend, marker);
+ else
+ if(co.e > 1) draw(pic, L, (hyperbola)co, align, p, arrow, bar, margin, legend, marker);
+ else abort("draw: unknown conic.");
+}
+
+/*<asyxml><function type="int" signature="conicnodesnumber(conic,real,real)"><code></asyxml>*/
+int conicnodesnumber(conic co, real angle1, real angle2, bool dir = CCW)
+{/*<asyxml></code><documentation>Return the number of node to draw a conic arc.</documentation></function></asyxml>*/
+ int oi;
+ if(co.e == 0) {
+ circle c = (circle)co;
+ oi = circlenodesnumber(c.r, angle1, angle2);
+ } else if(co.e < 1) {
+ ellipse el = (ellipse)co;
+ oi = ellipsenodesnumber(el.a, el.b, angle1, angle2, dir);
+ } else if(co.e == 1) {
+ parabola p = (parabola)co;
+ oi = parabolanodesnumber(p, angle1, angle2);
+ } else {
+ hyperbola h = (hyperbola)co;
+ oi = hyperbolanodesnumber(h, angle1, angle2);
+ }
+ return oi;
+}
+
+/*<asyxml><operator type = "path" signature="cast(conic)"><code></asyxml>*/
+path operator cast(conic co)
+{/*<asyxml></code><documentation>Cast conic section to path.</documentation></operator></asyxml>*/
+ if(co.e < 1) return (path)((ellipse)co);
+ if(co.e == 1) return (path)((parabola)co);
+ return (path)((hyperbola)co);
+}
+
+/*<asyxml><function type="bqe" signature="equation(explicit conic)"><code></asyxml>*/
+bqe equation(explicit conic co)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of conic section in its coordinate system:
+ bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0.
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ bqe obqe;
+ if(co.e == 0)
+ obqe = equation((circle)co);
+ else
+ if(co.e < 1) obqe = equation((ellipse)co);
+ else
+ if(co.e == 1) obqe = equation((parabola)co);
+ else
+ if(co.e > 1) obqe = equation((hyperbola)co);
+ else abort("draw: unknown conic.");
+ return obqe;
+}
+
+/*<asyxml><function type="string" signature="conictype(bqe)"><code></asyxml>*/
+string conictype(bqe bqe)
+{/*<asyxml></code><documentation>Returned values are "ellipse" or "parabola" or "hyperbola"
+ depending of the conic section represented by 'bqe'.</documentation></function></asyxml>*/
+ bqe lbqe = changecoordsys(defaultcoordsys, bqe);
+ string os = "degenerated";
+ real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
+ real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
+ if(abs(delta) < 10 * epsgeo) return os;
+ real J = a * c - b^2;
+ real I = a + c;
+ if(J > epsgeo) {
+ if(delta/I < -epsgeo);
+ os = "ellipse";
+ } else {
+ if(abs(J) < epsgeo) os = "parabola"; else os = "hyperbola";
+ }
+ return os;
+}
+
+/*<asyxml><function type="conic" signature="conic(point,point,point,point,point)"><code></asyxml>*/
+conic conic(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the conic passing through 'M1', 'M2', 'M3', 'M4' and 'M5' if the conic is not degenerated.</documentation></function></asyxml>*/
+ bqe bqe = bqe(M1, M2, M3, M4, M5);
+ string ct = conictype(bqe);
+ if(ct == "degenerated") abort("conic: degenerated conic passing through five points.");
+ if(ct == "ellipse") return ellipse(bqe);
+ if(ct == "parabola") return parabola(bqe);
+ return hyperbola(bqe);
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(explicit conic co)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e < 1) return canonicalcartesiansystem((ellipse)co);
+ else if(co.e == 1) return canonicalcartesiansystem((parabola)co);
+ return canonicalcartesiansystem((hyperbola)co);
+}
+
+/*<asyxml><function type="bqe" signature="canonical(bqe)"><code></asyxml>*/
+bqe canonical(bqe bqe)
+{/*<asyxml></code><documentation>Return the bivariate quadratic equation relative to the
+ canonical coordinate system of the conic section represented by 'bqe'.</documentation></function></asyxml>*/
+ string type = conictype(bqe);
+ if(type == "") abort("canonical: the equation can not be performed.");
+ bqe obqe;
+ if(type == "ellipse") {
+ ellipse el = ellipse(bqe);
+ obqe = changecoordsys(canonicalcartesiansystem(el), equation(el));
+ } else {
+ if(type == "parabola") {
+ parabola p = parabola(bqe);
+ obqe = changecoordsys(canonicalcartesiansystem(p), equation(p));
+ } else {
+ hyperbola h = hyperbola(bqe);
+ obqe = changecoordsys(canonicalcartesiansystem(h), equation(h));
+ }
+ }
+ return obqe;
+}
+
+/*<asyxml><function type="conic" signature="conic(bqe)"><code></asyxml>*/
+conic conic(bqe bqe)
+{/*<asyxml></code><documentation>Return the conic section represented by the bivariate quartic equation 'bqe'.</documentation></function></asyxml>*/
+ string type = conictype(bqe);
+ if(type == "") abort("canonical: the equation can not be performed.");
+ conic oc;
+ if(type == "ellipse") {
+ oc = ellipse(bqe);
+ } else {
+ if(type == "parabola") oc = parabola(bqe); else oc = hyperbola(bqe);
+ }
+ return oc;
+}
+
+/*<asyxml><function type="real" signature="arclength(circle)"><code></asyxml>*/
+real arclength(circle c)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return c.r * 2 * pi;
+}
+
+/*<asyxml><function type="real" signature="focusToCenter(ellipse,real)"><code></asyxml>*/
+real focusToCenter(ellipse el, real a)
+{/*<asyxml></code><documentation>Return the angle relatively to the center of 'el' for the angle 'a'
+ given relatively to the focus of 'el'.</documentation></function></asyxml>*/
+ pair p = point(fromFocus(el, a, a, 1, CCW), 0);
+ pair c = locate(el.C);
+ real d = degrees(p - c) - el.angle;
+ d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15
+ return d%(sgnd(a) * 360);
+}
+
+/*<asyxml><function type="real" signature="centerToFocus(ellipse,real)"><code></asyxml>*/
+real centerToFocus(ellipse el, real a)
+{/*<asyxml></code><documentation>Return the angle relatively to the focus of 'el' for the angle 'a'
+ given relatively to the center of 'el'.</documentation></function></asyxml>*/
+ pair P = point(fromCenter(el, a, a, 1, CCW), 0);
+ pair F1 = locate(el.F1);
+ pair F2 = locate(el.F2);
+ real d = degrees(P - F1) - degrees(F2 - F1);
+ d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15
+ return d%(sgnd(a) * 360);
+}
+
+/*<asyxml><function type="real" signature="arclength(ellipse)"><code></asyxml>*/
+real arclength(ellipse el)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return degenerate(el) ? infinity : 4 * el.a * elle(pi/2, el.e);
+}
+
+/*<asyxml><function type="real" signature="arclength(ellipse,real,real,bool,polarconicroutine)"><code></asyxml>*/
+real arclength(ellipse el, real angle1, real angle2,
+ bool direction = CCW,
+ polarconicroutine polarconicroutine = currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the length of the arc of the ellipse between 'angle1'
+ and 'angle2'.
+ 'angle1' and 'angle2' must be in the interval ]-360;+oo[ if polarconicroutine = fromFocus,
+ ]-oo;+oo[ if polarconicroutine = fromCenter.</documentation></function></asyxml>*/
+ if(degenerate(el)) return infinity;
+ if(angle1 > angle2) return arclength(el, angle2, angle1, !direction, polarconicroutine);
+ // path g;int n = 1000;
+ // if(el.e == 0) g = arcfromcenter(el, angle1, angle2, n, direction);
+ // if(el.e != 1) g = polarconicroutine(el, angle1, angle2, n, direction);
+ // write("with path = ", arclength(g));
+ if(polarconicroutine == fromFocus) {
+ // dot(point(fromFocus(el, angle1, angle1, 1, CCW), 0), 2mm + blue);
+ // dot(point(fromFocus(el, angle2, angle2, 1, CCW), 0), 2mm + blue);
+ // write("fromfocus1 = ", angle1);
+ // write("fromfocus2 = ", angle2);
+ real gle1 = focusToCenter(el, angle1);
+ real gle2 = focusToCenter(el, angle2);
+ if((gle1 - gle2) * (angle1 - angle2) > 0) {
+ angle1 = gle1; angle2 = gle2;
+ } else {
+ angle1 = gle2; angle2 = gle1;
+ }
+ // dot(point(fromCenter(el, angle1, angle1, 1, CCW), 0), 1mm + red);
+ // dot(point(fromCenter(el, angle2, angle2, 1, CCW), 0), 1mm + red);
+ // write("fromcenter1 = ", angle1);
+ // write("fromcenter2 = ", angle2);
+ }
+ if(angle1 < 0 || angle2 < 0) return arclength(el, 180 + angle1, 180 + angle2, direction, fromCenter);
+ real a1 = direction ? angle1 : angle2;
+ real a2 = direction ? angle2 : angle1 + 360;
+ real elleq = el.a * elle(pi/2, el.e);
+ real S(real a)
+ {//Return the arclength from 0 to the angle 'a' (in degrees)
+ // given form the center of the ellipse.
+ real gle = atan(el.a * Tan(a)/el.b)+
+ pi * (((a%90 == 0 && a != 0) ? floor(a/90) - 1 : floor(a/90)) -
+ ((a%180 == 0) ? 0 : floor(a/180)) -
+ (a%360 == 0 ? floor(a/(360)) : 0));
+ /* // Uncomment to visualize the used branches
+ unitsize(2cm, 1cm);
+ import graph;
+
+ real xmin = 0, xmax = 3pi;
+
+ xlimits( xmin, xmax);
+ ylimits( 0, 10);
+ yaxis( "y" , LeftRight(), RightTicks(pTick=.8red, ptick = lightgrey, extend = true));
+ xaxis( "x - value", BottomTop(), Ticks(Label("$%.2f$", red), Step = pi/2, step = pi/4, pTick=.8red, ptick = lightgrey, extend = true));
+
+ real p2 = pi/2;
+ real f(real t)
+ {
+ return atan(0.6 * tan(t))+
+ pi * ((t%p2 == 0 && t != 0) ? floor(t/p2) - 1 : floor(t/p2)) -
+ ((t%pi == 0) ? 0 : pi * floor(t/pi)) - (t%(2pi) == 0 ? pi * floor(t/(2 * pi)) : 0);
+ }
+
+ draw(graph(f, xmin, xmax, 100));
+ write(degrees(f(pi/2)));
+ write(degrees(f(pi)));
+ write(degrees(f(3pi/2)));
+ write(degrees(f(2pi)));
+ draw(graph(new real(real t){return t;}, xmin, xmax, 3));
+ */
+ return elleq - el.a * elle(pi/2 - gle, el.e);
+ }
+ return S(a2) - S(a1);
+}
+
+/*<asyxml><function type="real" signature="arclength(parabola,real)"><code></asyxml>*/
+real arclength(parabola p, real angle)
+{/*<asyxml></code><documentation>Return the arclength from 180 to 'angle' given from focus in the
+ canonical coordinate system of 'p'.</documentation></function></asyxml>*/
+ real a = p.a; /* In canonicalcartesiansystem(p) the equation of p
+ is x = y^2/(4a) */
+ // integrate(sqrt(1 + (x/(2 * a))^2), x);
+ real S(real t){return 0.5 * t * sqrt(1 + t^2/(4 * a^2)) + a * asinh(t/(2 * a));}
+ real R(real gle){return 2 * a/(1 - Cos(gle));}
+ real t = Sin(angle) * R(angle);
+ return S(t);
+}
+
+/*<asyxml><function type="real" signature="arclength(parabola,real,real)"><code></asyxml>*/
+real arclength(parabola p, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the arclength from 'angle1' to 'angle2' given from
+ focus in the canonical coordinate system of 'p'</documentation></function></asyxml>*/
+ return arclength(p, angle1) - arclength(p, angle2);
+}
+
+/*<asyxml><function type="real" signature="arclength(parabola p)"><code></asyxml>*/
+real arclength(parabola p)
+{/*<asyxml></code><documentation>Return the length of the arc of the parabola bounded to the bounding
+ box of the current picture.</documentation></function></asyxml>*/
+ real[] b = bangles(p);
+ return arclength(p, b[0], b[1]);
+}
+// *........................CONICS.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.......................ABSCISSA........................*
+/*<asyxml><struct signature="abscissa"><code></asyxml>*/
+struct abscissa
+{/*<asyxml></code><documentation>Provide abscissa structure on a curve used in the routine-like 'point(object, abscissa)'
+ where object can be 'line','segment','ellipse','circle','conic'...</documentation><property type = "real" signature="x"><code></asyxml>*/
+ real x;/*<asyxml></code><documentation>The abscissa value.</documentation></property><property type = "int" signature="system"><code></asyxml>*/
+ int system;/*<asyxml></code><documentation>0 = relativesystem; 1 = curvilinearsystem; 2 = angularsystem; 3 = nodesystem</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/
+ polarconicroutine polarconicroutine = fromCenter;/*<asyxml></code><documentation>The routine used with angular system and two foci conic section.
+ Possible values are 'formCenter' and 'formFocus'.</documentation></property></asyxml>*/
+ /*<asyxml><method type = "abscissa" signature="copy()"><code></asyxml>*/
+ abscissa copy()
+ {/*<asyxml></code><documentation>Return a copy of this abscissa.</documentation></method></asyxml>*/
+ abscissa oa = new abscissa;
+ oa.x = this.x;
+ oa.system = this.system;
+ oa.polarconicroutine = this.polarconicroutine;
+ return oa;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><constant type = "int" signature="relativesystem,curvilinearsystem,angularsystem,nodesystem"><code></asyxml>*/
+restricted int relativesystem = 0, curvilinearsystem = 1, angularsystem = 2, nodesystem = 3;/*<asyxml></code><documentation>Constant used to set the abscissa system.</documentation></constant></asyxml>*/
+
+/*<asyxml><operator type = "abscissa" signature="cast(explicit position)"><code></asyxml>*/
+abscissa operator cast(explicit position position)
+{/*<asyxml></code><documentation>Cast position to abscissa.
+ If 'position' is relative, the abscissa is relative else it's a curvilinear abscissa.</documentation></operator></asyxml>*/
+ abscissa oarcc;
+ oarcc.x = position.position.x;
+ oarcc.system = position.relative ? relativesystem : curvilinearsystem;
+ return oarcc;
+}
+
+/*<asyxml><operator type = "abscissa" signature="+(real,explicit abscissa)"><code></asyxml>*/
+abscissa operator +(real x, explicit abscissa a)
+{/*<asyxml></code><documentation>Provide 'real + abscissa'.
+ Return abscissa b so that b.x = a.x + x.
+ +(explicit abscissa, real), -(real, explicit abscissa) and -(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/
+ abscissa oa = a.copy();
+ oa.x = a.x + x;
+ return oa;
+}
+
+abscissa operator +(explicit abscissa a, real x)
+{
+ return x + a;
+}
+abscissa operator +(int x, explicit abscissa a)
+{
+ return ((real)x) + a;
+}
+
+/*<asyxml><operator type = "abscissa" signature="-(explicit abscissa a)"><code></asyxml>*/
+abscissa operator -(explicit abscissa a)
+{/*<asyxml></code><documentation>Return the abscissa b so that b.x = -a.x.</documentation></operator></asyxml>*/
+ abscissa oa;
+ oa.system = a.system;
+ oa.x = -a.x;
+ return oa;
+}
+
+abscissa operator -(real x, explicit abscissa a)
+{
+ abscissa oa;
+ oa.system = a.system;
+ oa.x = x - a.x;
+ return oa;
+}
+abscissa operator -(explicit abscissa a, real x)
+{
+ abscissa oa;
+ oa.system = a.system;
+ oa.x = a.x - x;
+ return oa;
+}
+abscissa operator -(int x, explicit abscissa a)
+{
+ return ((real)x) - a;
+}
+
+/*<asyxml><operator type = "abscissa" signature="*(real,abscissa)"><code></asyxml>*/
+abscissa operator *(real x, explicit abscissa a)
+{/*<asyxml></code><documentation>Provide 'real * abscissa'.
+ Return abscissa b so that b.x = x * a.x.
+ *(explicit abscissa, real), /(real, explicit abscissa) and /(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/
+ abscissa oa;
+ oa.system = a.system;
+ oa.x = a.x * x;
+ return oa;
+}
+abscissa operator *(explicit abscissa a, real x)
+{
+ return x * a;
+}
+
+abscissa operator /(real x, explicit abscissa a)
+{
+ abscissa oa;
+ oa.system = a.system;
+ oa.x = x/a.x;
+ return oa;
+}
+abscissa operator /(explicit abscissa a, real x)
+{
+ abscissa oa;
+ oa.system = a.system;
+ oa.x = a.x/x;
+ return oa;
+}
+
+abscissa operator /(int x, explicit abscissa a)
+{
+ return ((real)x)/a;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(real)"><code></asyxml>*/
+abscissa relabscissa(real x)
+{/*<asyxml></code><documentation>Return a relative abscissa.</documentation></function></asyxml>*/
+ return (abscissa)(Relative(x));
+}
+abscissa relabscissa(int x)
+{
+ return (abscissa)(Relative(x));
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(real)"><code></asyxml>*/
+abscissa curabscissa(real x)
+{/*<asyxml></code><documentation>Return a curvilinear abscissa.</documentation></function></asyxml>*/
+ return (abscissa)((position)x);
+}
+abscissa curabscissa(int x)
+{
+ return (abscissa)((position)x);
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(real,polarconicroutine)"><code></asyxml>*/
+abscissa angabscissa(real x, polarconicroutine polarconicroutine = currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return a angular abscissa.</documentation></function></asyxml>*/
+ abscissa oarcc;
+ oarcc.x = x;
+ oarcc.polarconicroutine = polarconicroutine;
+ oarcc.system = angularsystem;
+ return oarcc;
+}
+abscissa angabscissa(int x, polarconicroutine polarconicroutine = currentpolarconicroutine)
+{
+ return angabscissa((real)x, polarconicroutine);
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(real)"><code></asyxml>*/
+abscissa nodabscissa(real x)
+{/*<asyxml></code><documentation>Return an abscissa as time on the path.</documentation></function></asyxml>*/
+ abscissa oarcc;
+ oarcc.x = x;
+ oarcc.system = nodesystem;
+ return oarcc;
+}
+abscissa nodabscissa(int x)
+{
+ return nodabscissa((real)x);
+}
+
+/*<asyxml><operator type = "abscissa" signature="cast(real)"><code></asyxml>*/
+abscissa operator cast(real x)
+{/*<asyxml></code><documentation>Cast real to abscissa, precisely 'nodabscissa'.</documentation></operator></asyxml>*/
+ return nodabscissa(x);
+}
+abscissa operator cast(int x)
+{
+ return nodabscissa((real)x);
+}
+
+/*<asyxml><function type="point" signature="point(circle,abscissa)"><code></asyxml>*/
+point point(circle c, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'c' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ coordsys R = c.C.coordsys;
+ if (l.system == nodesystem)
+ return point(R, point((path)c, l.x)/R);
+ if (l.system == relativesystem)
+ return c.C + point(R, R.polar(c.r, 2 * pi * l.x));
+ if (l.system == curvilinearsystem)
+ return c.C + point(R, R.polar(c.r, l.x/c.r));
+ if (l.system == angularsystem)
+ return c.C + point(R, R.polar(c.r, radians(l.x)));
+ abort("point: bad abscissa system.");
+ return (0, 0);
+}
+
+/*<asyxml><function type="point" signature="point(ellipse,abscissa)"><code></asyxml>*/
+point point(ellipse el, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'el' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ if(el.e == 0) return point((circle)el, l);
+ coordsys R = coordsys(el);
+ if (l.system == nodesystem)
+ return point(R, point((path)el, l.x)/R);
+ if (l.system == relativesystem) {
+ return point(el, curabscissa((l.x%1) * arclength(el)));
+ }
+ if (l.system == curvilinearsystem) {
+ real a1 = 0, a2 = 360, cx = 0;
+ real aout = a1;
+ real x = abs(l.x)%arclength(el);
+ while (abs(cx - x) > epsgeo) {
+ aout = (a1 + a2)/2;
+ cx = arclength(el, 0, aout, CCW, fromCenter); //fromCenter is speeder
+ if(cx > x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2;
+ }
+ path pel = fromCenter(el, sgn(l.x) * aout, sgn(l.x) * aout, 1, CCW);
+ return point(R, point(pel, 0)/R);
+ }
+ if (l.system == angularsystem) {
+ return point(R, point(l.polarconicroutine(el, l.x, l.x, 1, CCW), 0)/R);
+ }
+ abort("point: bad abscissa system.");
+ return (0, 0);
+}
+
+/*<asyxml><function type="point" signature="point(parabola,abscissa)"><code></asyxml>*/
+point point(parabola p, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'p' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ coordsys R = coordsys(p);
+ if (l.system == nodesystem)
+ return point(R, point((path)p, l.x)/R);
+ if (l.system == relativesystem) {
+ real[] b = bangles(p);
+ real al = sgn(l.x) > 0 ? arclength(p, 180, b[1]) : arclength(p, 180, b[0]);
+ return point(p, curabscissa(abs(l.x) * al));
+ }
+ if (l.system == curvilinearsystem) {
+ real a1 = 1e-3, a2 = 360 - 1e-3, cx = infinity;
+ while (abs(cx - l.x) > epsgeo) {
+ cx = arclength(p, 180, (a1 + a2)/2);
+ if(cx > l.x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2;
+ }
+ path pp = fromFocus(p, a1, a1, 1, CCW);
+ return point(R, point(pp, 0)/R);
+ }
+ if (l.system == angularsystem) {
+ return point(R, point(fromFocus(p, l.x, l.x, 1, CCW), 0)/R);
+ }
+ abort("point: bad abscissa system.");
+ return (0, 0);
+}
+
+/*<asyxml><function type="point" signature="point(hyperbola,abscissa)"><code></asyxml>*/
+point point(hyperbola h, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'h' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ coordsys R = coordsys(h);
+ if (l.system == nodesystem)
+ return point(R, point((path)h, l.x)/R);
+ if (l.system == relativesystem) {
+ abort("point(hyperbola, relativeSystem) is not implemented...
+Try relpoint((path)your_hyperbola, x);");
+ }
+ if (l.system == curvilinearsystem) {
+ abort("point(hyperbola, curvilinearSystem) is not implemented...");
+ }
+ if (l.system == angularsystem) {
+ return point(R, point(l.polarconicroutine(h, l.x, l.x, 1, CCW), 0)/R);
+ }
+ abort("point: bad abscissa system.");
+ return (0, 0);
+}
+
+/*<asyxml><function type="abscissa" signature="point(conic,point)"><code></asyxml>*/
+point point(explicit conic co, abscissa l)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e == 0) return point((circle)co, l);
+ if(co.e < 1) return point((ellipse)co, l);
+ if(co.e == 1) return point((parabola)co, l);
+ return point((hyperbola)co, l);
+}
+
+
+/*<asyxml><function type="point" signature="point(line,abscissa)"><code></asyxml>*/
+point point(line l, abscissa x)
+{/*<asyxml></code><documentation>Return the point of 'l' which has the abscissa 'l.x' according to the abscissa system 'l.system'.
+ Note that the origin is l.A, and point(l, relabscissa(x)) returns l.A + x.x * vector(l.B - l.A).</documentation></function></asyxml>*/
+ coordsys R = l.A.coordsys;
+ if (x.system == nodesystem)
+ return l.A + (x.x < 0 ? 0 : x.x > 1 ? 1 : x.x) * vector(l.B - l.A);
+ if (x.system == relativesystem)
+ return l.A + x.x * vector(l.B - l.A);
+ if (x.system == curvilinearsystem)
+ return l.A + x.x * l.u;
+ if (x.system == angularsystem)
+ abort("point: what the meaning of angular abscissa on line ?.");
+ abort("point: bad abscissa system.");
+ return (0, 0);
+}
+
+/*<asyxml><function type="point" signature="point(line,real)"><code></asyxml>*/
+point point(line l, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node l.A and l.B (x <= 0 means l.A, x >=1 means l.B).</documentation></function></asyxml>*/
+ return point(l, nodabscissa(x));
+}
+point point(line l, explicit int x)
+{
+ return point(l, nodabscissa(x));
+}
+
+/*<asyxml><function type="circle" signature="point(explicit circle,explicit real)"><code></asyxml>*/
+point point(explicit circle c, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
+ return point(c, nodabscissa(x));
+}
+point point(explicit circle c, explicit int x)
+{
+ return point(c, nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit ellipse,explicit real)"><code></asyxml>*/
+point point(explicit ellipse el, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
+ return point(el, nodabscissa(x));
+}
+point point(explicit ellipse el, explicit int x)
+{
+ return point(el, nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit parabola,explicit real)"><code></asyxml>*/
+point point(explicit parabola p, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
+ return point(p, nodabscissa(x));
+}
+point point(explicit parabola p, explicit int x)
+{
+ return point(p, nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit hyperbola,explicit real)"><code></asyxml>*/
+point point(explicit hyperbola h, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
+ return point(h, nodabscissa(x));
+}
+point point(explicit hyperbola h, explicit int x)
+{
+ return point(h, nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit conic,explicit real)"><code></asyxml>*/
+point point(explicit conic co, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
+ point op;
+ if(co.e == 0) op = point((circle)co, nodabscissa(x));
+ else if(co.e < 1) op = point((ellipse)co, nodabscissa(x));
+ else if(co.e == 1) op = point((parabola)co, nodabscissa(x));
+ else op = point((hyperbola)co, nodabscissa(x));
+ return op;
+}
+point point(explicit conic co, explicit int x)
+{
+ return point(co, (real)x);
+}
+
+/*<asyxml><function type="point" signature="relpoint(line,real)"><code></asyxml>*/
+point relpoint(line l, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'l' (0 means l.A,
+ 1 means l.B, x means l.A + x * vector(l.B - l.A) ).</documentation></function></asyxml>*/
+ return point(l, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit circle,real)"><code></asyxml>*/
+point relpoint(explicit circle c, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'c' (0 means origin, 1 means end).
+ Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/
+ return point(c, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit ellipse,real)"><code></asyxml>*/
+point relpoint(explicit ellipse el, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'el' (0 means origin, 1 means end).</documentation></function></asyxml>*/
+ return point(el, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit parabola,real)"><code></asyxml>*/
+point relpoint(explicit parabola p, real x)
+{/*<asyxml></code><documentation>Return the relative point of the path of the parabola
+ bounded by the bounding box of the current picture.
+ 0 means origin, 1 means end, where the origin is the vertex of 'p'.</documentation></function></asyxml>*/
+ return point(p, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit hyperbola,real)"><code></asyxml>*/
+point relpoint(explicit hyperbola h, real x)
+{/*<asyxml></code><documentation>Not yet implemented... <look href = "point(hyperbola, abscissa)"/></documentation></function></asyxml>*/
+ return point(h, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit conic,explicit real)"><code></asyxml>*/
+point relpoint(explicit conic co, explicit real x)
+{/*<asyxml></code><documentation>Return the relative point of 'co' (0 means origin, 1 means end).</documentation></function></asyxml>*/
+ point op;
+ if(co.e == 0) op = point((circle)co, Relative(x));
+ else if(co.e < 1) op = point((ellipse)co, Relative(x));
+ else if(co.e == 1) op = point((parabola)co, Relative(x));
+ else op = point((hyperbola)co, Relative(x));
+ return op;
+}
+point relpoint(explicit conic co, explicit int x)
+{
+ return relpoint(co, (real)x);
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit circle,real)"><code></asyxml>*/
+point angpoint(explicit circle c, real x)
+{/*<asyxml></code><documentation>Return the point of 'c' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/
+ return point(c, angabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit ellipse,real,polarconicroutine)"><code></asyxml>*/
+point angpoint(explicit ellipse el, real x,
+ polarconicroutine polarconicroutine = currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the point of 'el' in the direction 'x'
+ measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ return el.e == 0 ? angpoint((circle) el, x) : point(el, angabscissa(x, polarconicroutine));
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit parabola,real)"><code></asyxml>*/
+point angpoint(explicit parabola p, real x)
+{/*<asyxml></code><documentation>Return the point of 'p' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/
+ return point(p, angabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit hyperbola,real,polarconicroutine)"><code></asyxml>*/
+point angpoint(explicit hyperbola h, real x,
+ polarconicroutine polarconicroutine = currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the point of 'h' in the direction 'x'
+ measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ return point(h, angabscissa(x, polarconicroutine));
+}
+
+/*<asyxml><function type="point" signature="curpoint(line,real)"><code></asyxml>*/
+point curpoint(line l, real x)
+{/*<asyxml></code><documentation>Return the point of 'l' which has the curvilinear abscissa 'x'.
+ Origin is l.A.</documentation></function></asyxml>*/
+ return point(l, curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(explicit circle,real)"><code></asyxml>*/
+point curpoint(explicit circle c, real x)
+{/*<asyxml></code><documentation>Return the point of 'c' which has the curvilinear abscissa 'x'.
+ Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/
+ return point(c, curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(explicit ellipse,real)"><code></asyxml>*/
+point curpoint(explicit ellipse el, real x)
+{/*<asyxml></code><documentation>Return the point of 'el' which has the curvilinear abscissa 'el'.</documentation></function></asyxml>*/
+ return point(el, curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(explicit parabola,real)"><code></asyxml>*/
+point curpoint(explicit parabola p, real x)
+{/*<asyxml></code><documentation>Return the point of 'p' which has the curvilinear abscissa 'x'.
+ Origin is the vertex of 'p'.</documentation></function></asyxml>*/
+ return point(p, curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(conic,real)"><code></asyxml>*/
+point curpoint(conic co, real x)
+{/*<asyxml></code><documentation>Return the point of 'co' which has the curvilinear abscissa 'x'.</documentation></function></asyxml>*/
+ point op;
+ if(co.e == 0) op = point((circle)co, curabscissa(x));
+ else if(co.e < 1) op = point((ellipse)co, curabscissa(x));
+ else if(co.e == 1) op = point((parabola)co, curabscissa(x));
+ else op = point((hyperbola)co, curabscissa(x));
+ return op;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(circle,point)"><code></asyxml>*/
+abscissa angabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("angabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system = angularsystem;
+ oa.x = degrees(M - c.C);
+ if(oa.x < 0) oa.x+=360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(ellipse,point,polarconicroutine)"><code></asyxml>*/
+abscissa angabscissa(ellipse el, point M,
+ polarconicroutine polarconicroutine = currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the ellipse 'el' according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("angabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system = angularsystem;
+ oa.polarconicroutine = polarconicroutine;
+ oa.x = polarconicroutine == fromCenter ? degrees(M - el.C) : degrees(M - el.F1);
+ oa.x -= el.angle;
+ if(oa.x < 0) oa.x += 360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(hyperbola,point,polarconicroutine)"><code></asyxml>*/
+abscissa angabscissa(hyperbola h, point M,
+ polarconicroutine polarconicroutine = currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the hyperbola 'h' according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ if(!(M @ h)) abort("angabscissa: the point is not on the hyperbola.");
+ abscissa oa;
+ oa.system = angularsystem;
+ oa.polarconicroutine = polarconicroutine;
+ oa.x = polarconicroutine == fromCenter ? degrees(M - h.C) : degrees(M - h.F1) + 180;
+ oa.x -= h.angle;
+ if(oa.x < 0) oa.x += 360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(parabola,point)"><code></asyxml>*/
+abscissa angabscissa(parabola p, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/
+ if(!(M @ p)) abort("angabscissa: the point is not on the parabola.");
+ abscissa oa;
+ oa.system = angularsystem;
+ oa.polarconicroutine = fromFocus;// Not used
+ oa.x = degrees(M - p.F);
+ oa.x -= p.angle;
+ if(oa.x < 0) oa.x += 360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(conic,point)"><code></asyxml>*/
+abscissa angabscissa(explicit conic co, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e == 0) return angabscissa((circle)co, M);
+ if(co.e < 1) return angabscissa((ellipse)co, M);
+ if(co.e == 1) return angabscissa((parabola)co, M);
+ return angabscissa((hyperbola)co, M);
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(line,point)"><code></asyxml>*/
+abscissa curabscissa(line l, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
+ if(!(M @ extend(l))) abort("curabscissa: the point is not on the line.");
+ abscissa oa;
+ oa.system = curvilinearsystem;
+ oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A);
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(circle,point)"><code></asyxml>*/
+abscissa curabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("curabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system = curvilinearsystem;
+ oa.x = pi * angabscissa(c, M).x * c.r/180;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(ellipse,point)"><code></asyxml>*/
+abscissa curabscissa(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("curabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system = curvilinearsystem;
+ real a = angabscissa(el, M, fromCenter).x;
+ oa.x = arclength(el, 0, a, fromCenter);
+ oa.polarconicroutine = fromCenter;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(parabola,point)"><code></asyxml>*/
+abscissa curabscissa(parabola p, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/
+ if(!(M @ p)) abort("curabscissa: the point is not on the parabola.");
+ abscissa oa;
+ oa.system = curvilinearsystem;
+ real a = angabscissa(p, M).x;
+ oa.x = arclength(p, 180, a);
+ oa.polarconicroutine = fromFocus; // Not used.
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(conic,point)"><code></asyxml>*/
+abscissa curabscissa(conic co, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e > 1) abort("curabscissa: not implemented for this hyperbola.");
+ if(co.e == 0) return curabscissa((circle)co, M);
+ if(co.e < 1) return curabscissa((ellipse)co, M);
+ return curabscissa((parabola)co, M);
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(line,point)"><code></asyxml>*/
+abscissa nodabscissa(line l, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
+ if(!(M @ (segment)l)) abort("nodabscissa: the point is not on the segment.");
+ abscissa oa;
+ oa.system = nodesystem;
+ oa.x = abs(M - l.A)/abs(l.A - l.B);
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(circle,point)"><code></asyxml>*/
+abscissa nodabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("nodabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system = nodesystem;
+ oa.x = intersect((path)c, locate(M))[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(ellipse,point)"><code></asyxml>*/
+abscissa nodabscissa(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("nodabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system = nodesystem;
+ oa.x = intersect((path)el, M)[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(parabola,point)"><code></asyxml>*/
+abscissa nodabscissa(parabola p, point M)
+{/*<asyxml></code><documentation>Return the node abscissa OF 'M' on the parabola 'p'.</documentation></function></asyxml>*/
+ if(!(M @ p)) abort("nodabscissa: the point is not on the parabola.");
+ abscissa oa;
+ oa.system = nodesystem;
+ path pg = p;
+ real[] t = intersect(pg, M, 1e-5);
+ if(t.length == 0) abort("nodabscissa: the point is not on the path of the parabola.");
+ oa.x = t[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(conic,point)"><code></asyxml>*/
+abscissa nodabscissa(conic co, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e > 1) abort("nodabscissa: not implemented for hyperbola.");
+ if(co.e == 0) return nodabscissa((circle)co, M);
+ if(co.e < 1) return nodabscissa((ellipse)co, M);
+ return nodabscissa((parabola)co, M);
+}
+
+
+/*<asyxml><function type="abscissa" signature="relabscissa(line,point)"><code></asyxml>*/
+abscissa relabscissa(line l, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
+ if(!(M @ extend(l))) abort("relabscissa: the point is not on the line.");
+ abscissa oa;
+ oa.system = relativesystem;
+ oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A)/abs(l.A - l.B);
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(circle,point)"><code></asyxml>*/
+abscissa relabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("relabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system = relativesystem;
+ oa.x = angabscissa(c, M).x/360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(ellipse,point)"><code></asyxml>*/
+abscissa relabscissa(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("relabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system = relativesystem;
+ oa.x = curabscissa(el, M).x/arclength(el);
+ oa.polarconicroutine = fromFocus;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(conic,point)"><code></asyxml>*/
+abscissa relabscissa(conic co, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M'
+ on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e > 1) abort("relabscissa: not implemented for hyperbola and parabola.");
+ if(co.e == 1) return relabscissa((parabola)co, M);
+ if(co.e == 0) return relabscissa((circle)co, M);
+ return relabscissa((ellipse)co, M);
+}
+// *.......................ABSCISSA........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.........................ARCS..........................*
+/*<asyxml><struct signature="arc"><code></asyxml>*/
+struct arc {
+ /*<asyxml></code><documentation>Implement oriented ellipse (included circle) arcs.
+ All the calculus with this structure will be as exact as Asymptote can do.
+ For a full precision, you must not cast 'arc' to 'path' excepted for drawing routines.
+ </documentation><property type = "ellipse" signature="el"><code></asyxml>*/
+ ellipse el;/*<asyxml></code><documentation>The support of the arc.</documentation></property><property type = "real" signature="angle0"><code></asyxml>*/
+ restricted real angle0 = 0;/*<asyxml></code><documentation>Internal use: rotating a circle does not modify the origin point,this variable stocks the eventual angle rotation. This value is not used for ellipses which are not circles.</documentation></property><property type = "real" signature="angle1,angle2"><code></asyxml>*/
+ restricted real angle1, angle2;/*<asyxml></code><documentation>Values (in degrees) in ]-360, 360[.</documentation></property><property type = "bool" signature="direction"><code></asyxml>*/
+ bool direction = CCW;/*<asyxml></code><documentation>The arc will be drawn from 'angle1' to 'angle2' rotating in the direction 'direction'.</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/
+ polarconicroutine polarconicroutine = currentpolarconicroutine;/*<asyxml></code><documentation>The routine to which the angles refer.
+ If 'el' is a circle 'fromCenter' is always used.</documentation></property></asyxml>*/
+
+ /*<asyxml><method type = "void" signature="setangles(real,real,real)"><code></asyxml>*/
+ void setangles(real a0, real a1, real a2)
+ {/*<asyxml></code><documentation>Set the angles.</documentation></method></asyxml>*/
+ if (a1 < 0 && a2 < 0) {
+ a1 += 360;
+ a2 += 360;
+ }
+ this.angle0 = a0%(sgnd(a0) * 360);
+ this.angle1 = a1%(sgnd(a1) * 360);
+ this.angle2 = a2%(sgnd(2) * 360);
+ }
+
+ /*<asyxml><method type = "void" signature="init(ellipse,real,real,real,polarconicroutine,bool)"><code></asyxml>*/
+ void init(ellipse el, real angle0 = 0, real angle1, real angle2,
+ polarconicroutine polarconicroutine,
+ bool direction = CCW)
+ {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/
+ if(abs(angle1 - angle2) > 360) abort("arc: |angle1 - angle2| > 360.");
+ this.el = el;
+ this.setangles(angle0, angle1, angle2);
+ this.polarconicroutine = polarconicroutine;
+ this.direction = direction;
+ }
+
+ /*<asyxml><method type = "arc" signature="copy()"><code></asyxml>*/
+ arc copy()
+ {/*<asyxml></code><documentation>Copy the arc.</documentation></method></asyxml>*/
+ arc oa = new arc;
+ oa.el = this.el;
+ oa.direction = this.direction;
+ oa.polarconicroutine = this.polarconicroutine;
+ oa.angle1 = this.angle1;
+ oa.angle2 = this.angle2;
+ oa.angle0 = this.angle0;
+ return oa;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="polarconicroutine" signature="polarconicroutine(ellipse)"><code></asyxml>*/
+polarconicroutine polarconicroutine(conic co)
+{/*<asyxml></code><documentation>Return the default routine used to draw a conic.</documentation></function></asyxml>*/
+ if(co.e == 0) return fromCenter;
+ if(co.e == 1) return fromFocus;
+ return currentpolarconicroutine;
+}
+
+/*<asyxml><function type="arc" signature="arc(ellipse,real,real,polarconicroutine,bool)"><code></asyxml>*/
+arc arc(ellipse el, real angle1, real angle2,
+ polarconicroutine polarconicroutine = polarconicroutine(el),
+ bool direction = CCW)
+{/*<asyxml></code><documentation>Return the ellipse arc from 'angle1' to 'angle2' with respect to 'polarconicroutine' and rotating in the direction 'direction'.</documentation></function></asyxml>*/
+ arc oa;
+ oa.init(el, 0, angle1, angle2, polarconicroutine, direction);
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="complementary(arc)"><code></asyxml>*/
+arc complementary(arc a)
+{/*<asyxml></code><documentation>Return the complementary of 'a'.</documentation></function></asyxml>*/
+ arc oa;
+ oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, a.direction);
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="reverse(arc)"><code></asyxml>*/
+arc reverse(arc a)
+{/*<asyxml></code><documentation>Return arc 'a' oriented in reverse direction.</documentation></function></asyxml>*/
+ arc oa;
+ oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, !a.direction);
+ return oa;
+}
+
+/*<asyxml><function type="real" signature="degrees(arc)"><code></asyxml>*/
+real degrees(arc a)
+{/*<asyxml></code><documentation>Return the measure in degrees of the oriented arc 'a'.</documentation></function></asyxml>*/
+ real or;
+ real da = a.angle2 - a.angle1;
+ if(a.direction) {
+ or = a.angle1 < a.angle2 ? da : 360 + da;
+ } else {
+ or = a.angle1 < a.angle2 ? -360 + da : da;
+ }
+ return or;
+}
+
+/*<asyxml><function type="real" signature="angle(a)"><code></asyxml>*/
+real angle(arc a)
+{/*<asyxml></code><documentation>Return the measure in radians of the oriented arc 'a'.</documentation></function></asyxml>*/
+ return radians(degrees(a));
+}
+
+/*<asyxml><function type="int" signature="arcnodesnumber(explicit arc)"><code></asyxml>*/
+int arcnodesnumber(explicit arc a)
+{/*<asyxml></code><documentation>Return the number of nodes to draw the arc 'a'.</documentation></function></asyxml>*/
+ return ellipsenodesnumber(a.el.a, a.el.b, a.angle1, a.angle2, a.direction);
+}
+
+private path arctopath(arc a, int n)
+{
+ if(a.el.e == 0) return arcfromcenter(a.el, a.angle0 + a.angle1, a.angle0 + a.angle2, a.direction, n);
+ if(a.el.e != 1) return a.polarconicroutine(a.el, a.angle1, a.angle2, n, a.direction);
+ return arcfromfocus(a.el, a.angle1, a.angle2, n, a.direction);
+}
+
+/*<asyxml><function type="point" signature="angpoint(arc,real)"><code></asyxml>*/
+point angpoint(arc a, real angle)
+{/*<asyxml></code><documentation>Return the point given by its angular position (in degrees) relative to the arc 'a'.
+ If 'angle > degrees(a)' or 'angle < 0' the returned point is on the extended arc.</documentation></function></asyxml>*/
+ pair p;
+ if(a.el.e == 0) {
+ real gle = a.angle0 + a.angle1 + (a.direction ? angle : -angle);
+ p = point(arcfromcenter(a.el, gle, gle, CCW, 1), 0);
+ }
+ else {
+ real gle = a.angle1 + (a.direction ? angle : -angle);
+ p = point(a.polarconicroutine(a.el, gle, gle, 1, CCW), 0);
+ }
+ return point(coordsys(a.el), p/coordsys(a.el));
+}
+
+/*<asyxml><operator type = "path" signature="cast(explicit arc)"><code></asyxml>*/
+path operator cast(explicit arc a)
+{/*<asyxml></code><documentation>Cast arc to path.</documentation></operator></asyxml>*/
+ return arctopath(a, arcnodesnumber(a));
+}
+
+/*<asyxml><operator type = "guide" signature="cast(explicit arc)"><code></asyxml>*/
+guide operator cast(explicit arc a)
+{/*<asyxml></code><documentation>Cast arc to guide.</documentation></operator></asyxml>*/
+ return arctopath(a, arcnodesnumber(a));
+}
+
+/*<asyxml><operator type = "arc" signature="*(transform,explicit arc)"><code></asyxml>*/
+arc operator *(transform t, explicit arc a)
+{/*<asyxml></code><documentation>Provide transform * arc.</documentation></operator></asyxml>*/
+ pair[] P, PP;
+ path g = arctopath(a, 3);
+ real a0, a1 = a.angle1, a2 = a.angle2, ap1, ap2;
+ bool dir = a.direction;
+ P[0] = t * point(g, 0);
+ P[1] = t * point(g, 2);
+ ellipse el = t * a.el;
+ arc oa;
+ a0 = (a.angle0 + angle(shiftless(t)))%360;
+ pair C;
+ if(a.polarconicroutine == fromCenter) C = el.C; else C = el.F1;
+ real d = abs(locate(el.F2 - el.F1)) > epsgeo ?
+ degrees(locate(el.F2 - el.F1)) : a0 + degrees(el.C.coordsys.i);
+ ap1 = (degrees(P[0]-C, false) - d)%360;
+ ap2 = (degrees(P[1]-C, false) - d)%360;
+ oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir);
+ g = arctopath(oa, 3);
+ PP[0] = point(g, 0);
+ PP[1] = point(g, 2);
+ if((a1 - a2) * (ap1 - ap2) < 0) {// Handle reflection.
+ dir=!a.direction;
+ oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir);
+ }
+ return oa;
+}
+
+/*<asyxml><operator type = "arc" signature="*(real,explicit arc)"><code></asyxml>*/
+arc operator *(real x, explicit arc a)
+{/*<asyxml></code><documentation>Provide real * arc.
+ Return the arc subtracting and adding '(x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/
+ real a1, a2, gle;
+ gle = (x - 1) * degrees(a)/2;
+ a1 = a.angle1 - gle;
+ a2 = a.angle2 + gle;
+ arc oa;
+ oa.init(a.el, a.angle0, a1, a2, a.polarconicroutine, a.direction);
+ return oa;
+}
+arc operator *(int x, explicit arc a){return (real)x * a;}
+/*<asyxml><operator type = "arc" signature="/(real,explicit arc)"><code></asyxml>*/
+arc operator /(explicit arc a, real x)
+{/*<asyxml></code><documentation>Provide arc/real.
+ Return the arc subtracting and adding '(1/x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/
+ return (1/x) * a;
+}
+/*<asyxml><operator type = "arc" signature="+(explicit arc,point)"><code></asyxml>*/
+arc operator +(explicit arc a, point M)
+{/*<asyxml></code><documentation>Provide arc + point.
+ Return shifted arc.
+ 'operator +(explicit arc, point)', 'operator +(explicit arc, vector)' and 'operator -(explicit arc, vector)' are also defined.</documentation></operator></asyxml>*/
+ return shift(M) * a;
+}
+arc operator -(explicit arc a, point M){return a + (-M);}
+arc operator +(explicit arc a, vector v){return shift(locate(v)) * a;}
+arc operator -(explicit arc a, vector v){return a + (-v);}
+
+
+/*<asyxml><operator type = "bool" signature="@(point,arc)"><code></asyxml>*/
+bool operator @(point M, arc a)
+{/*<asyxml></code><documentation>Return true iff 'M' is on the arc 'a'.</documentation></operator></asyxml>*/
+ if (!(M @ a.el)) return false;
+ coordsys R = defaultcoordsys;
+ path ap = arctopath(a, 3);
+ line l = line(point(R, point(ap, 0)), point(R, point(ap, 2)));
+ return sameside(M, point(R, point(ap, 1)), l);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,arc,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, Label L = "", arc a,
+ align align = NoAlign, pen p = currentpen,
+ arrowbar arrow = None, arrowbar bar = None, margin margin = NoMargin,
+ Label legend = "", marker marker = nomarker)
+{/*<asyxml></code><documentation>Draw 'arc' adding the pen returned by 'addpenarc(p)' to the pen 'p'.
+ <look href = "#addpenarc"/></documentation></function></asyxml>*/
+ draw(pic, L, (path)a, align, addpenarc(p), arrow, bar, margin, legend, marker);
+}
+
+/*<asyxml><function type="real" signature="arclength(arc)"><code></asyxml>*/
+real arclength(arc a)
+{/*<asyxml></code><documentation>The arc length of 'a'.</documentation></function></asyxml>*/
+ return arclength(a.el, a.angle1, a.angle2, a.direction, a.polarconicroutine);
+}
+
+private point ppoint(arc a, real x)
+{// Return the point of the arc proportionally to its length.
+ point oP;
+ if(a.el.e == 0) { // Case of circle.
+ oP = angpoint(a, x * abs(degrees(a)));
+ } else { // Ellipse and not circle.
+ if(!a.direction) {
+ transform t = reflect(line(a.el.F1, a.el.F2));
+ return t * ppoint(t * a, x);
+ }
+
+ real angle1 = a.angle1, angle2 = a.angle2;
+ if(a.polarconicroutine == fromFocus) {
+ // dot(point(fromFocus(a.el, angle1, angle1, 1, CCW), 0), 2mm + blue);
+ // dot(point(fromFocus(a.el, angle2, angle2, 1, CCW), 0), 2mm + blue);
+ // write("fromfocus1 = ", angle1);
+ // write("fromfocus2 = ", angle2);
+ real gle1 = focusToCenter(a.el, angle1);
+ real gle2 = focusToCenter(a.el, angle2);
+ if((gle1 - gle2) * (angle1 - angle2) > 0) {
+ angle1 = gle1; angle2 = gle2;
+ } else {
+ angle1 = gle2; angle2 = gle1;
+ }
+ // write("fromcenter1 = ", angle1);
+ // write("fromcenter2 = ", angle2);
+ // dot(point(fromCenter(a.el, angle1, angle1, 1, CCW), 0), 1mm + red);
+ // dot(point(fromCenter(a.el, angle2, angle2, 1, CCW), 0), 1mm + red);
+ }
+
+ if(angle1 > angle2) {
+ arc ta = a.copy();
+ ta.polarconicroutine = fromCenter;
+ ta.setangles(a0 = a.angle0, a1 = angle1 - 360, a2 = angle2);
+ return ppoint(ta, x);
+ }
+ ellipse co = a.el;
+ real gle, a1, a2, cx = 0;
+ bool direction;
+ if(x >= 0) {
+ a1 = angle1;
+ a2 = a1 + 360;
+ direction = CCW;
+ } else {
+ a1 = angle1 - 360;
+ a2 = a1 - 360;
+ direction = CW;
+ }
+ gle = a1;
+ real L = arclength(co, angle1, angle2, a.direction, fromCenter);
+ real tx = L * abs(x)%arclength(co);
+ real aout = a1;
+ while(abs(cx - tx) > epsgeo) {
+ aout = (a1 + a2)/2;
+ cx = abs(arclength(co, gle, aout, direction, fromCenter));
+ if(cx > tx) a2 = (a1 + a2)/2 ; else a1 = (a1 + a2)/2;
+ }
+ pair p = point(arcfromcenter(co, aout, aout, CCW, 1), 0);
+ oP = point(coordsys(co), p/coordsys(co));
+ }
+ return oP;
+}
+
+/*<asyxml><function type="point" signature="point(arc,abscissa)"><code></asyxml>*/
+point point(arc a, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'a' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.
+ Note that 'a.polarconicroutine' is used instead of 'l.polarconicroutine'.
+ <look href = "#struct abscissa"/></documentation></function></asyxml>*/
+ real posx;
+ arc ta = a.copy();
+ ellipse co = a.el;
+ if (l.system == relativesystem) {
+ posx = l.x;
+ } else
+ if (l.system == curvilinearsystem) {
+ real tl;
+ if(co.e == 0) {
+ tl = curabscissa(a.el, angpoint(a.el, a.angle0 + a.angle1)).x;
+ return curpoint(a.el, tl + (a.direction ? l.x : -l.x));
+ } else {
+ tl = curabscissa(a.el, angpoint(a.el, a.angle1, a.polarconicroutine)).x;
+ return curpoint(a.el, tl + (a.direction ? l.x : -l.x));
+ }
+ } else
+ if (l.system == nodesystem) {
+ coordsys R = coordsys(co);
+ return point(R, point((path)a, l.x)/R);
+ } else
+ if (l.system == angularsystem) {
+ return angpoint(a, l.x);
+ } else abort("point: bad abscissa system.");
+ return ppoint(ta, posx);
+}
+
+
+/*<asyxml><function type="point" signature="point(arc,real)"><code></asyxml>*/
+point point(arc a, real x)
+{/*<asyxml></code><documentation>Return the point between node floor(t) and floor(t) + 1.</documentation></function></asyxml>*/
+ return point(a, nodabscissa(x));
+}
+pair point(explicit arc a, int x)
+{
+ return point(a, nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(arc,real)"><code></asyxml>*/
+point relpoint(arc a, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'a'.
+ If x > 1 or x < 0, the returned point is on the extended arc.</documentation></function></asyxml>*/
+ return point(a, relabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(arc,real)"><code></asyxml>*/
+point curpoint(arc a, real x)
+{/*<asyxml></code><documentation>Return the point of 'a' which has the curvilinear abscissa 'x'.
+ If x < 0 or x > arclength(a), the returned point is on the extended arc.</documentation></function></asyxml>*/
+ return point(a, curabscissa(x));
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(arc,point)"><code></asyxml>*/
+abscissa angabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' according to the arc 'a'.</documentation></function></asyxml>*/
+ if(!(M @ a.el))
+ abort("angabscissa: the point is not on the extended arc.");
+ abscissa oa;
+ oa.system = angularsystem;
+ oa.polarconicroutine = a.polarconicroutine;
+ real am = angabscissa(a.el, M, a.polarconicroutine).x;
+ oa.x = (am - a.angle1 - (a.el.e == 0 ? a.angle0 : 0))%360;
+ oa.x = a.direction ? oa.x : 360 - oa.x;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(arc,point)"><code></asyxml>*/
+abscissa curabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa according to the arc 'a'.</documentation></function></asyxml>*/
+ ellipse el = a.el;
+ if(!(M @ el))
+ abort("angabscissa: the point is not on the extended arc.");
+ abscissa oa;
+ oa.system = curvilinearsystem;
+ real xm = curabscissa(el, M).x;
+ real a0 = el.e == 0 ? a.angle0 : 0;
+ real am = curabscissa(el, angpoint(el, a.angle1 + a0, a.polarconicroutine)).x;
+ real l = arclength(el);
+ oa.x = (xm - am)%l;
+ oa.x = a.direction ? oa.x : l - oa.x;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(arc,point)"><code></asyxml>*/
+abscissa nodabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the node abscissa according to the arc 'a'.</documentation></function></asyxml>*/
+ if(!(M @ a))
+ abort("nodabscissa: the point is not on the arc.");
+ abscissa oa;
+ oa.system = nodesystem;
+ oa.x = intersect((path)a, M)[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(arc,point)"><code></asyxml>*/
+abscissa relabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa according to the arc 'a'.</documentation></function></asyxml>*/
+ ellipse el = a.el;
+ if(!( M @ el))
+ abort("relabscissa: the point is not on the prolonged arc.");
+ abscissa oa;
+ oa.system = relativesystem;
+ oa.x = curabscissa(a, M).x/arclength(a);
+ return oa;
+}
+
+/*<asyxml><function type="void" signature="markarc(picture,Label,int,real,real,arc,arrowbar,pen,pen,margin,marker)"><code></asyxml>*/
+void markarc(picture pic = currentpicture,
+ Label L = "",
+ int n = 1, real radius = 0, real space = 0,
+ arc a,
+ pen sectorpen = currentpen,
+ pen markpen = sectorpen,
+ margin margin = NoMargin,
+ arrowbar arrow = None,
+ marker marker = nomarker)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ real Da = degrees(a);
+ pair p1 = point(a, 0);
+ pair p2 = relpoint(a, 1);
+ pair c = a.polarconicroutine == fromCenter ? locate(a.el.C) : locate(a.el.F1);
+ if(radius == 0) radius = markangleradius(markpen);
+ if(abs(Da) > 180) radius = -radius;
+ radius = (a.direction ? 1 : -1) * sgnd(Da) * radius;
+ draw(c--p1^^c--p2, sectorpen);
+ markangle(pic = pic, L = L, n = n, radius = radius, space = space,
+ A = p1, O = c, B = p2,
+ arrow = arrow, p = markpen, margin = margin,
+ marker = marker);
+}
+// *.........................ARCS..........................*
+// *=======================================================*
+
+// *=======================================================*
+// *........................MASSES.........................*
+/*<asyxml><struct signature="mass"><code></asyxml>*/
+struct mass {/*<asyxml></code><documentation></documentation><property type = "point" signature="M"><code></asyxml>*/
+ point M;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="m"><code></asyxml>*/
+ real m;/*<asyxml></code><documentation></documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="mass" signature="mass(point,real)"><code></asyxml>*/
+mass mass(point M, real m)
+{/*<asyxml></code><documentation>Constructor of mass point.</documentation></function></asyxml>*/
+ mass om;
+ om.M = M;
+ om.m = m;
+ return om;
+}
+
+/*<asyxml><operator type = "point" signature="cast(mass)"><code></asyxml>*/
+point operator cast(mass m)
+{/*<asyxml></code><documentation>Cast mass point to point.</documentation></operator></asyxml>*/
+ point op;
+ op = m.M;
+ op.m = m.m;
+ return op;
+}
+/*<asyxml><function type="point" signature="point(explicit mass)"><code></asyxml>*/
+point point(explicit mass m){return m;}/*<asyxml></code><documentation>Cast
+ 'm' to point</documentation></function></asyxml>*/
+
+/*<asyxml><operator type = "mass" signature="cast(point)"><code></asyxml>*/
+mass operator cast(point M)
+{/*<asyxml></code><documentation>Cast point to mass point.</documentation></operator></asyxml>*/
+ mass om;
+ om.M = M;
+ om.m = M.m;
+ return om;
+}
+/*<asyxml><function type="mass" signature="mass(explicit point)"><code></asyxml>*/
+mass mass(explicit point P)
+{/*<asyxml></code><documentation>Cast 'P' to mass.</documentation></function></asyxml>*/
+ return mass(P, P.m);
+}
+
+/*<asyxml><operator type = "point[]" signature="cast(mass[])"><code></asyxml>*/
+point[] operator cast(mass[] m)
+{/*<asyxml></code><documentation>Cast mass[] to point[].</documentation></operator></asyxml>*/
+ point[] op;
+ for(mass am : m) op.push(point(am));
+ return op;
+}
+
+/*<asyxml><operator type = "mass[]" signature="cast(point[])"><code></asyxml>*/
+mass[] operator cast(point[] P)
+{/*<asyxml></code><documentation>Cast point[] to mass[].</documentation></operator></asyxml>*/
+ mass[] om;
+ for(point op : P) om.push(mass(op));
+ return om;
+}
+
+/*<asyxml><function type="mass" signature="mass(coordsys,explicit pair,real)"><code></asyxml>*/
+mass mass(coordsys R, explicit pair p, real m)
+{/*<asyxml></code><documentation>Return the mass which has coordinates
+ 'p' with respect to 'R' and weight 'm'.</documentation></function></asyxml>*/
+ return point(R, p, m);// Using casting.
+}
+
+/*<asyxml><operator type = "mass" signature="cast(pair)"><code></asyxml>*/
+mass operator cast(pair m){return mass((point)m, 1);}/*<asyxml></code><documentation>Cast pair to mass point.</documentation></operator></asyxml>*/
+/*<asyxml><operator type = "path" signature="cast(mass)"><code></asyxml>*/
+path operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass point to path.</documentation></operator></asyxml>*/
+/*<asyxml><operator type = "guide" signature="cast(mass)"><code></asyxml>*/
+guide operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass to guide.</documentation></operator></asyxml>*/
+
+/*<asyxml><operator type = "mass" signature="+(mass,mass)"><code></asyxml>*/
+mass operator +(mass M1, mass M2)
+{/*<asyxml></code><documentation>Provide mass + mass.
+ mass - mass is also defined.</documentation></operator></asyxml>*/
+ return mass(M1.M + M2.M, M1.m + M2.m);
+}
+mass operator -(mass M1, mass M2)
+{
+ return mass(M1.M - M2.M, M1.m - M2.m);
+}
+
+/*<asyxml><operator type = "mass" signature="*(real,mass)"><code></asyxml>*/
+mass operator *(real x, explicit mass M)
+{/*<asyxml></code><documentation>Provide real * mass.
+ The resulted mass is the mass of 'M' multiplied by 'x' .
+ mass/real, mass + real and mass - real are also defined.</documentation></operator></asyxml>*/
+ return mass(M.M, x * M.m);
+}
+mass operator *(int x, explicit mass M){return mass(M.M, x * M.m);}
+mass operator /(explicit mass M, real x){return mass(M.M, M.m/x);}
+mass operator /(explicit mass M, int x){return mass(M.M, M.m/x);}
+mass operator +(explicit mass M, real x){return mass(M.M, M.m + x);}
+mass operator +(explicit mass M, int x){return mass(M.M, M.m + x);}
+mass operator -(explicit mass M, real x){return mass(M.M, M.m - x);}
+mass operator -(explicit mass M, int x){return mass(M.M, M.m - x);}
+/*<asyxml><operator type = "mass" signature="*(transform,mass)"><code></asyxml>*/
+mass operator *(transform t, mass M)
+{/*<asyxml></code><documentation>Provide transform * mass.</documentation></operator></asyxml>*/
+ return mass(t * M.M, M.m);
+}
+
+/*<asyxml><function type="mass" signature="masscenter(... mass[])"><code></asyxml>*/
+mass masscenter(... mass[] M)
+{/*<asyxml></code><documentation>Return the center of the masses 'M'.</documentation></function></asyxml>*/
+ point[] P;
+ for (int i = 0; i < M.length; ++i)
+ P.push(M[i].M);
+ P = standardizecoordsys(currentcoordsys, true ... P);
+ real m = M[0].m;
+ point oM = M[0].m * P[0];
+ for (int i = 1; i < M.length; ++i) {
+ oM += M[i].m * P[i];
+ m += M[i].m;
+ }
+ if (m == 0) abort("masscenter: the sum of masses is null.");
+ return mass(oM/m, m);
+}
+
+/*<asyxml><function type="string" signature="massformat(string,string,mass)"><code></asyxml>*/
+string massformat(string format = defaultmassformat,
+ string s, mass M)
+{/*<asyxml></code><documentation>Return the string formated by 'format' with the mass value.
+ In the parameter 'format', %L will be replaced by 's'.
+ <look href = "#defaultmassformat"/>.</documentation></function></asyxml>*/
+ return format == "" ? s :
+ format(replace(format, "%L", replace(s, "$", "")), M.m);
+}
+
+/*<asyxml><function type="void" signature="label(picture,Label,explicit mass,align,string,pen,filltype)"><code></asyxml>*/
+void label(picture pic = currentpicture, Label L, explicit mass M,
+ align align = NoAlign, string format = defaultmassformat,
+ pen p = nullpen, filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw label returned by massformat(format, L, M) at coordinates of M.
+ <look href = "#massformat(string, string, mass)"/>.</documentation></function></asyxml>*/
+ Label lL = L.copy();
+ lL.s = massformat(format, lL.s, M);
+ Label L = Label(lL, M.M, align, p, filltype);
+ add(pic, L);
+}
+
+/*<asyxml><function type="void" signature="dot(picture,Label,explicit mass,align,string,pen)"><code></asyxml>*/
+void dot(picture pic = currentpicture, Label L, explicit mass M, align align = NoAlign,
+ string format = defaultmassformat, pen p = currentpen)
+{/*<asyxml></code><documentation>Draw a dot with label 'L' as
+ label(picture, Label, explicit mass, align, string, pen, filltype) does.
+ <look href = "#label(picture, Label, mass, align, string, pen, filltype)"/>.</documentation></function></asyxml>*/
+ Label lL = L.copy();
+ lL.s = massformat(format, lL.s, M);
+ lL.position(locate(M.M));
+ lL.align(align, E);
+ lL.p(p);
+ dot(pic, M.M, p);
+ add(pic, lL);
+}
+// *........................MASSES.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.......................TRIANGLES.......................*
+/*<asyxml><function type="point" signature="orthocentercenter(point,point,point)"><code></asyxml>*/
+point orthocentercenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the orthocenter of the triangle ABC.</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B, C);
+ coordsys R = P[0].coordsys;
+ pair pp = extension(A, projection(P[1], P[2]) * P[0], B, projection(P[0], P[2]) * P[1]);
+ return point(R, pp/R);
+}
+
+/*<asyxml><function type="point" signature="centroid(point,point,point)"><code></asyxml>*/
+point centroid(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the centroid of the triangle ABC.</documentation></function></asyxml>*/
+ return (A + B + C)/3;
+}
+
+/*<asyxml><function type="point" signature="incenter(point,point,point)"><code></asyxml>*/
+point incenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the center of the incircle of the triangle ABC.</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B, C);
+ coordsys R = P[0].coordsys;
+ pair a = A, b = B, c = C;
+ pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c));
+ return point(R, pp/R);
+}
+
+/*<asyxml><function type="real" signature="inradius(point,point,point)"><code></asyxml>*/
+real inradius(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle ABC.</documentation></function></asyxml>*/
+ point IC = incenter(A, B, C);
+ return abs(IC - projection(A, B) * IC);
+}
+
+/*<asyxml><function type="circle" signature="incircle(point,point,point)"><code></asyxml>*/
+circle incircle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the incircle of the triangle ABC.</documentation></function></asyxml>*/
+ point IC = incenter(A, B, C);
+ return circle(IC, abs(IC - projection(A, B) * IC));
+}
+
+/*<asyxml><function type="point" signature="excenter(point,point,point)"><code></asyxml>*/
+point excenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the center of the excircle of the triangle tangent with (AB).</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B, C);
+ coordsys R = P[0].coordsys;
+ pair a = A, b = B, c = C;
+ pair pp = extension(a, a + rotate(90) * dir(a--b, a--c), b, b + rotate(90) * dir(b--a, b--c));
+ return point(R, pp/R);
+}
+
+/*<asyxml><function type="real" signature="exradius(point,point,point)"><code></asyxml>*/
+real exradius(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the radius of the excircle of the triangle ABC with (AB).</documentation></function></asyxml>*/
+ point EC = excenter(A, B, C);
+ return abs(EC - projection(A, B) * EC);
+}
+
+/*<asyxml><function type="circle" signature="excircle(point,point,point)"><code></asyxml>*/
+circle excircle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the excircle of the triangle ABC tangent with (AB).</documentation></function></asyxml>*/
+ point center = excenter(A, B, C);
+ real radius = abs(center - projection(B, C) * center);
+ return circle(center, radius);
+}
+
+private int[] numarray = {1, 2, 3};
+numarray.cyclic = true;
+
+/*<asyxml><struct signature="triangle"><code></asyxml>*/
+struct triangle {/*<asyxml></code><documentation></documentation></asyxml>*/
+
+ /*<asyxml><struct signature="vertex"><code></asyxml>*/
+ struct vertex {/*<asyxml></code><documentation>Structure used to communicate the vertex of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/
+ int n;/*<asyxml></code><documentation>1 means VA,2 means VB,3 means VC,4 means VA etc...</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/
+ triangle t;/*<asyxml></code><documentation>The triangle to which the vertex refers.</documentation></property></asyxml>*/
+ }/*<asyxml></struct></asyxml>*/
+
+ /*<asyxml><property type = "point" signature="A,B,C"><code></asyxml>*/
+ restricted point A, B, C;/*<asyxml></code><documentation>The vertices of the triangle (as point).</documentation></property><property type = "vertex" signature="VA, VB, VC"><code></asyxml>*/
+ restricted vertex VA, VB, VC;/*<asyxml></code><documentation>The vertices of the triangle (as vertex).
+ Note that the vertex structure contains the triangle to wish it refers.</documentation></property></asyxml>*/
+ VA.n = 1;VB.n = 2;VC.n = 3;
+
+ /*<asyxml><method type = "vertex" signature="vertex(int)"><code></asyxml>*/
+ vertex vertex(int n)
+ {/*<asyxml></code><documentation>Return numbered vertex.
+ 'n' is 1 means VA, 2 means VB, 3 means VC, 4 means VA etc...</documentation></method></asyxml>*/
+ n = numarray[n - 1];
+ if(n == 1) return VA;
+ else if(n == 2) return VB;
+ return VC;
+ }
+
+ /*<asyxml><method type = "point" signature="point(int)"><code></asyxml>*/
+ point point(int n)
+ {/*<asyxml></code><documentation>Return numbered point.
+ n is 1 means A, 2 means B, 3 means C, 4 means A etc...</documentation></method></asyxml>*/
+ n = numarray[n - 1];
+ if(n == 1) return A;
+ else if(n == 2) return B;
+ return C;
+ }
+
+ /*<asyxml><method type = "void" signature="init(point,point,point)"><code></asyxml>*/
+ void init(point A, point B, point C)
+ {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/
+ point[] P = standardizecoordsys(A, B, C);
+ this.A = P[0];
+ this.B = P[1];
+ this.C = P[2];
+ VA.t = this; VB.t = this; VC.t = this;
+ }
+
+ /*<asyxml><method type = "void" signature="operator init(point,point,point)"><code></asyxml>*/
+ void operator init(point A, point B, point C)
+ {/*<asyxml></code><documentation>For backward compatibility.
+ Provide the routine 'triangle(point A, point B, point C)'.</documentation></method></asyxml>*/
+ this.init(A, B, C);
+ }
+
+ /*<asyxml><method type = "void" signature="init(real,real,real,real,point)"><code></asyxml>*/
+ void operator init(real b, real alpha, real c, real angle = 0, point A = (0, 0))
+ {/*<asyxml></code><documentation>For backward compatibility.
+ Provide the routine 'triangle(real b, real alpha, real c, real angle = 0, point A = (0, 0))
+ which returns the triangle ABC rotated by 'angle' (in degrees) and where b = AC, degrees(A) = alpha, AB = c.</documentation></method></asyxml>*/
+ coordsys R = A.coordsys;
+ this.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha)));
+ }
+
+ /*<asyxml><method type = "real" signature="a(),b(),c()"><code></asyxml>*/
+ real a()
+ {/*<asyxml></code><documentation>Return the length BC.
+ b() and c() are also defined and return the length AC and AB respectively.</documentation></method></asyxml>*/
+ return length(C - B);
+ }
+ real b() {return length(A - C);}
+ real c() {return length(B - A);}
+
+ private real det(pair a, pair b) {return a.x * b.y - a.y * b.x;}
+
+ /*<asyxml><method type = "real" signature="area()"><code></asyxml>*/
+ real area()
+ {/*<asyxml></code><documentation></documentation></method></asyxml>*/
+ pair a = locate(A), b = locate(B), c = locate(C);
+ return 0.5 * abs(det(a, b) + det(b, c) + det(c, a));
+ }
+
+ /*<asyxml><method type = "real" signature="alpha(),beta(),gamma()"><code></asyxml>*/
+ real alpha()
+ {/*<asyxml></code><documentation>Return the measure (in degrees) of the angle A.
+ beta() and gamma() are also defined and return the measure of the angles B and C respectively.</documentation></method></asyxml>*/
+ return degrees(acos((b()^2 + c()^2 - a()^2)/(2b() * c())));
+ }
+ real beta() {return degrees(acos((c()^2 + a()^2 - b()^2)/(2c() * a())));}
+ real gamma() {return degrees(acos((a()^2 + b()^2 - c()^2)/(2a() * b())));}
+
+ /*<asyxml><method type = "path" signature="Path()"><code></asyxml>*/
+ path Path()
+ {/*<asyxml></code><documentation>The path of the triangle.</documentation></method></asyxml>*/
+ return A--C--B--cycle;
+ }
+
+ /*<asyxml><struct signature="side"><code></asyxml>*/
+ struct side
+ {/*<asyxml></code><documentation>Structure used to communicate the side of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/
+ int n;/*<asyxml></code><documentation>1 or 0 means [AB],-1 means [BA],2 means [BC],-2 means [CB] etc.</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/
+ triangle t;/*<asyxml></code><documentation>The triangle to which the side refers.</documentation></property></asyxml>*/
+ }/*<asyxml></struct></asyxml>*/
+
+ /*<asyxml><property type = "side" signature="AB"><code></asyxml>*/
+ side AB;/*<asyxml></code><documentation>For the routines using the structure 'side', triangle.AB means 'side AB'.
+ BA, AC, CA etc are also defined.</documentation></property></asyxml>*/
+ AB.n = 1; AB.t = this;
+ side BA; BA.n = -1; BA.t = this;
+ side BC; BC.n = 2; BC.t = this;
+ side CB; CB.n = -2; CB.t = this;
+ side CA; CA.n = 3; CA.t = this;
+ side AC; AC.n = -3; AC.t = this;
+
+ /*<asyxml><method type = "side" signature="side(int)"><code></asyxml>*/
+ side side(int n)
+ {/*<asyxml></code><documentation>Return numbered side.
+ n is 1 means AB, -1 means BA, 2 means BC, -2 means CB, etc.</documentation></method></asyxml>*/
+ if(n == 0) abort('Invalid side number.');
+ int an = numarray[abs(n)-1];
+ if(an == 1) return n > 0 ? AB : BA;
+ else if(an == 2) return n > 0 ? BC : CB;
+ return n > 0 ? CA : AC;
+ }
+
+ /*<asyxml><method type = "line" signature="line(int)"><code></asyxml>*/
+ line line(int n)
+ {/*<asyxml></code><documentation>Return the numbered line.</documentation></method></asyxml>*/
+ if(n == 0) abort('Invalid line number.');
+ int an = numarray[abs(n)-1];
+ if(an == 1) return n > 0 ? line(A, B) : line(B, A);
+ else if(an == 2) return n > 0 ? line(B, C) : line(C, B);
+ return n > 0 ? line(C, A) : line(A, C);
+ }
+
+}/*<asyxml></struct></asyxml>*/
+
+from triangle unravel side; // The structure 'side' is now available outside the triangle structure.
+from triangle unravel vertex; // The structure 'vertex' is now available outside the triangle structure.
+
+triangle[] operator ^^(triangle[] t1, triangle t2)
+{
+ triangle[] T;
+ for (int i = 0; i < t1.length; ++i) T.push(t1[i]);
+ T.push(t2);
+ return T;
+}
+
+triangle[] operator ^^(... triangle[] t)
+{
+ triangle[] T;
+ for (int i = 0; i < t.length; ++i) {
+ T.push(t[i]);
+ }
+ return T;
+}
+
+/*<asyxml><operator type = "line" signature="cast(side)"><code></asyxml>*/
+line operator cast(side side)
+{/*<asyxml></code><documentation>Cast side to (infinite) line.
+ Most routine with line parameters works with side parameters.
+ One can use the code 'segment(a_side)' to obtain a line segment.</documentation></operator></asyxml>*/
+ triangle t = side.t;
+ return t.line(side.n);
+}
+
+/*<asyxml><function type="line" signature="line(explicit side)"><code></asyxml>*/
+line line(explicit side side)
+{/*<asyxml></code><documentation>Return 'side' as line.</documentation></function></asyxml>*/
+ return (line)side;
+}
+
+/*<asyxml><function type="segment" signature="segment(explicit side)"><code></asyxml>*/
+segment segment(explicit side side)
+{/*<asyxml></code><documentation>Return 'side' as segment.</documentation></function></asyxml>*/
+ return (segment)(line)side;
+}
+
+/*<asyxml><operator type = "point" signature="cast(vertex)"><code></asyxml>*/
+point operator cast(vertex V)
+{/*<asyxml></code><documentation>Cast vertex to point.
+ Most routine with point parameters works with vertex parameters.</documentation></operator></asyxml>*/
+ return V.t.point(V.n);
+}
+
+/*<asyxml><function type="point" signature="point(explicit vertex)"><code></asyxml>*/
+point point(explicit vertex V)
+{/*<asyxml></code><documentation>Return the point corresponding to the vertex 'V'.</documentation></function></asyxml>*/
+ return (point)V;
+}
+
+/*<asyxml><function type="side" signature="opposite(vertex)"><code></asyxml>*/
+side opposite(vertex V)
+{/*<asyxml></code><documentation>Return the opposite side of vertex 'V'.</documentation></function></asyxml>*/
+ return V.t.side(numarray[abs(V.n)]);
+}
+
+/*<asyxml><function type="vertex" signature="opposite(side)"><code></asyxml>*/
+vertex opposite(side side)
+{/*<asyxml></code><documentation>Return the opposite vertex of side 'side'.</documentation></function></asyxml>*/
+ return side.t.vertex(numarray[abs(side.n) + 1]);
+}
+
+/*<asyxml><function type="point" signature="midpoint(side)"><code></asyxml>*/
+point midpoint(side side)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return midpoint(segment(side));
+}
+
+/*<asyxml><operator type = "triangle" signature="*(transform,triangle)"><code></asyxml>*/
+triangle operator *(transform T, triangle t)
+{/*<asyxml></code><documentation>Provide transform * triangle.</documentation></operator></asyxml>*/
+ return triangle(T * t.A, T * t.B, T * t.C);
+}
+
+/*<asyxml><function type="triangle" signature="triangleAbc(real,real,real,real,point)"><code></asyxml>*/
+triangle triangleAbc(real alpha, real b, real c, real angle = 0, point A = (0, 0))
+{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BAC = alpha, AC = b and AB = c.</documentation></function></asyxml>*/
+ triangle T;
+ coordsys R = A.coordsys;
+ T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha)));
+ return T;
+}
+
+/*<asyxml><function type="triangle" signature="triangleabc(real,real,real,real,point)"><code></asyxml>*/
+triangle triangleabc(real a, real b, real c, real angle = 0, point A = (0, 0))
+{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BC = a, AC = b and AB = c.</documentation></function></asyxml>*/
+ triangle T;
+ coordsys R = A.coordsys;
+ T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle) + acos((b^2 + c^2 - a^2)/(2 * b * c))));
+ return T;
+}
+
+/*<asyxml><function type="triangle" signature="triangle(line,line,line)"><code></asyxml>*/
+triangle triangle(line l1, line l2, line l3)
+{/*<asyxml></code><documentation>Return the triangle defined by three line.</documentation></function></asyxml>*/
+ point P1, P2, P3;
+ P1 = intersectionpoint(l1, l2);
+ P2 = intersectionpoint(l1, l3);
+ P3 = intersectionpoint(l2, l3);
+ if(!(defined(P1) && defined(P2) && defined(P3))) abort("triangle: two lines are parallel.");
+ return triangle(P1, P2, P3);
+}
+
+/*<asyxml><function type="point" signature="foot(vertex)"><code></asyxml>*/
+point foot(vertex V)
+{/*<asyxml></code><documentation>Return the endpoint of the altitude from V.</documentation></function></asyxml>*/
+ return projection((line)opposite(V)) * ((point)V);
+}
+
+/*<asyxml><function type="point" signature="foot(side)"><code></asyxml>*/
+point foot(side side)
+{/*<asyxml></code><documentation>Return the endpoint of the altitude on 'side'.</documentation></function></asyxml>*/
+ return projection((line)side) * point(opposite(side));
+}
+
+/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/
+line altitude(vertex V)
+{/*<asyxml></code><documentation>Return the altitude passing through 'V'.</documentation></function></asyxml>*/
+ return line(point(V), foot(V));
+}
+
+/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/
+line altitude(side side)
+{/*<asyxml></code><documentation>Return the altitude cutting 'side'.</documentation></function></asyxml>*/
+ return altitude(opposite(side));
+}
+
+/*<asyxml><function type="point" signature="orthocentercenter(triangle)"><code></asyxml>*/
+point orthocentercenter(triangle t)
+{/*<asyxml></code><documentation>Return the orthocenter of the triangle t.</documentation></function></asyxml>*/
+ return orthocentercenter(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="point" signature="centroid(triangle)"><code></asyxml>*/
+point centroid(triangle t)
+{/*<asyxml></code><documentation>Return the centroid of the triangle 't'.</documentation></function></asyxml>*/
+ return (t.A + t.B + t.C)/3;
+}
+
+/*<asyxml><function type="point" signature="circumcenter(triangle)"><code></asyxml>*/
+point circumcenter(triangle t)
+{/*<asyxml></code><documentation>Return the circumcenter of the triangle 't'.</documentation></function></asyxml>*/
+ return circumcenter(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="circle" signature="circle(triangle)"><code></asyxml>*/
+circle circle(triangle t)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/
+ return circle(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="circle" signature="circumcircle(triangle)"><code></asyxml>*/
+circle circumcircle(triangle t)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/
+ return circle(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="point" signature="incenter(triangle)"><code></asyxml>*/
+point incenter(triangle t)
+{/*<asyxml></code><documentation>Return the center of the incircle of the triangle 't'.</documentation></function></asyxml>*/
+ return incenter(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="real" signature="inradius(triangle)"><code></asyxml>*/
+real inradius(triangle t)
+{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle 't'.</documentation></function></asyxml>*/
+ return inradius(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="circle" signature="incircle(triangle)"><code></asyxml>*/
+circle incircle(triangle t)
+{/*<asyxml></code><documentation>Return the the incircle of the triangle 't'.</documentation></function></asyxml>*/
+ return incircle(t.A, t.B, t.C);
+}
+
+/*<asyxml><function type="point" signature="excenter(side,triangle)"><code></asyxml>*/
+point excenter(side side)
+{/*<asyxml></code><documentation>Return the center of the excircle tangent with the side 'side' of its triangle.
+ side = 0 means AB, 1 means AC, other means BC.
+ One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
+ point op;
+ triangle t = side.t;
+ int n = numarray[abs(side.n) - 1];
+ if(n == 1) op = excenter(t.A, t.B, t.C);
+ else if(n == 2) op = excenter(t.B, t.C, t.A);
+ else op = excenter(t.C, t.A, t.B);
+ return op;
+}
+
+/*<asyxml><function type="real" signature="exradius(side,triangle)"><code></asyxml>*/
+real exradius(side side)
+{/*<asyxml></code><documentation>Return radius of the excircle tangent with the side 'side' of its triangle.
+ side = 0 means AB, 1 means BC, other means CA.
+ One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
+ real or;
+ triangle t = side.t;
+ int n = numarray[abs(side.n) - 1];
+ if(n == 1) or = exradius(t.A, t.B, t.C);
+ else if(n == 2) or = exradius(t.B, t.C, t.A);
+ else or = exradius(t.A, t.C, t.B);
+ return or;
+}
+
+/*<asyxml><function type="circle" signature="excircle(side,triangle)"><code></asyxml>*/
+circle excircle(side side)
+{/*<asyxml></code><documentation>Return the excircle tangent with the side 'side' of its triangle.
+ side = 0 means AB, 1 means AC, other means BC.
+ One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
+ circle oc;
+ int n = numarray[abs(side.n) - 1];
+ triangle t = side.t;
+ if(n == 1) oc = excircle(t.A, t.B, t.C);
+ else if(n == 2) oc = excircle(t.B, t.C, t.A);
+ else oc = excircle(t.A, t.C, t.B);
+ return oc;
+}
+
+/*<asyxml><struct signature="trilinear"><code></asyxml>*/
+struct trilinear
+{/*<asyxml></code><documentation>Trilinear coordinates 'a:b:c' relative to triangle 't'.
+ <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation><property type = "real" signature="a,b,c"><code></asyxml>*/
+ real a,b,c;/*<asyxml></code><documentation>The trilinear coordinates.</documentation></property><property type = "triangle" signature="t"><code></asyxml>*/
+ triangle t;/*<asyxml></code><documentation>The reference triangle.</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="trilinear" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/
+trilinear trilinear(triangle t, real a, real b, real c)
+{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'.
+ <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
+ trilinear ot;
+ ot.a = a; ot.b = b; ot.c = c;
+ ot.t = t;
+ return ot;
+}
+
+/*<asyxml><function type="trilinear" signature="trilinear(triangle,point)"><code></asyxml>*/
+trilinear trilinear(triangle t, point M)
+{/*<asyxml></code><documentation>Return the trilinear coordinates of 'M' relative to 't'.
+ <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
+ trilinear ot;
+ pair m = locate(M);
+ int sameside(pair A, pair B, pair m, pair p)
+ {// Return 1 if 'm' and 'p' are same side of line (AB) else return -1.
+ pair mil = (A + B)/2;
+ pair mA = rotate(90, mil) * A;
+ pair mB = rotate(-90, mil) * A;
+ return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB)) ? 1 : -1;
+ }
+ real det(pair a, pair b) {return a.x * b.y - a.y * b.x;}
+ real area(pair a, pair b, pair c){return 0.5 * abs(det(a, b) + det(b, c) + det(c, a));}
+ pair A = t.A, B = t.B, C = t.C;
+ real t1 = area(B, C, m), t2 = area(C, A, m), t3 = area(A, B, m);
+ ot.a = sameside(B, C, A, m) * t1/t.a();
+ ot.b = sameside(A, C, B, m) * t2/t.b();
+ ot.c = sameside(A, B, C, m) * t3/t.c();
+ ot.t = t;
+ return ot;
+}
+
+/*<asyxml><function type="void" signature="write(trilinear)"><code></asyxml>*/
+void write(trilinear tri)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ write(format("%f : ", tri.a) + format("%f : ", tri.b) + format("%f", tri.c));
+}
+
+/*<asyxml><function type="point" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/
+point point(trilinear tri)
+{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'.
+ <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
+ triangle t = tri.t;
+ return masscenter(0.5 * t.a() * mass(t.A, tri.a),
+ 0.5 * t.b() * mass(t.B, tri.b),
+ 0.5 * t.c() * mass(t.C, tri.c));
+}
+
+/*<asyxml><function type="int[]" signature="tricoef(side)"><code></asyxml>*/
+int[] tricoef(side side)
+{/*<asyxml></code><documentation>Return an array of integer (values are 0 or 1) which represents 'side'.
+ For example, side = t.BC will be represented by {0, 1, 1}.</documentation></function></asyxml>*/
+ int[] oi;
+ int n = numarray[abs(side.n) - 1];
+ oi.push((n == 1 || n == 3) ? 1 : 0);
+ oi.push((n == 1 || n == 2) ? 1 : 0);
+ oi.push((n == 2 || n == 3) ? 1 : 0);
+ return oi;
+}
+
+/*<asyxml><operator type = "point" signature="cast(trilinear)"><code></asyxml>*/
+point operator cast(trilinear tri)
+{/*<asyxml></code><documentation>Cast trilinear to point.
+ One may use the routine 'point(trilinear)' to force the casting.</documentation></operator></asyxml>*/
+ return point(tri);
+}
+
+/*<asyxml><typedef type = "centerfunction" return = "real" params = "real, real, real"><code></asyxml>*/
+typedef real centerfunction(real, real, real);/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></typedef></asyxml>*/
+
+/*<asyxml><function type="trilinear" signature="trilinear(triangle,centerfunction,real,real,real)"><code></asyxml>*/
+trilinear trilinear(triangle t, centerfunction f, real a = t.a(), real b = t.b(), real c = t.c())
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></function></asyxml>*/
+ return trilinear(t, f(a, b, c), f(b, c, a), f(c, a, b));
+}
+
+/*<asyxml><function type="point" signature="symmedian(triangle)"><code></asyxml>*/
+point symmedian(triangle t)
+{/*<asyxml></code><documentation>Return the symmedian point of 't'.</documentation></function></asyxml>*/
+ point A, B, C;
+ real a = t.a(), b = t.b(), c = t.c();
+ A = trilinear(t, 0, b, c);
+ B = trilinear(t, a, 0, c);
+ return intersectionpoint(line(t.A, A), line(t.B, B));
+}
+
+/*<asyxml><function type="point" signature="symmedian(side)"><code></asyxml>*/
+point symmedian(side side)
+{/*<asyxml></code><documentation>The symmedian point on the side 'side'.</documentation></function></asyxml>*/
+ triangle t = side.t;
+ int n = numarray[abs(side.n) - 1];
+ if(n == 1) return trilinear(t, t.a(), t.b(), 0);
+ if(n == 2) return trilinear(t, 0, t.b(), t.c());
+ return trilinear(t, t.a(), 0, t.c());
+}
+
+/*<asyxml><function type="line" signature="symmedian(vertex)"><code></asyxml>*/
+line symmedian(vertex V)
+{/*<asyxml></code><documentation>Return the symmedian passing through 'V'.</documentation></function></asyxml>*/
+ return line(point(V), symmedian(V.t));
+}
+
+/*<asyxml><function type="triangle" signature="cevian(triangle,point)"><code></asyxml>*/
+triangle cevian(triangle t, point P)
+{/*<asyxml></code><documentation>Return the Cevian triangle with respect of 'P'
+ <url href = "http://mathworld.wolfram.com/CevianTriangle.html"/>.</documentation></function></asyxml>*/
+ trilinear tri = trilinear(t, locate(P));
+ point A = point(trilinear(t, 0, tri.b, tri.c));
+ point B = point(trilinear(t, tri.a, 0, tri.c));
+ point C = point(trilinear(t, tri.a, tri.b, 0));
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="point" signature="cevian(side,point)"><code></asyxml>*/
+point cevian(side side, point P)
+{/*<asyxml></code><documentation>Return the Cevian point on 'side' with respect of 'P'.</documentation></function></asyxml>*/
+ triangle t = side.t;
+ trilinear tri = trilinear(t, locate(P));
+ int[] s = tricoef(side);
+ return point(trilinear(t, s[0] * tri.a, s[1] * tri.b, s[2] * tri.c));
+}
+
+/*<asyxml><function type="line" signature="cevian(vertex,point)"><code></asyxml>*/
+line cevian(vertex V, point P)
+{/*<asyxml></code><documentation>Return line passing through 'V' and its Cevian image with respect of 'P'.</documentation></function></asyxml>*/
+ return line(point(V), cevian(opposite(V), P));
+}
+
+/*<asyxml><function type="point" signature="gergonne(triangle)"><code></asyxml>*/
+point gergonne(triangle t)
+{/*<asyxml></code><documentation>Return the Gergonne point of 't'.</documentation></function></asyxml>*/
+ real f(real a, real b, real c){return 1/(a * (b + c - a));}
+ return point(trilinear(t, f));
+}
+
+/*<asyxml><function type="point[]" signature="fermat(triangle)"><code></asyxml>*/
+point[] fermat(triangle t)
+{/*<asyxml></code><documentation>Return the Fermat points of 't'.</documentation></function></asyxml>*/
+ point[] P;
+ real A = t.alpha(), B = t.beta(), C = t.gamma();
+ P.push(point(trilinear(t, 1/Sin(A + 60), 1/Sin(B + 60), 1/Sin(C + 60))));
+ P.push(point(trilinear(t, 1/Sin(A - 60), 1/Sin(B - 60), 1/Sin(C - 60))));
+ return P;
+}
+
+/*<asyxml><function type="point" signature="isotomicconjugate(triangle,point)"><code></asyxml>*/
+point isotomicconjugate(triangle t, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
+ if(!inside(t.Path(), locate(M))) abort("isotomic: the point must be inside the triangle.");
+ trilinear tr = trilinear(t, M);
+ return point(trilinear(t, 1/(t.a()^2 * tr.a), 1/(t.b()^2 * tr.b), 1/(t.c()^2 * tr.c)));
+}
+
+/*<asyxml><function type="line" signature="isotomic(vertex,point)"><code></asyxml>*/
+line isotomic(vertex V, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/>.</documentation></function></asyxml>*/
+ side op = opposite(V);
+ return line(V, rotate(180, midpoint(op)) * cevian(op, M));
+}
+
+/*<asyxml><function type="point" signature="isotomic(side,point)"><code></asyxml>*/
+point isotomic(side side, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
+ return intersectionpoint(isotomic(opposite(side), M), side);
+}
+
+/*<asyxml><function type="triangle" signature="isotomic(triangle,point)"><code></asyxml>*/
+triangle isotomic(triangle t, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
+ return triangle(isotomic(t.BC, M), isotomic(t.CA, M), isotomic(t.AB, M));
+}
+
+/*<asyxml><function type="point" signature="isogonalconjugate(triangle,point)"><code></asyxml>*/
+point isogonalconjugate(triangle t, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ trilinear tr = trilinear(t, M);
+ return point(trilinear(t, 1/tr.a, 1/tr.b, 1/tr.c));
+}
+
+/*<asyxml><function type="point" signature="isogonal(side,point)"><code></asyxml>*/
+point isogonal(side side, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ return cevian(side, isogonalconjugate(side.t, M));
+}
+
+/*<asyxml><function type="line" signature="isogonal(vertex,point)"><code></asyxml>*/
+line isogonal(vertex V, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ return line(V, isogonal(opposite(V), M));
+}
+
+/*<asyxml><function type="triangle" signature="isogonal(triangle,point)"><code></asyxml>*/
+triangle isogonal(triangle t, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ return triangle(isogonal(t.BC, M), isogonal(t.CA, M), isogonal(t.AB, M));
+}
+
+/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/
+triangle pedal(triangle t, point M)
+{/*<asyxml></code><documentation>Return the pedal triangle of 'M' in 't'.
+ <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/
+ return triangle(projection(t.BC) * M, projection(t.AC) * M, projection(t.AB) * M);
+}
+
+/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/
+line pedal(side side, point M)
+{/*<asyxml></code><documentation>Return the pedal line of 'M' cutting 'side'.
+ <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/
+ return line(M, projection(side) * M);
+}
+
+/*<asyxml><function type="triangle" signature="antipedal(triangle,point)"><code></asyxml>*/
+triangle antipedal(triangle t, point M)
+{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/AntipedalTriangle.html"/></documentation></function></asyxml>*/
+ trilinear Tm = trilinear(t, M);
+ real a = Tm.a, b = Tm.b, c = Tm.c;
+ real CA = Cos(t.alpha()), CB = Cos(t.beta()), CC = Cos(t.gamma());
+ point A = trilinear(t, -(b + a * CC) * (c + a * CB), (c + a * CB) * (a + b * CC), (b + a * CC) * (a + c * CB));
+ point B = trilinear(t, (c + b * CA) * (b + a * CC), -(c + b * CA) * (a + b * CC), (a + b * CC) * (b + c * CA));
+ point C = trilinear(t, (b + c * CA) * (c + a * CB), (a + c * CB) * (c + b * CA), -(a + c * CB) * (b + c * CA));
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/
+triangle extouch(triangle t)
+{/*<asyxml></code><documentation>Return the extouch triangle of the triangle 't'.
+ The extouch triangle of 't' is the triangle formed by the points
+ of tangency of a triangle 't' with its excircles.</documentation></function></asyxml>*/
+ point A, B, C;
+ real a = t.a(), b = t.b(), c = t.c();
+ A = trilinear(t, 0, (a - b + c)/b, (a + b - c)/c);
+ B = trilinear(t, (-a + b + c)/a, 0, (a + b - c)/c);
+ C = trilinear(t, (-a + b + c)/a, (a - b + c)/b, 0);
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/
+triangle incentral(triangle t)
+{/*<asyxml></code><documentation>Return the incentral triangle of the triangle 't'.
+ It is the triangle whose vertices are determined by the intersections of the
+ reference triangle's angle bisectors with the respective opposite sides.</documentation></function></asyxml>*/
+ point A, B, C;
+ // real a = t.a(), b = t.b(), c = t.c();
+ A = trilinear(t, 0, 1, 1);
+ B = trilinear(t, 1, 0, 1);
+ C = trilinear(t, 1, 1, 0);
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="triangle" signature="extouch(side)"><code></asyxml>*/
+triangle extouch(side side)
+{/*<asyxml></code><documentation>Return the triangle formed by the points of tangency of the triangle referenced by 'side' with its excircles.
+ One vertex of the returned triangle is on the segment 'side'.</documentation></function></asyxml>*/
+ triangle t = side.t;
+ transform p1 = projection((line)t.AB);
+ transform p2 = projection((line)t.AC);
+ transform p3 = projection((line)t.BC);
+ point EP = excenter(side);
+ return triangle(p3 * EP, p2 * EP, p1 * EP);
+}
+
+/*<asyxml><function type="point" signature="bisectorpoint(side)"><code></asyxml>*/
+point bisectorpoint(side side)
+{/*<asyxml></code><documentation>The intersection point of the angle bisector from the
+ opposite point of 'side' with the side 'side'.</documentation></function></asyxml>*/
+ triangle t = side.t;
+ int n = numarray[abs(side.n) - 1];
+ if(n == 1) return trilinear(t, 1, 1, 0);
+ if(n == 2) return trilinear(t, 0, 1, 1);
+ return trilinear(t, 1, 0, 1);
+}
+
+/*<asyxml><function type="line" signature="bisector(vertex,real)"><code></asyxml>*/
+line bisector(vertex V, real angle = 0)
+{/*<asyxml></code><documentation>Return the interior bisector passing through 'V' rotated by angle (in degrees)
+ around 'V'.</documentation></function></asyxml>*/
+ return rotate(angle, point(V)) * line(point(V), incenter(V.t));
+}
+
+/*<asyxml><function type="line" signature="bisector(side)"><code></asyxml>*/
+line bisector(side side)
+{/*<asyxml></code><documentation>Return the bisector of the line segment 'side'.</documentation></function></asyxml>*/
+ return bisector(segment(side));
+}
+
+/*<asyxml><function type="point" signature="intouch(side)"><code></asyxml>*/
+point intouch(side side)
+{/*<asyxml></code><documentation>The point of tangency on the side 'side' of its incircle.</documentation></function></asyxml>*/
+ triangle t = side.t;
+ real a = t.a(), b = t.b(), c = t.c();
+ int n = numarray[abs(side.n) - 1];
+ if(n == 1) return trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0);
+ if(n == 2) return trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c));
+ return trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c));
+}
+
+/*<asyxml><function type="triangle" signature="intouch(triangle)"><code></asyxml>*/
+triangle intouch(triangle t)
+{/*<asyxml></code><documentation>Return the intouch triangle of the triangle 't'.
+ The intouch triangle of 't' is the triangle formed by the points
+ of tangency of a triangle 't' with its incircles.</documentation></function></asyxml>*/
+ point A, B, C;
+ real a = t.a(), b = t.b(), c = t.c();
+ A = trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c));
+ B = trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c));
+ C = trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0);
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="triangle" signature="tangential(triangle)"><code></asyxml>*/
+triangle tangential(triangle t)
+{/*<asyxml></code><documentation>Return the tangential triangle of the triangle 't'.
+ The tangential triangle of 't' is the triangle formed by the lines
+ tangent to the circumcircle of the given triangle 't' at its vertices.</documentation></function></asyxml>*/
+ point A, B, C;
+ real a = t.a(), b = t.b(), c = t.c();
+ A = trilinear(t, -a, b, c);
+ B = trilinear(t, a, -b, c);
+ C = trilinear(t, a, b, -c);
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="triangle" signature="medial(triangle t)"><code></asyxml>*/
+triangle medial(triangle t)
+{/*<asyxml></code><documentation>Return the triangle whose vertices are midpoints of the sides of 't'.</documentation></function></asyxml>*/
+ return triangle(midpoint(t.BC), midpoint(t.AC), midpoint(t.AB));
+}
+
+/*<asyxml><function type="line" signature="median(vertex)"><code></asyxml>*/
+line median(vertex V)
+{/*<asyxml></code><documentation>Return median from 'V'.</documentation></function></asyxml>*/
+ return line(point(V), midpoint(segment(opposite(V))));
+}
+
+/*<asyxml><function type="line" signature="median(side)"><code></asyxml>*/
+line median(side side)
+{/*<asyxml></code><documentation>Return median from the opposite vertex of 'side'.</documentation></function></asyxml>*/
+ return median(opposite(side));
+}
+
+/*<asyxml><function type="triangle" signature="orthic(triangle)"><code></asyxml>*/
+triangle orthic(triangle t)
+{/*<asyxml></code><documentation>Return the triangle whose vertices are endpoints of the altitudes from each of the vertices of 't'.</documentation></function></asyxml>*/
+ return triangle(foot(t.BC), foot(t.AC), foot(t.AB));
+}
+
+/*<asyxml><function type="triangle" signature="symmedial(triangle)"><code></asyxml>*/
+triangle symmedial(triangle t)
+{/*<asyxml></code><documentation>Return the symmedial triangle of 't'.</documentation></function></asyxml>*/
+ point A, B, C;
+ real a = t.a(), b = t.b(), c = t.c();
+ A = trilinear(t, 0, b, c);
+ B = trilinear(t, a, 0, c);
+ C = trilinear(t, a, b, 0);
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="triangle" signature="anticomplementary(triangle)"><code></asyxml>*/
+triangle anticomplementary(triangle t)
+{/*<asyxml></code><documentation>Return the triangle which has the given triangle 't' as its medial triangle.</documentation></function></asyxml>*/
+ real a = t.a(), b = t.b(), c = t.c();
+ real ab = a * b, bc = b * c, ca = c * a;
+ point A = trilinear(t, -bc, ca, ab);
+ point B = trilinear(t, bc, -ca, ab);
+ point C = trilinear(t, bc, ca, -ab);
+ return triangle(A, B, C);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,line,bool)"><code></asyxml>*/
+point[] intersectionpoints(triangle t, line l, bool extended = false)
+{/*<asyxml></code><documentation>Return the intersection points.
+ If 'extended' is true, the sides are lines else the sides are segments.
+ intersectionpoints(line, triangle, bool) is also defined.</documentation></function></asyxml>*/
+ point[] OP;
+ void addpoint(point P)
+ {
+ if(defined(P)) {
+ bool exist = false;
+ for (int i = 0; i < OP.length; ++i) {
+ if(P == OP[i]) {exist = true; break;}
+ }
+ if(!exist) OP.push(P);
+ }
+ }
+ if(extended) {
+ for (int i = 1; i <= 3; ++i) {
+ addpoint(intersectionpoint(t.line(i), l));
+ }
+ } else {
+ for (int i = 1; i <= 3; ++i) {
+ addpoint(intersectionpoint((segment)t.line(i), l));
+ }
+ }
+ return OP;
+}
+
+point[] intersectionpoints(line l, triangle t, bool extended = false)
+{
+ return intersectionpoints(t, l, extended);
+}
+
+/*<asyxml><function type="vector" signature="dir(vertex)"><code></asyxml>*/
+vector dir(vertex V)
+{/*<asyxml></code><documentation>The direction (towards the outside of the triangle) of the interior angle bisector of 'V'.</documentation></function></asyxml>*/
+ triangle t = V.t;
+ if(V.n == 1) return vector(defaultcoordsys, (-dir(t.A--t.B, t.A--t.C)));
+ if(V.n == 2) return vector(defaultcoordsys, (-dir(t.B--t.A, t.B--t.C)));
+ return vector(defaultcoordsys, (-dir(t.C--t.A, t.C--t.B)));
+}
+
+/*<asyxml><function type="void" signature="lvoid label(picture,Label,vertex,pair,real,pen,filltype)"><code></asyxml>*/
+void label(picture pic = currentpicture, Label L, vertex V,
+ pair align = dir(V),
+ real alignFactor = 1,
+ pen p = nullpen, filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw 'L' on picture 'pic' at vertex 'V' aligned by 'alignFactor * align'.</documentation></function></asyxml>*/
+ label(pic, L, locate(point(V)), alignFactor * align, p, filltype);
+}
+
+/*<asyxml><function type="void" signature="label(picture,Label,Label,Label,triangle,real,real,pen,filltype)"><code></asyxml>*/
+void label(picture pic = currentpicture, Label LA = "$A$",
+ Label LB = "$B$", Label LC = "$C$",
+ triangle t,
+ real alignAngle = 0,
+ real alignFactor = 1,
+ pen p = nullpen, filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw labels LA, LB and LC aligned in the rotated (by 'alignAngle' in degrees) direction
+ (towards the outside of the triangle) of the interior angle bisector of vertices.
+ One can individually modify the alignment by setting the Label parameter 'align'.</documentation></function></asyxml>*/
+ Label lla = LA.copy();
+ lla.align(lla.align, rotate(alignAngle) * locate(dir(t.VA)));
+ label(pic, LA, t.VA, align = lla.align.dir, alignFactor = alignFactor, p, filltype);
+ Label llb = LB.copy();
+ llb.align(llb.align, rotate(alignAngle) * locate(dir(t.VB)));
+ label(pic, llb, t.VB, align = llb.align.dir, alignFactor = alignFactor, p, filltype);
+ Label llc = LC.copy();
+ llc.align(llc.align, rotate(alignAngle) * locate(dir(t.VC)));
+ label(pic, llc, t.VC, align = llc.align.dir, alignFactor = alignFactor, p, filltype);
+}
+
+/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,Label,Label,Label,triangle,pen,filltype)"><code></asyxml>*/
+void show(picture pic = currentpicture,
+ Label LA = "$A$", Label LB = "$B$", Label LC = "$C$",
+ Label La = "$a$", Label Lb = "$b$", Label Lc = "$c$",
+ triangle t, pen p = currentpen, filltype filltype = NoFill)
+{/*<asyxml></code><documentation>Draw triangle and labels of sides and vertices.</documentation></function></asyxml>*/
+ pair a = locate(t.A), b = locate(t.B), c = locate(t.C);
+ draw(pic, a--b--c--cycle, p);
+ label(pic, LA, a, -dir(a--b, a--c), p, filltype);
+ label(pic, LB, b, -dir(b--a, b--c), p, filltype);
+ label(pic, LC, c, -dir(c--a, c--b), p, filltype);
+ pair aligna = I * unit(c - b), alignb = I * unit(c - a), alignc = I * unit(b - a);
+ pair mAB = locate(midpoint(t.AB)), mAC = locate(midpoint(t.AC)), mBC = locate(midpoint(t.BC));
+ label(pic, La, b--c, align = rotate(dot(a - mBC, aligna) > 0 ? 180 :0) * aligna, p);
+ label(pic, Lb, a--c, align = rotate(dot(b - mAC, alignb) > 0 ? 180 :0) * alignb, p);
+ label(pic, Lc, a--b, align = rotate(dot(c - mAB, alignc) > 0 ? 180 :0) * alignc, p);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,triangle,pen,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, triangle t, pen p = currentpen, marker marker = nomarker)
+{/*<asyxml></code><documentation>Draw sides of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
+ draw(pic, t.Path(), p, marker);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,triangle[],pen,marker)"><code></asyxml>*/
+void draw(picture pic = currentpicture, triangle[] t, pen p = currentpen, marker marker = nomarker)
+{/*<asyxml></code><documentation>Draw sides of the triangles 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
+ for(int i = 0; i < t.length; ++i) draw(pic, t[i], p, marker);
+}
+
+/*<asyxml><function type="void" signature="drawline(picture,triangle,pen)"><code></asyxml>*/
+void drawline(picture pic = currentpicture, triangle t, pen p = currentpen)
+{/*<asyxml></code><documentation>Draw lines of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
+ draw(t, p);
+ draw(pic, line(t.A, t.B), p);
+ draw(pic, line(t.A, t.C), p);
+ draw(pic, line(t.B, t.C), p);
+}
+
+/*<asyxml><function type="void" signature="dot(picture,triangle,pen)"><code></asyxml>*/
+void dot(picture pic = currentpicture, triangle t, pen p = currentpen)
+{/*<asyxml></code><documentation>Draw a dot at each vertex of 't'.</documentation></function></asyxml>*/
+ dot(pic, t.A^^t.B^^t.C, p);
+}
+// *.......................TRIANGLES.......................*
+// *=======================================================*
+
+// *=======================================================*
+// *.......................INVERSIONS......................*
+/*<asyxml><function type="point" signature="inverse(real k,point,point)"><code></asyxml>*/
+point inverse(real k, point A, point M)
+{/*<asyxml></code><documentation>Return the inverse point of 'M' with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
+ return A + k/conj(M - A);
+}
+
+/*<asyxml><function type="point" signature="radicalcenter(circle,circle)"><code></asyxml>*/
+point radicalcenter(circle c1, circle c2)
+{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(c1.C, c2.C);
+ real k = c1.r^2 - c2.r^2;
+ pair C1 = locate(c1.C);
+ pair C2 = locate(c2.C);
+ pair oop = C2 - C1;
+ pair K = (abs(oop) == 0) ?
+ (infinity, infinity) :
+ midpoint(C1--C2) + 0.5 * k * oop/dot(oop, oop);
+ return point(P[0].coordsys, K/P[0].coordsys);
+}
+
+/*<asyxml><function type="line" signature="radicalline(circle,circle)"><code></asyxml>*/
+line radicalline(circle c1, circle c2)
+{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
+ if (c1.C == c2.C) abort("radicalline: the centers must be distinct");
+ return perpendicular(radicalcenter(c1, c2), line(c1.C, c2.C));
+}
+
+/*<asyxml><function type="point" signature="radicalcenter(circle,circle,circle)"><code></asyxml>*/
+point radicalcenter(circle c1, circle c2, circle c3)
+{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
+ return intersectionpoint(radicalline(c1, c2), radicalline(c1, c3));
+}
+
+/*<asyxml><struct signature="inversion"><code></asyxml>*/
+struct inversion
+{/*<asyxml></code><documentation>http://mathworld.wolfram.com/Inversion.html</documentation></asyxml>*/
+ point C;
+ real k;
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/
+inversion inversion(real k, point C)
+{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/
+ inversion oi;
+ oi.k = k;
+ oi.C = C;
+ return oi;
+}
+/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/
+inversion inversion(point C, real k)
+{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/
+ return inversion(k, C);
+}
+
+/*<asyxml><function type="inversion" signature="inversion(circle,circle)"><code></asyxml>*/
+inversion inversion(circle c1, circle c2, real sgn = 1)
+{/*<asyxml></code><documentation>Return the inversion which transforms 'c1' to
+ . 'c2' and positive inversion radius if 'sgn > 0';
+ . 'c2' and negative inversion radius if 'sgn < 0';
+ . 'c1' and 'c2' to 'c2' if 'sgn = 0'.</documentation></function></asyxml>*/
+ if(sgn == 0) {
+ point O = radicalcenter(c1, c2);
+ return inversion(O^c1, O);
+ }
+ real a = abs(c1.r/c2.r);
+ if(sgn > 0) {
+ point O = c1.C + a/abs(1 - a) * (c2.C - c1.C);
+ return inversion(a * abs(abs(O - c2.C)^2 - c2.r^2), O);
+ }
+ point O = c1.C + a/abs(1 + a) * (c2.C - c1.C);
+ return inversion(-a * abs(abs(O - c2.C)^2 - c2.r^2), O);
+}
+
+/*<asyxml><function type="inversion" signature="inversion(circle,circle,circle)"><code></asyxml>*/
+inversion inversion(circle c1, circle c2, circle c3)
+{/*<asyxml></code><documentation>Return the inversion which transform 'c1' to 'c1', 'c2' to 'c2' and 'c3' to 'c3'.</documentation></function></asyxml>*/
+ point Rc = radicalcenter(c1, c2, c3);
+ return inversion(Rc, Rc^c1);
+}
+
+circle operator cast(inversion i){return circle(i.C, sgn(i.k) * sqrt(abs(i.k)));}
+/*<asyxml><function type="circle" signature="circle(inversion)"><code></asyxml>*/
+circle circle(inversion i)
+{/*<asyxml></code><documentation>Return the inversion circle of 'i'.</documentation></function></asyxml>*/
+ return i;
+}
+
+inversion operator cast(circle c)
+{
+ return inversion(sgn(c.r) * c.r^2, c.C);
+}
+/*<asyxml><function type="inversion" signature="inversion(circle)"><code></asyxml>*/
+inversion inversion(circle c)
+{/*<asyxml></code><documentation>Return the inversion represented by the circle of 'c'.</documentation></function></asyxml>*/
+ return c;
+}
+
+/*<asyxml><operator type = "point" signature="*(inversion,point)"><code></asyxml>*/
+point operator *(inversion i, point P)
+{/*<asyxml></code><documentation>Provide inversion * point.</documentation></operator></asyxml>*/
+ return inverse(i.k, i.C, P);
+}
+
+void lineinversion()
+{
+ warning("lineinversion", "the inversion of the line is not a circle.
+The returned circle has an infinite radius, circle.l has been set.");
+}
+
+
+/*<asyxml><function type="circle" signature="inverse(real,point,line)"><code></asyxml>*/
+circle inverse(real k, point A, line l)
+{/*<asyxml></code><documentation>Return the inverse circle of 'l' with
+ respect to point 'A' and inversion radius 'k'.</documentation></function></asyxml>*/
+ if(A @ l) {
+ lineinversion();
+ circle C = circle(A, infinity);
+ C.l = l;
+ return C;
+ }
+ point Ap = inverse(k, A, l.A), Bp = inverse(k, A, l.B);
+ return circle(A, Ap, Bp);
+}
+
+/*<asyxml><operator type = "circle" signature="*(inversion,line)"><code></asyxml>*/
+circle operator *(inversion i, line l)
+{/*<asyxml></code><documentation>Provide inversion * line for lines that don't pass through the inversion center.</documentation></operator></asyxml>*/
+ return inverse(i.k, i.C, l);
+}
+
+/*<asyxml><function type="circle" signature="inverse(real,point,circle)"><code></asyxml>*/
+circle inverse(real k, point A, circle c)
+{/*<asyxml></code><documentation>Return the inverse circle of 'c' with
+ respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
+ if(degenerate(c)) return inverse(k, A, c.l);
+ if(A @ c) {
+ lineinversion();
+ point M = rotate(180, c.C) * A, Mp = rotate(90, c.C) * A;
+ circle oc = circle(A, infinity);
+ oc.l = line(inverse(k, A, M), inverse(k, A, Mp));
+ return oc;
+ }
+ point[] P = standardizecoordsys(A, c.C);
+ real s = k/((P[1].x - P[0].x)^2 + (P[1].y - P[0].y)^2 - c.r^2);
+ return circle(P[0] + s * (P[1]-P[0]), abs(s) * c.r);
+}
+
+/*<asyxml><operator type = "circle" signature="*(inversion,circle)"><code></asyxml>*/
+circle operator *(inversion i, circle c)
+{/*<asyxml></code><documentation>Provide inversion * circle.</documentation></operator></asyxml>*/
+ return inverse(i.k, i.C, c);
+}
+// *.......................INVERSIONS......................*
+// *=======================================================*
+
+// *=======================================================*
+// *........................FOOTER.........................*
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,circle)"><code></asyxml>*/
+point[] intersectionpoints(line l, circle c)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(circle, line) is also defined.</documentation></function></asyxml>*/
+ if(degenerate(c)) return new point[]{intersectionpoint(l, c.l)};
+ point[] op;
+ coordsys R = samecoordsys(l.A, c.C) ?
+ l.A.coordsys : defaultcoordsys;
+ coordsys Rp = defaultcoordsys;
+ circle cc = circle(changecoordsys(Rp, c.C), c.r);
+ point proj = projection(l) * c.C;
+ if(proj @ cc) { // The line is a tangente of the circle.
+ if(proj @ l) op.push(proj);// line may be a segement...
+ } else {
+ coordsys Rc = cartesiansystem(c.C, (1, 0), (0, 1));
+ line ll = changecoordsys(Rc, l);
+ pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates,
+ 1, 0, 1, 0, 0, -c.r^2);
+ for (int i = 0; i < P.length; ++i) {
+ point inter = changecoordsys(R, point(Rc, P[i]));
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(circle c, line l)
+{
+ return intersectionpoints(l, c);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(line l, ellipse el)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(ellipse, line) is also defined.</documentation></function></asyxml>*/
+ if(el.e == 0) return intersectionpoints(l, (circle)el);
+ if(degenerate(el)) return new point[]{intersectionpoint(l, el.l)};
+ point[] op;
+ coordsys R = samecoordsys(l.A, el.C) ? l.A.coordsys : defaultcoordsys;
+ coordsys Rp = defaultcoordsys;
+ line ll = changecoordsys(Rp, l);
+ ellipse ell = changecoordsys(Rp, el);
+ circle C = circle(ell.C, ell.a);
+ point[] Ip = intersectionpoints(ll, C);
+ if (Ip.length > 0 &&
+ (perpendicular(ll, line(ell.F1, Ip[0])) ||
+ perpendicular(ll, line(ell.F2, Ip[0])))) {
+ // http://www.mathcurve.com/courbes2d/ellipse/ellipse.shtml
+ // Définition tangentielle par antipodaire de cercle.
+ // 'l' is a tangent of 'el'
+ transform t = scale(el.a/el.b, el.F1, el.F2, el.C, rotate(90, el.C) * el.F1);
+ point inter = inverse(t) * intersectionpoints(C, t * ll)[0];
+ if(inter @ l) op.push(inter);
+ } else {
+ coordsys Rc = canonicalcartesiansystem(el);
+ line ll = changecoordsys(Rc, l);
+ pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates,
+ 1/el.a^2, 0, 1/el.b^2, 0, 0, -1);
+ for (int i = 0; i < P.length; ++i) {
+ point inter = changecoordsys(R, point(Rc, P[i]));
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(ellipse el, line l)
+{
+ return intersectionpoints(l, el);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,parabola)"><code></asyxml>*/
+point[] intersectionpoints(line l, parabola p)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(parabola, line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ coordsys R = coordsys(p);
+ bool tgt = false;
+ line ll = changecoordsys(R, l),
+ lv = parallel(p.V, p.D);
+ point M = intersectionpoint(lv, ll), tgtp;
+ if(finite(M)) {// Test if 'l' is tangent to 'p'
+ line l1 = bisector(line(M, p.F));
+ line l2 = rotate(90, M) * lv;
+ point P = intersectionpoint(l1, l2);
+ tgtp = rotate(180, P) * p.F;
+ tgt = (tgtp @ l);
+ }
+ if(tgt) {
+ if(tgtp @ l) op.push(tgtp);
+ } else {
+ real[] eq = changecoordsys(defaultcoordsys, equation(p)).a;
+ pair[] tp = intersectionpoints(locate(l.A), locate(l.B), eq);
+ point inter;
+ for (int i = 0; i < tp.length; ++i) {
+ inter = point(R, tp[i]/R);
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(parabola p, line l)
+{
+ return intersectionpoints(l, p);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(line l, hyperbola h)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(hyperbola, line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ coordsys R = coordsys(h);
+ point A = intersectionpoint(l, h.A1), B = intersectionpoint(l, h.A2);
+ point M = midpoint(segment(A, B));
+ bool tgt = M @ h;
+ if(tgt) {
+ if(M @ l) op.push(M);
+ } else {
+ real[] eq = changecoordsys(defaultcoordsys, equation(h)).a;
+ pair[] tp = intersectionpoints(locate(l.A), locate(l.B), eq);
+ point inter;
+ for (int i = 0; i < tp.length; ++i) {
+ inter = point(R, tp[i]/R);
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(hyperbola h, line l)
+{
+ return intersectionpoints(l, h);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,conic)"><code></asyxml>*/
+point[] intersectionpoints(line l, conic co)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(conic, line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ if(co.e < 1) op = intersectionpoints((ellipse)co, l);
+ else
+ if(co.e == 1) op = intersectionpoints((parabola)co, l);
+ else op = intersectionpoints((hyperbola)co, l);
+ return op;
+}
+
+point[] intersectionpoints(conic co, line l)
+{
+ return intersectionpoints(l, co);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(conic,conic)"><code></asyxml>*/
+point[] intersectionpoints(conic co1, conic co2)
+{/*<asyxml></code><documentation>Return the intersection points of the two conics.</documentation></function></asyxml>*/
+ if(degenerate(co1)) return intersectionpoints(co1.l[0], co2);
+ if(degenerate(co2)) return intersectionpoints(co1, co2.l[0]);
+ return intersectionpoints(equation(co1), equation(co2));
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,conic,bool)"><code></asyxml>*/
+point[] intersectionpoints(triangle t, conic co, bool extended = false)
+{/*<asyxml></code><documentation>Return the intersection points.
+ If 'extended' is true, the sides are lines else the sides are segments.
+ intersectionpoints(conic, triangle, bool) is also defined.</documentation></function></asyxml>*/
+ if(degenerate(co)) return intersectionpoints(t, co.l[0], extended);
+ point[] OP;
+ void addpoint(point P[])
+ {
+ for (int i = 0; i < P.length; ++i) {
+ if(defined(P[i])) {
+ bool exist = false;
+ for (int j = 0; j < OP.length; ++j) {
+ if(P[i] == OP[j]) {exist = true; break;}
+ }
+ if(!exist) OP.push(P[i]);
+ }}}
+ if(extended) {
+ for (int i = 1; i <= 3; ++i) {
+ addpoint(intersectionpoints(t.line(i), co));
+ }
+ } else {
+ for (int i = 1; i <= 3; ++i) {
+ addpoint(intersectionpoints((segment)t.line(i), co));
+ }
+ }
+ return OP;
+}
+
+point[] intersectionpoints(conic co, triangle t, bool extended = false)
+{
+ return intersectionpoints(t, co, extended);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l, b);
+ // if(degenerate(b)) return intersectionpoints(a, b.l);;
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,circle)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, circle b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l, b);
+ // if(degenerate(b)) return intersectionpoints(a, b.l);;
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(circle a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints(b, a);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,parabola)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l, b);
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints(b, a);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l, b);
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints(b, a);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,parabola)"><code></asyxml>*/
+point[] intersectionpoints(circle a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,circle)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, circle b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(circle a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,circle)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, circle b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,parabola)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,parabola)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a, (conic)b);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,circle)"><code></asyxml>*/
+point[] intersectionpoints(circle c1, circle c2)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ if(degenerate(c1))
+ return degenerate(c2) ?
+ new point[]{intersectionpoint(c1.l, c2.l)} : intersectionpoints(c1.l, c2);
+ if(degenerate(c2)) return intersectionpoints(c1, c2.l);
+ return (c1.C == c2.C) ?
+ new point[] :
+ intersectionpoints(radicalline(c1, c2), c1);
+}
+
+/*<asyxml><function type="line" signature="tangent(circle,abscissa)"><code></asyxml>*/
+line tangent(circle c, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'c' at 'point(c, x)'.</documentation></function></asyxml>*/
+ if(c.r == 0) abort("tangent: a circle with a radius equals zero has no tangent.");
+ point M = point(c, x);
+ return line(rotate(90, M) * c.C, M);
+}
+
+/*<asyxml><function type="line[]" signature="tangents(circle,point)"><code></asyxml>*/
+line[] tangents(circle c, point M)
+{/*<asyxml></code><documentation>Return the tangents of 'c' passing through 'M'.</documentation></function></asyxml>*/
+ line[] ol;
+ if(inside(c, M)) return ol;
+ if(M @ c) {
+ ol.push(tangent(c, relabscissa(c, M)));
+ } else {
+ circle cc = circle(c.C, M);
+ point[] inter = intersectionpoints(c, cc);
+ for (int i = 0; i < inter.length; ++i)
+ ol.push(tangents(c, inter[i])[0]);
+ }
+ return ol;
+}
+
+/*<asyxml><function type="point" signature="point(circle,point)"><code></asyxml>*/
+point point(circle c, point M)
+{/*<asyxml></code><documentation>Return the intersection point of 'c'
+ with the half-line '[c.C M)'.</documentation></function></asyxml>*/
+ return intersectionpoints(c, line(c.C, false, M))[0];
+}
+
+/*<asyxml><function type="line" signature="tangent(circle,point)"><code></asyxml>*/
+line tangent(circle c, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'c' at the
+ intersection point of the half-line'[c.C M)'.</documentation></function></asyxml>*/
+ return tangents(c, point(c, M))[0];
+}
+
+/*<asyxml><function type="point" signature="point(circle,explicit vector)"><code></asyxml>*/
+point point(circle c, explicit vector v)
+{/*<asyxml></code><documentation>Return the intersection point of 'c'
+ with the half-line '[c.C v)'.</documentation></function></asyxml>*/
+ return point(c, c.C + v);
+}
+
+/*<asyxml><function type="line" signature="tangent(circle,explicit vector)"><code></asyxml>*/
+line tangent(circle c, explicit vector v)
+{/*<asyxml></code><documentation>Return the tangent of 'c' at the
+ point M so that vec(c.C M) is collinear to 'v' with the same sense.</documentation></function></asyxml>*/
+ line ol = tangent(c, c.C + v);
+ return dot(ol.v, v) > 0 ? ol : reverse(ol);
+}
+
+/*<asyxml><function type="line" signature="tangent(ellipse,abscissa)"><code></asyxml>*/
+line tangent(ellipse el, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'el' at 'point(el, x)'.</documentation></function></asyxml>*/
+ point M = point(el, x);
+ line l1 = line(el.F1, M);
+ line l2 = line(el.F2, M);
+ line ol = (l1 == l2) ? perpendicular(M, l1) : bisector(l1, l2, 90, false);
+ return ol;
+}
+
+/*<asyxml><function type="line[]" signature="tangents(ellipse,point)"><code></asyxml>*/
+line[] tangents(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the tangents of 'el' passing through 'M'.</documentation></function></asyxml>*/
+ line[] ol;
+ if(inside(el, M)) return ol;
+ if(M @ el) {
+ ol.push(tangent(el, relabscissa(el, M)));
+ } else {
+ point Mp = samecoordsys(M, el.F2) ?
+ M : changecoordsys(el.F2.coordsys, M);
+ circle c = circle(Mp, abs(el.F1 - Mp));
+ circle cc = circle(el.F2, 2 * el.a);
+ point[] inter = intersectionpoints(c, cc);
+ for (int i = 0; i < inter.length; ++i) {
+ line tl = line(inter[i], el.F2, false);
+ point[] P = intersectionpoints(tl, el);
+ ol.push(line(Mp, P[0]));
+ }
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="tangent(parabola,abscissa)"><code></asyxml>*/
+line tangent(parabola p, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'p' at 'point(p, x)' (use the Wells method).</documentation></function></asyxml>*/
+ line lt = rotate(90, p.V) * line(p.V, p.F);
+ point P = point(p, x);
+ if(P == p.V) return lt;
+ point M = midpoint(segment(P, p.F));
+ line l = rotate(90, M) * line(P, p.F);
+ return line(P, projection(lt) * M);
+}
+
+/*<asyxml><function type="line[]" signature="tangents(parabola,point)"><code></asyxml>*/
+line[] tangents(parabola p, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'p' at 'M' (use the Wells method).</documentation></function></asyxml>*/
+ line[] ol;
+ if(inside(p, M)) return ol;
+ if(M @ p) {
+ ol.push(tangent(p, angabscissa(p, M)));
+ }
+ else {
+ point Mt = changecoordsys(coordsys(p), M);
+ circle c = circle(Mt, p.F);
+ line l = rotate(90, p.V) * line(p.V, p.F);
+ point[] R = intersectionpoints(l, c);
+ for (int i = 0; i < R.length; ++i) {
+ ol.push(line(Mt, R[i]));
+ }
+ // An other method: http://www.du.edu/~jcalvert/math/parabola.htm
+ // point[] R = intersectionpoints(p.directrix, c);
+ // for (int i = 0; i < R.length; ++i) {
+ // ol.push(bisector(segment(p.F, R[i])));
+ // }
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="tangent(hyperbola,abscissa)"><code></asyxml>*/
+line tangent(hyperbola h, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'h' at 'point(p, x)'.</documentation></function></asyxml>*/
+ point M = point(h, x);
+ line ol = bisector(line(M, h.F1), line(M, h.F2));
+ if(sameside(h.F1, h.F2, ol) || ol == line(h.F1, h.F2)) ol = rotate(90, M) * ol;
+ return ol;
+}
+
+/*<asyxml><function type="line[]" signature="tangents(hyperbola,point)"><code></asyxml>*/
+line[] tangents(hyperbola h, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'h' at 'M'.</documentation></function></asyxml>*/
+ line[] ol;
+ if(M @ h) {
+ ol.push(tangent(h, angabscissa(h, M, fromCenter)));
+ } else {
+ coordsys cano = canonicalcartesiansystem(h);
+ bqe bqe = changecoordsys(cano, equation(h));
+ real a = abs(1/(bqe.a[5] * bqe.a[0])), b = abs(1/(bqe.a[5] * bqe.a[2]));
+ point Mp = changecoordsys(cano, M);
+ real x0 = Mp.x, y0 = Mp.y;
+ if(abs(x0) > epsgeo) {
+ real c0 = a * y0^2/(b * x0)^2 - 1/b,
+ c1 = 2 * a * y0/(b * x0^2), c2 = a/x0^2 - 1;
+ real[] sol = quadraticroots(c0, c1, c2);
+ for (real y:sol) {
+ point tmp = changecoordsys(coordsys(h), point(cano, (a * (1 + y * y0/b)/x0, y)));
+ ol.push(line(M, tmp));
+ }
+ } else if(abs(y0) > epsgeo) {
+ real y = -b/y0, x = sqrt(a * (1 + b/y0^2));
+ ol.push(line(M, changecoordsys(coordsys(h), point(cano, (x, y)))));
+ ol.push(line(M, changecoordsys(coordsys(h), point(cano, (-x, y)))));
+ }}
+ return ol;
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(conic,arc)"><code></asyxml>*/
+point[] intersectionpoints(conic co, arc a)
+{/*<asyxml></code><documentation>intersectionpoints(arc, circle) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ point[] tp = intersectionpoints(co, (conic)a.el);
+ for (int i = 0; i < tp.length; ++i)
+ if(tp[i] @ a) op.push(tp[i]);
+ return op;
+}
+
+point[] intersectionpoints(arc a, conic co)
+{
+ return intersectionpoints(co, a);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(arc,arc)"><code></asyxml>*/
+point[] intersectionpoints(arc a1, arc a2)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ point[] op;
+ point[] tp = intersectionpoints(a1.el, a2.el);
+ for (int i = 0; i < tp.length; ++i)
+ if(tp[i] @ a1 && tp[i] @ a2) op.push(tp[i]);
+ return op;
+}
+
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,arc)"><code></asyxml>*/
+point[] intersectionpoints(line l, arc a)
+{/*<asyxml></code><documentation>intersectionpoints(arc, line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ point[] tp = intersectionpoints(a.el, l);
+ for (int i = 0; i < tp.length; ++i)
+ if(tp[i] @ a && tp[i] @ l) op.push(tp[i]);
+ return op;
+}
+
+point[] intersectionpoints(arc a, line l)
+{
+ return intersectionpoints(l, a);
+}
+
+/*<asyxml><function type="point" signature="arcsubtendedcenter(point,point,real)"><code></asyxml>*/
+point arcsubtendedcenter(point A, point B, real angle)
+{/*<asyxml></code><documentation>Return the center of the arc retuned
+ by the 'arcsubtended' routine.</documentation></function></asyxml>*/
+ point OM;
+ point[] P = standardizecoordsys(A, B);
+ angle = angle%(sgnd(angle) * 180);
+ line bis = bisector(P[0], P[1]);
+ line AB = line(P[0], P[1]);
+ return intersectionpoint(bis, rotate(90 - angle, A) * AB);
+}
+
+/*<asyxml><function type="arc" signature="arcsubtended(point,point,real)"><code></asyxml>*/
+arc arcsubtended(point A, point B, real angle)
+{/*<asyxml></code><documentation>Return the arc circle from which the segment AB is saw with
+ the angle 'angle'.
+ If the point 'M' is on this arc, the oriented angle (MA, MB) is
+ equal to 'angle'.</documentation></function></asyxml>*/
+ point[] P = standardizecoordsys(A, B);
+ line AB = line(P[0], P[1]);
+ angle = angle%(sgnd(angle) * 180);
+ point C = arcsubtendedcenter(P[0], P[1], angle);
+ real BC = degrees(B - C)%360;
+ real AC = degrees(A - C)%360;
+ return arc(circle(C, abs(B - C)), BC, AC, angle > 0 ? CCW : CW);
+}
+
+/*<asyxml><function type="arc" signature="arccircle(point,point,point)"><code></asyxml>*/
+arc arccircle(point A, point M, point B)
+{/*<asyxml></code><documentation>Return the CCW arc circle 'AB' passing through 'M'.</documentation></function></asyxml>*/
+ circle tc = circle(A, M, B);
+ real a = degrees(A - tc.C);
+ real b = degrees(B - tc.C);
+ real m = degrees(M - tc.C);
+
+ arc oa = arc(tc, a, b);
+ // TODO : use cross product to determine CWW or CW
+ if (!(M @ oa)) {
+ oa.direction = !oa.direction;
+ }
+
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="arc(ellipse,abscissa,abscissa,bool)"><code></asyxml>*/
+arc arc(ellipse el, explicit abscissa x1, explicit abscissa x2, bool direction = CCW)
+{/*<asyxml></code><documentation>Return the arc from 'point(c, x1)' to 'point(c, x2)' in the direction 'direction'.</documentation></function></asyxml>*/
+ real a = degrees(point(el, x1) - el.C);
+ real b = degrees(point(el, x2) - el.C);
+ arc oa = arc(el, a - el.angle, b - el.angle, fromCenter, direction);
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="arc(ellipse,point,point,bool)"><code></asyxml>*/
+arc arc(ellipse el, point M, point N, bool direction = CCW)
+{/*<asyxml></code><documentation>Return the arc from 'M' to 'N' in the direction 'direction'.
+ The points 'M' and 'N' must belong to the ellipse 'el'.</documentation></function></asyxml>*/
+ return arc(el, relabscissa(el, M), relabscissa(el, N), direction);
+}
+
+/*<asyxml><function type="arc" signature="arccircle(point,point,real,bool)"><code></asyxml>*/
+arc arccircle(point A, point B, real angle, bool direction = CCW)
+{/*<asyxml></code><documentation>Return the arc circle centered on A
+ from B to rotate(angle, A) * B in the direction 'direction'.</documentation></function></asyxml>*/
+ point M = rotate(angle, A) * B;
+ return arc(circle(A, abs(A - B)), B, M, direction);
+}
+
+/*<asyxml><function type="arc" signature="arc(explicit arc,abscissa,abscissa)"><code></asyxml>*/
+arc arc(explicit arc a, abscissa x1, abscissa x2)
+{/*<asyxml></code><documentation>Return the arc from 'point(a, x1)' to 'point(a, x2)' traversed in the direction of the arc direction.</documentation></function></asyxml>*/
+ real a1 = angabscissa(a.el, point(a, x1), a.polarconicroutine).x;
+ real a2 = angabscissa(a.el, point(a, x2), a.polarconicroutine).x;
+ return arc(a.el, a1, a2, a.polarconicroutine, a.direction);
+}
+
+/*<asyxml><function type="arc" signature="arc(explicit arc,point,point)"><code></asyxml>*/
+arc arc(explicit arc a, point M, point N)
+{/*<asyxml></code><documentation>Return the arc from 'M' to 'N'.
+ The points 'M' and 'N' must belong to the arc 'a'.</documentation></function></asyxml>*/
+ return arc(a, relabscissa(a, M), relabscissa(a, N));
+}
+
+/*<asyxml><function type="arc" signature="inverse(real,point,segment)"><code></asyxml>*/
+arc inverse(real k, point A, segment s)
+{/*<asyxml></code><documentation>Return the inverse arc circle of 's'
+ with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
+ point Ap = inverse(k, A, s.A), Bp = inverse(k, A, s.B),
+ M = inverse(k, A, midpoint(s));
+ return arccircle(Ap, M, Bp);
+}
+
+/*<asyxml><operator type = "arc" signature="*(inversion,segment)"><code></asyxml>*/
+arc operator *(inversion i, segment s)
+{/*<asyxml></code><documentation>Provide
+ inversion * segment.</documentation></operator></asyxml>*/
+ return inverse(i.k, i.C, s);
+}
+
+/*<asyxml><operator type = "path" signature="*(inversion,triangle)"><code></asyxml>*/
+path operator *(inversion i, triangle t)
+{/*<asyxml></code><documentation>Provide inversion * triangle.</documentation></operator></asyxml>*/
+ return (path)(i * segment(t.AB))--
+ (path)(i * segment(t.BC))--
+ (path)(i * segment(t.CA))&cycle;
+}
+
+/*<asyxml><function type="path" signature="compassmark(pair,pair,real,real)"><code></asyxml>*/
+path compassmark(pair O, pair A, real position, real angle = 10)
+{/*<asyxml></code><documentation>Return an arc centered on O with the angle 'angle' so that the position
+ of 'A' on this arc makes an angle 'position * angle'.</documentation></function></asyxml>*/
+ real a = degrees(A - O);
+ real pa = (a - position * angle)%360,
+ pb = (a - (position - 1) * angle)%360;
+ real t1 = intersect(unitcircle, (0, 0)--2 * dir(pa))[0];
+ real t2 = intersect(unitcircle, (0, 0)--2 * dir(pb))[0];
+ int n = length(unitcircle);
+ if(t1 >= t2) t1 -= n;
+ return shift(O) * scale(abs(O - A)) * subpath(unitcircle, t1, t2);
+}
+
+/*<asyxml><function type="line" signature="tangent(explicit arc,abscissa)"><code></asyxml>*/
+line tangent(explicit arc a, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'a' at 'point(a, x)'.</documentation></function></asyxml>*/
+ abscissa ag = angabscissa(a, point(a, x));
+ return tangent(a.el, ag + a.angle1 + (a.el.e == 0 ? a.angle0 : 0));
+}
+
+/*<asyxml><function type="line" signature="tangent(explicit arc,point)"><code></asyxml>*/
+line tangent(explicit arc a, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'a' at 'M'.
+ The points 'M' must belong to the arc 'a'.</documentation></function></asyxml>*/
+ return tangent(a, angabscissa(a, M));
+}
+
+// *=======================================================*
+// *.......Routines for compatibility with original geometry module........*
+
+path square(pair z1, pair z2)
+{
+ pair v = z2 - z1;
+ pair z3 = z2 + I * v;
+ pair z4 = z3 - v;
+ return z1--z2--z3--z4--cycle;
+}
+
+// Draw a perpendicular symbol at z aligned in the direction align
+// relative to the path z--z + dir.
+void perpendicular(picture pic = currentpicture, pair z, pair align,
+ pair dir = E, real size = 0, pen p = currentpen,
+ margin margin = NoMargin, filltype filltype = NoFill)
+{
+ perpendicularmark(pic, (point) z, align, dir, size, p, margin, filltype);
+}
+
+
+// Draw a perpendicular symbol at z aligned in the direction align
+// relative to the path z--z + dir(g, 0)
+void perpendicular(picture pic = currentpicture, pair z, pair align, path g,
+ real size = 0, pen p = currentpen, margin margin = NoMargin,
+ filltype filltype = NoFill)
+{
+ perpendicularmark(pic, (point) z, align, dir(g, 0), size, p, margin, filltype);
+}
+
+// Return an interior arc BAC of triangle ABC, given a radius r > 0.
+// If r < 0, return the corresponding exterior arc of radius |r|.
+path arc(explicit pair B, explicit pair A, explicit pair C, real r)
+{
+ return arc(A, r, degrees(B - A), degrees(C - A));
+}
+
+// *.......End of compatibility routines........*
+// *=======================================================*
+
+// *........................FOOTER.........................*
+// *=======================================================*
diff --git a/Master/texmf-dist/asymptote/graph.asy b/Master/texmf-dist/asymptote/graph.asy
new file mode 100644
index 00000000000..cbcea4aeae3
--- /dev/null
+++ b/Master/texmf-dist/asymptote/graph.asy
@@ -0,0 +1,2237 @@
+private import math;
+import graph_splinetype;
+import graph_settings;
+
+scaleT Linear;
+
+scaleT Log=scaleT(log10,pow10,logarithmic=true);
+scaleT Logarithmic=Log;
+
+string baselinetemplate="$10^4$";
+
+// A linear scale, with optional autoscaling of minimum and maximum values,
+// scaling factor s and intercept.
+scaleT Linear(bool automin=false, bool automax=automin, real s=1,
+ real intercept=0)
+{
+ real sinv=1/s;
+ scalefcn T,Tinv;
+ if(s == 1 && intercept == 0)
+ T=Tinv=identity;
+ else {
+ T=new real(real x) {return (x-intercept)*s;};
+ Tinv=new real(real x) {return x*sinv+intercept;};
+ }
+ return scaleT(T,Tinv,logarithmic=false,automin,automax);
+}
+
+// A logarithmic scale, with optional autoscaling of minimum and maximum
+// values.
+scaleT Log(bool automin=false, bool automax=automin)
+{
+ return scaleT(Log.T,Log.Tinv,logarithmic=true,automin,automax);
+}
+
+// A "broken" linear axis omitting the segment [a,b].
+scaleT Broken(real a, real b, bool automin=false, bool automax=automin)
+{
+ real skip=b-a;
+ real T(real x) {
+ if(x <= a) return x;
+ if(x <= b) return a;
+ return x-skip;
+ }
+ real Tinv(real x) {
+ if(x <= a) return x;
+ return x+skip;
+ }
+ return scaleT(T,Tinv,logarithmic=false,automin,automax);
+}
+
+// A "broken" logarithmic axis omitting the segment [a,b], where a and b are
+// automatically rounded to the nearest integral power of the base.
+scaleT BrokenLog(real a, real b, bool automin=false, bool automax=automin)
+{
+ real A=round(Log.T(a));
+ real B=round(Log.T(b));
+ a=Log.Tinv(A);
+ b=Log.Tinv(B);
+ real skip=B-A;
+ real T(real x) {
+ if(x <= a) return Log.T(x);
+ if(x <= b) return A;
+ return Log.T(x)-skip;
+ }
+ real Tinv(real x) {
+ real X=Log.Tinv(x);
+ if(X <= a) return X;
+ return Log.Tinv(x+skip);
+ }
+ return scaleT(T,Tinv,logarithmic=true,automin,automax);
+}
+
+Label Break=Label("$\approx$",UnFill(0.2mm));
+
+void scale(picture pic=currentpicture, scaleT x, scaleT y=x, scaleT z=y)
+{
+ pic.scale.x.scale=x;
+ pic.scale.y.scale=y;
+ pic.scale.z.scale=z;
+ pic.scale.x.automin=x.automin;
+ pic.scale.y.automin=y.automin;
+ pic.scale.z.automin=z.automin;
+ pic.scale.x.automax=x.automax;
+ pic.scale.y.automax=y.automax;
+ pic.scale.z.automax=z.automax;
+}
+
+void scale(picture pic=currentpicture, bool xautoscale=false,
+ bool yautoscale=xautoscale, bool zautoscale=yautoscale)
+{
+ scale(pic,Linear(xautoscale,xautoscale),Linear(yautoscale,yautoscale),
+ Linear(zautoscale,zautoscale));
+}
+
+struct scientific
+{
+ int sign;
+ real mantissa;
+ int exponent;
+ int ceil() {return sign*ceil(mantissa);}
+ real scale(real x, real exp) {
+ static real max=0.1*realMax;
+ static real limit=-log10(max);
+ return x*(exp > limit ? 10^-exp : max);
+ }
+ real ceil(real x, real exp) {return ceil(sign*scale(abs(x),exp));}
+ real floor(real x, real exp) {return floor(sign*scale(abs(x),exp));}
+}
+
+// Convert x to scientific notation
+scientific scientific(real x)
+{
+ scientific s;
+ s.sign=sgn(x);
+ x=abs(x);
+ if(x == 0) {s.mantissa=0; s.exponent=-intMax; return s;}
+ real logx=log10(x);
+ s.exponent=floor(logx);
+ s.mantissa=s.scale(x,s.exponent);
+ return s;
+}
+
+// Autoscale limits and tick divisor.
+struct bounds {
+ real min;
+ real max;
+
+ // Possible tick intervals:
+ int[] divisor;
+
+ void operator init(real min, real max, int[] divisor=new int[]) {
+ this.min=min;
+ this.max=max;
+ this.divisor=divisor;
+ }
+}
+
+// Compute tick divisors.
+int[] divisors(int a, int b)
+{
+ int[] dlist;
+ int n=b-a;
+ dlist[0]=1;
+ if(n == 1) {dlist[1]=10; dlist[2]=100; return dlist;}
+ if(n == 2) {dlist[1]=2; return dlist;}
+ int sqrtn=floor(sqrt(n));
+ int i=0;
+ for(int d=2; d <= sqrtn; ++d)
+ if(n % d == 0 && (a*b >= 0 || b % (n/d) == 0)) dlist[++i]=d;
+ for(int d=sqrtn; d >= 1; --d)
+ if(n % d == 0 && (a*b >= 0 || b % d == 0)) dlist[++i]=quotient(n,d);
+ return dlist;
+}
+
+real upscale(real b, real a)
+{
+ if(b <= 5) b=5;
+ else if (b > 10 && a >= 0 && b <= 12) b=12;
+ else if (b > 10 && (a >= 0 || 15 % -a == 0) && b <= 15) b=15;
+ else b=ceil(b/10)*10;
+ return b;
+}
+
+// Compute autoscale limits and tick divisor.
+bounds autoscale(real Min, real Max, scaleT scale=Linear)
+{
+ bounds m;
+ if(scale.logarithmic) {
+ m.min=floor(Min);
+ m.max=ceil(Max);
+ return m;
+ }
+ if(!(finite(Min) && finite(Max)))
+ abort("autoscale requires finite limits");
+ Min=scale.Tinv(Min);
+ Max=scale.Tinv(Max);
+ m.min=Min;
+ m.max=Max;
+ if(Min > Max) {real temp=Min; Min=Max; Max=temp;}
+ if(Min == Max) {
+ if(Min == 0) {m.max=1; return m;}
+ if(Min > 0) {Min=0; Max *= 2;}
+ else {Min *= 2; Max=0;}
+ }
+
+ int sign;
+ if(Min < 0 && Max <= 0) {real temp=-Min; Min=-Max; Max=temp; sign=-1;}
+ else sign=1;
+ scientific sa=scientific(Min);
+ scientific sb=scientific(Max);
+ int exp=max(sa.exponent,sb.exponent);
+ real a=sa.floor(Min,exp);
+ real b=sb.ceil(Max,exp);
+
+ void zoom() {
+ --exp;
+ a=sa.floor(Min,exp);
+ b=sb.ceil(Max,exp);
+ }
+
+ if(sb.mantissa <= 1.5)
+ zoom();
+
+ while((b-a)*10.0^exp > 10*(Max-Min))
+ zoom();
+
+ real bsave=b;
+ if(b-a > (a >= 0 ? 8 : 6)) {
+ b=upscale(b,a);
+ if(a >= 0) {
+ if(a <= 5) a=0; else a=floor(a/10)*10;
+ } else a=-upscale(-a,-1);
+ }
+
+ // Redo b in case the value of a has changed
+ if(bsave-a > (a >= 0 ? 8 : 6))
+ b=upscale(bsave,a);
+
+ if(sign == -1) {real temp=-a; a=-b; b=temp;}
+ real Scale=10.0^exp;
+ m.min=scale.T(a*Scale);
+ m.max=scale.T(b*Scale);
+ if(m.min > m.max) {real temp=m.min; m.min=m.max; m.max=temp;}
+ m.divisor=divisors(round(a),round(b));
+ return m;
+}
+
+typedef string ticklabel(real);
+
+ticklabel Format(string s=defaultformat)
+{
+ return new string(real x) {return format(s,x);};
+}
+
+ticklabel OmitFormat(string s=defaultformat ... real[] x)
+{
+ return new string(real v) {
+ int i=find(x == v);
+ return i < 0 ? format(s,v) : "";
+ };
+}
+
+string trailingzero="$%#$";
+string signedtrailingzero="$%+#$";
+
+ticklabel DefaultFormat=Format();
+ticklabel NoZeroFormat=OmitFormat(0);
+
+// Format tick values as integral powers of base; otherwise with DefaultFormat.
+ticklabel DefaultLogFormat(int base) {
+ return new string(real x) {
+ string exponent=format("%.4f",log(x)/log(base));
+ return find(exponent,".") == -1 ?
+ "$"+(string) base+"^{"+exponent+"}$" : format(x);
+ };
+}
+
+// Format all tick values as powers of base.
+ticklabel LogFormat(int base) {
+ return new string(real x) {
+ return format("$"+(string) base+"^{%g}$",log(x)/log(base));
+ };
+}
+
+ticklabel LogFormat=LogFormat(10);
+ticklabel DefaultLogFormat=DefaultLogFormat(10);
+
+// The default direction specifier.
+pair zero(real) {return 0;}
+
+struct ticklocate {
+ real a,b; // Tick values at point(g,0), point(g,length(g)).
+ autoscaleT S; // Autoscaling transformation.
+ pair dir(real t); // Absolute 2D tick direction.
+ triple dir3(real t); // Absolute 3D tick direction.
+ real time(real v); // Returns the time corresponding to the value v.
+ ticklocate copy() {
+ ticklocate T=new ticklocate;
+ T.a=a;
+ T.b=b;
+ T.S=S.copy();
+ T.dir=dir;
+ T.dir3=dir3;
+ T.time=time;
+ return T;
+ }
+}
+
+autoscaleT defaultS;
+
+typedef real valuetime(real);
+
+valuetime linear(scalefcn S=identity, real Min, real Max)
+{
+ real factor=Max == Min ? 0.0 : 1.0/(Max-Min);
+ return new real(real v) {return (S(v)-Min)*factor;};
+}
+
+ticklocate ticklocate(real a, real b, autoscaleT S=defaultS,
+ real tickmin=-infinity, real tickmax=infinity,
+ real time(real)=null, pair dir(real)=zero)
+{
+ if((valuetime) time == null) time=linear(S.T(),a,b);
+ ticklocate locate;
+ locate.a=a;
+ locate.b=b;
+ locate.S=S.copy();
+ if(finite(tickmin)) locate.S.tickMin=tickmin;
+ if(finite(tickmax)) locate.S.tickMax=tickmax;
+ locate.time=time;
+ locate.dir=dir;
+ return locate;
+}
+
+private struct locateT {
+ real t; // tick location time
+ pair Z; // tick location in frame coordinates
+ pair pathdir; // path direction in frame coordinates
+ pair dir; // tick direction in frame coordinates
+
+ void dir(transform T, path g, ticklocate locate, real t) {
+ pathdir=unit(shiftless(T)*dir(g,t));
+ pair Dir=locate.dir(t);
+ dir=Dir == 0 ? -I*pathdir : unit(Dir);
+ }
+ // Locate the desired position of a tick along a path.
+ void calc(transform T, path g, ticklocate locate, real val) {
+ t=locate.time(val);
+ Z=T*point(g,t);
+ dir(T,g,locate,t);
+ }
+}
+
+pair ticklabelshift(pair align, pen p=currentpen)
+{
+ return 0.25*unit(align)*labelmargin(p);
+}
+
+void drawtick(frame f, transform T, path g, path g2, ticklocate locate,
+ real val, real Size, int sign, pen p, bool extend)
+{
+ locateT locate1,locate2;
+ locate1.calc(T,g,locate,val);
+ if(extend && size(g2) > 0) {
+ locate2.calc(T,g2,locate,val);
+ draw(f,locate1.Z--locate2.Z,p);
+ } else
+ if(sign == 0)
+ draw(f,locate1.Z-Size*locate1.dir--locate1.Z+Size*locate1.dir,p);
+ else
+ draw(f,locate1.Z--locate1.Z+Size*sign*locate1.dir,p);
+}
+
+real zerotickfuzz=10*epsilon;
+
+// Label a tick on a frame.
+pair labeltick(frame d, transform T, path g, ticklocate locate, real val,
+ pair side, int sign, real Size, ticklabel ticklabel,
+ Label F, real norm=0)
+{
+ locateT locate1;
+ locate1.calc(T,g,locate,val);
+ pair align=side*locate1.dir;
+ pair perp=I*locate1.pathdir;
+
+ // Adjust tick label alignment
+ pair adjust=unit(align+0.75perp*sgn(dot(align,perp)));
+ // Project align onto adjusted direction.
+ align=adjust*dot(align,adjust);
+ pair shift=dot(align,-sign*locate1.dir) <= 0 ? align*Size :
+ ticklabelshift(align,F.p);
+
+ real label;
+ if(locate.S.scale.logarithmic)
+ label=locate.S.scale.Tinv(val);
+ else {
+ label=val;
+ if(abs(label) < zerotickfuzz*norm) label=0;
+ // Fix epsilon errors at +/-1e-4
+ // default format changes to scientific notation here
+ if(abs(abs(label)-1e-4) < epsilon) label=sgn(label)*1e-4;
+ }
+
+ string s=ticklabel(label);
+ if(s != "")
+ label(d,F.T*baseline(s,baselinetemplate),locate1.Z+shift,align,F.p,
+ F.filltype);
+ return locate1.pathdir;
+}
+
+// Add axis label L to frame f.
+void labelaxis(frame f, transform T, Label L, path g,
+ ticklocate locate=null, int sign=1, bool ticklabels=false)
+{
+ Label L0=L.copy();
+ real t=L0.relative(g);
+ pair z=point(g,t);
+ pair dir=dir(g,t);
+ pair perp=I*dir;
+ if(locate != null) {
+ locateT locate1;
+ locate1.dir(T,g,locate,t);
+ L0.align(L0.align,unit(-sgn(dot(sign*locate1.dir,perp))*perp));
+ }
+ pair align=L0.align.dir;
+ if(L0.align.relative) align *= -perp;
+ pair alignperp=dot(align,perp)*perp;
+ pair offset;
+ if(ticklabels) {
+ if(piecewisestraight(g)) {
+ real angle=degrees(dir,warn=false);
+ transform S=rotate(-angle,z);
+ frame F=S*f;
+ pair Align=rotate(-angle)*alignperp;
+ offset=unit(alignperp-sign*locate.dir(t))*
+ abs((Align.y >= 0 ? max(F).y : (Align.y < 0 ? min(F).y : 0))-z.y);
+ }
+ z += offset;
+ }
+
+ L0.align(align);
+ L0.position(z);
+ frame d;
+ add(d,L0);
+ pair width=0.5*size(d);
+ int n=length(g);
+ real t=L.relative();
+ pair s=realmult(width,dir(g,t));
+ if(t <= 0) {
+ if(L.align.default) s *= -axislabelfactor;
+ d=shift(s)*d;
+ } else if(t >= n) {
+ if(L.align.default) s *= -axislabelfactor;
+ d=shift(-s)*d;
+ } else if(offset == 0 && L.align.default) {
+ pair s=realmult(width,I*dir(g,t));
+ s=axislabelfactor*s;
+ d=shift(s)*d;
+ }
+ add(f,d);
+}
+
+// Check the tick coverage of a linear axis.
+bool axiscoverage(int N, transform T, path g, ticklocate locate, real Step,
+ pair side, int sign, real Size, Label F, ticklabel ticklabel,
+ real norm, real limit)
+{
+ real coverage=0;
+ bool loop=cyclic(g);
+ real a=locate.S.Tinv(locate.a);
+ real b=locate.S.Tinv(locate.b);
+ real tickmin=finite(locate.S.tickMin) ? locate.S.Tinv(locate.S.tickMin) : a;
+ if(Size > 0) {
+ int count=0;
+ if(loop) count=N+1;
+ else {
+ for(int i=0; i <= N; ++i) {
+ real val=tickmin+i*Step;
+ if(val >= a && val <= b)
+ ++count;
+ }
+ }
+ if(count > 0) limit /= count;
+ for(int i=0; i <= N; ++i) {
+ real val=tickmin+i*Step;
+ if(loop || (val >= a && val <= b)) {
+ frame d;
+ pair dir=labeltick(d,T,g,locate,val,side,sign,Size,ticklabel,F,norm);
+ if(abs(dot(size(d),dir)) > limit) return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Check the tick coverage of a logarithmic axis.
+bool logaxiscoverage(int N, transform T, path g, ticklocate locate, pair side,
+ int sign, real Size, Label F, ticklabel ticklabel,
+ real limit, int first, int last)
+{
+ bool loop=cyclic(g);
+ real coverage=0;
+ real a=locate.a;
+ real b=locate.b;
+ int count=0;
+ for(int i=first-1; i <= last+1; i += N) {
+ if(loop || i >= a && i <= b)
+ ++count;
+ }
+ if(count > 0) limit /= count;
+ for(int i=first-1; i <= last+1; i += N) {
+ if(loop || i >= a && i <= b) {
+ frame d;
+ pair dir=labeltick(d,T,g,locate,i,side,sign,Size,ticklabel,F);
+ if(abs(dot(size(d),dir)) > limit) return false;
+ }
+ }
+ return true;
+}
+
+struct tickvalues {
+ real[] major;
+ real[] minor;
+ int N; // For logarithmic axes: number of decades between tick labels.
+}
+
+// Determine a format that distinguishes adjacent pairs of ticks, optionally
+// adding trailing zeros.
+string autoformat(string format="", real norm ... real[] a)
+{
+ bool trailingzero=(format == trailingzero);
+ bool signedtrailingzero=(format == signedtrailingzero);
+ if(!trailingzero && !signedtrailingzero && format != "") return format;
+
+ real[] A=sort(a);
+ real[] a=abs(A);
+
+ bool signchange=(A.length > 1 && A[0] < 0 && A[A.length-1] >= 0);
+
+ for(int i=0; i < A.length; ++i)
+ if(a[i] < zerotickfuzz*norm) A[i]=a[i]=0;
+
+ int n=0;
+
+ bool Fixed=find(a >= 1e4-epsilon | (a > 0 & a <= 1e-4-epsilon)) < 0;
+
+ string Format=defaultformat(4,fixed=Fixed);
+
+ if(Fixed && n < 4) {
+ for(int i=0; i < A.length; ++i) {
+ real a=A[i];
+ while(format(defaultformat(n,fixed=Fixed),a) != format(Format,a))
+ ++n;
+ }
+ }
+
+ string trailing=trailingzero ? (signchange ? "# " : "#") :
+ signedtrailingzero ? "#+" : "";
+
+ string format=defaultformat(n,trailing,Fixed);
+
+ for(int i=0; i < A.length-1; ++i) {
+ real a=A[i];
+ real b=A[i+1];
+ // Check if an extra digit of precision should be added.
+ string fixedformat="%#."+string(n+1)+"f";
+ string A=format(fixedformat,a);
+ string B=format(fixedformat,b);
+ if(substr(A,length(A)-1,1) != "0" || substr(B,length(B)-1,1) != "0") {
+ a *= 0.1;
+ b *= 0.1;
+ }
+ if(a != b) {
+ while(format(format,a) == format(format,b))
+ format=defaultformat(++n,trailing,Fixed);
+ }
+ }
+
+ if(n == 0) return defaultformat;
+ return format;
+}
+
+// Automatic tick generation routine.
+tickvalues generateticks(int sign, Label F="", ticklabel ticklabel=null,
+ int N, int n=0, real Step=0, real step=0,
+ real Size=0, real size=0,
+ transform T, pair side, path g, real limit,
+ pen p, ticklocate locate, int[] divisor,
+ bool opposite)
+{
+ tickvalues tickvalues;
+ sign=opposite ? -sign : sign;
+ if(Size == 0) Size=Ticksize;
+ if(size == 0) size=ticksize;
+ F=F.copy();
+ F.p(p);
+
+ if(F.align.dir != 0) side=F.align.dir;
+ else if(side == 0) side=((sign == 1) ? left : right);
+
+ bool ticklabels=false;
+ path G=T*g;
+
+ if(!locate.S.scale.logarithmic) {
+ real a=locate.S.Tinv(locate.a);
+ real b=locate.S.Tinv(locate.b);
+ real norm=max(abs(a),abs(b));
+ string format=autoformat(F.s,norm,a,b);
+ if(F.s == "%") F.s="";
+ if(ticklabel == null) ticklabel=Format(format);
+
+ if(a > b) {real temp=a; a=b; b=temp;}
+
+ if(b-a < 100.0*epsilon*norm) b=a;
+
+ bool autotick=Step == 0 && N == 0;
+
+ real tickmin=finite(locate.S.tickMin) && (autotick || locate.S.automin) ?
+ locate.S.Tinv(locate.S.tickMin) : a;
+ real tickmax=finite(locate.S.tickMax) && (autotick || locate.S.automax) ?
+ locate.S.Tinv(locate.S.tickMax) : b;
+ if(tickmin > tickmax) {real temp=tickmin; tickmin=tickmax; tickmax=temp;}
+
+ real inStep=Step;
+
+ bool calcStep=true;
+ real len=tickmax-tickmin;
+ if(autotick) {
+ N=1;
+ if(divisor.length > 0) {
+ bool autoscale=locate.S.automin && locate.S.automax;
+ real h=0.5*(b-a);
+ if(h > 0) {
+ for(int d=divisor.length-1; d >= 0; --d) {
+ int N0=divisor[d];
+ Step=len/N0;
+ int N1=N0;
+ int m=2;
+ while(Step > h) {
+ N0=m*N1;
+ Step=len/N0;
+ m *= 2;
+ }
+ if(axiscoverage(N0,T,g,locate,Step,side,sign,Size,F,ticklabel,norm,
+ limit)) {
+ N=N0;
+ if(N0 == 1 && !autoscale && d < divisor.length-1) {
+ // Try using 2 ticks (otherwise 1);
+ int div=divisor[d+1];
+ Step=quotient(div,2)*len/div;
+ calcStep=false;
+ if(axiscoverage(2,T,g,locate,Step,side,sign,Size,F,ticklabel,
+ norm,limit)) N=2;
+ else Step=len;
+ }
+ // Found a good divisor; now compute subtick divisor
+ if(n == 0) {
+ if(step != 0) n=ceil(Step/step);
+ else {
+ n=quotient(divisor[divisor.length-1],N);
+ if(N == 1) n=(a*b >= 0) ? 2 : 1;
+ if(n == 1) n=2;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if(inStep != 0 && !locate.S.automin) {
+ tickmin=floor(tickmin/Step)*Step;
+ len=tickmax-tickmin;
+ }
+
+ if(calcStep) {
+ if(N == 0) N=(int) (len/Step);
+ else Step=len/N;
+ }
+
+ if(n == 0) {
+ if(step != 0) n=ceil(Step/step);
+ } else step=Step/n;
+
+ b += epsilon*norm;
+
+ if(Size > 0) {
+ for(int i=0; i <= N; ++i) {
+ real val=tickmin+i*Step;
+ if(val >= a && val <= b)
+ tickvalues.major.push(val);
+ if(size > 0 && step > 0) {
+ real iStep=i*Step;
+ real jstop=(len-iStep)/step;
+ for(int j=1; j < n && j <= jstop; ++j) {
+ real val=tickmin+iStep+j*step;
+ if(val >= a && val <= b)
+ tickvalues.minor.push(val);
+ }
+ }
+ }
+ }
+
+ } else { // Logarithmic
+ string format=F.s;
+ if(F.s == "%") F.s="";
+
+ int base=round(locate.S.scale.Tinv(1));
+
+ if(ticklabel == null)
+ ticklabel=format == "%" ? Format("") : DefaultLogFormat(base);
+ real a=locate.S.postscale.Tinv(locate.a);
+ real b=locate.S.postscale.Tinv(locate.b);
+ if(a > b) {real temp=a; a=b; b=temp;}
+
+ int first=floor(a-epsilon);
+ int last=ceil(b+epsilon);
+
+ if(N == 0) {
+ N=1;
+ while(N <= last-first) {
+ if(logaxiscoverage(N,T,g,locate,side,sign,Size,F,ticklabel,limit,
+ first,last)) break;
+ ++N;
+ }
+ }
+
+ if(N <= 2 && n == 0) n=base;
+ tickvalues.N=N;
+
+ if(N > 0) {
+ for(int i=first-1; i <= last+1; ++i) {
+ if(i >= a && i <= b)
+ tickvalues.major.push(locate.S.scale.Tinv(i));
+ if(n > 0) {
+ for(int j=2; j < n; ++j) {
+ real val=(i+1+locate.S.scale.T(j/n));
+ if(val >= a && val <= b)
+ tickvalues.minor.push(locate.S.scale.Tinv(val));
+ }
+ }
+ }
+ }
+ }
+ return tickvalues;
+}
+
+// Signature of routines that draw labelled paths with ticks and tick labels.
+typedef void ticks(frame, transform, Label, pair, path, path, pen,
+ arrowbar, margin, ticklocate, int[], bool opposite=false);
+
+// Tick construction routine for a user-specified array of tick values.
+ticks Ticks(int sign, Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks=new real[], real[] ticks=new real[], int N=1,
+ bool begin=true, bool end=true,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return new void(frame f, transform t, Label L, pair side, path g, path g2,
+ pen p, arrowbar arrow, margin margin, ticklocate locate,
+ int[] divisor, bool opposite) {
+ // Use local copy of context variables:
+ int sign=opposite ? -sign : sign;
+ pen pTick=pTick;
+ pen ptick=ptick;
+ ticklabel ticklabel=ticklabel;
+
+ real Size=Size;
+ real size=size;
+ if(Size == 0) Size=Ticksize;
+ if(size == 0) size=ticksize;
+
+ Label L=L.copy();
+ Label F=F.copy();
+ L.p(p);
+ F.p(p);
+ if(pTick == nullpen) pTick=p;
+ if(ptick == nullpen) ptick=pTick;
+
+ if(F.align.dir != 0) side=F.align.dir;
+ else if(side == 0) side=F.T*((sign == 1) ? left : right);
+
+ bool ticklabels=false;
+ path G=t*g;
+ path G2=t*g2;
+
+ scalefcn T;
+
+ real a,b;
+ if(locate.S.scale.logarithmic) {
+ a=locate.S.postscale.Tinv(locate.a);
+ b=locate.S.postscale.Tinv(locate.b);
+ T=locate.S.scale.T;
+ } else {
+ a=locate.S.Tinv(locate.a);
+ b=locate.S.Tinv(locate.b);
+ T=identity;
+ }
+
+ if(a > b) {real temp=a; a=b; b=temp;}
+
+ real norm=max(abs(a),abs(b));
+
+ string format=autoformat(F.s,norm...Ticks);
+ if(F.s == "%") F.s="";
+ if(ticklabel == null) {
+ if(locate.S.scale.logarithmic) {
+ int base=round(locate.S.scale.Tinv(1));
+ ticklabel=format == "%" ? Format("") : DefaultLogFormat(base);
+ } else ticklabel=Format(format);
+ }
+
+ begingroup(f);
+ if(opposite) draw(f,G,p);
+ else draw(f,margin(G,p).g,p,arrow);
+ for(int i=(begin ? 0 : 1); i < (end ? Ticks.length : Ticks.length-1); ++i) {
+ real val=T(Ticks[i]);
+ if(val >= a && val <= b)
+ drawtick(f,t,g,g2,locate,val,Size,sign,pTick,extend);
+ }
+ for(int i=0; i < ticks.length; ++i) {
+ real val=T(ticks[i]);
+ if(val >= a && val <= b)
+ drawtick(f,t,g,g2,locate,val,size,sign,ptick,extend);
+ }
+ endgroup(f);
+
+ if(N == 0) N=1;
+ if(Size > 0 && !opposite) {
+ for(int i=(beginlabel ? 0 : 1);
+ i < (endlabel ? Ticks.length : Ticks.length-1); i += N) {
+ real val=T(Ticks[i]);
+ if(val >= a && val <= b) {
+ ticklabels=true;
+ labeltick(f,t,g,locate,val,side,sign,Size,ticklabel,F,norm);
+ }
+ }
+ }
+ if(L.s != "" && !opposite)
+ labelaxis(f,t,L,G,locate,sign,ticklabels);
+ };
+}
+
+// Optional routine to allow modification of auto-generated tick values.
+typedef tickvalues tickmodifier(tickvalues);
+tickvalues None(tickvalues v) {return v;}
+
+// Tickmodifier that removes all ticks in the intervals [a[i],b[i]].
+tickmodifier OmitTickIntervals(real[] a, real[] b) {
+ return new tickvalues(tickvalues v) {
+ if(a.length != b.length) abort(differentlengths);
+ void omit(real[] A) {
+ if(A.length != 0) {
+ real norm=max(abs(A));
+ for(int i=0; i < a.length; ++i) {
+ int j;
+ while((j=find(A > a[i]-zerotickfuzz*norm
+ & A < b[i]+zerotickfuzz*norm)) >= 0) {
+ A.delete(j);
+ }
+ }
+ }
+ }
+ omit(v.major);
+ omit(v.minor);
+ return v;
+ };
+}
+
+// Tickmodifier that removes all ticks in the interval [a,b].
+tickmodifier OmitTickInterval(real a, real b) {
+ return OmitTickIntervals(new real[] {a}, new real[] {b});
+}
+
+// Tickmodifier that removes the specified ticks.
+tickmodifier OmitTick(... real[] x) {
+ return OmitTickIntervals(x,x);
+}
+
+tickmodifier NoZero=OmitTick(0);
+
+tickmodifier Break(real, real)=OmitTickInterval;
+
+// Automatic tick construction routine.
+ticks Ticks(int sign, Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return new void(frame f, transform T, Label L, pair side, path g, path g2,
+ pen p, arrowbar arrow, margin margin, ticklocate locate,
+ int[] divisor, bool opposite) {
+ real limit=Step == 0 ? axiscoverage*arclength(T*g) : 0;
+ tickvalues values=modify(generateticks(sign,F,ticklabel,N,n,Step,step,
+ Size,size,T,side,g,
+ limit,p,locate,divisor,opposite));
+
+ Ticks(sign,F,ticklabel,beginlabel,endlabel,values.major,values.minor,
+ values.N,begin,end,Size,size,extend,pTick,ptick)
+ (f,T,L,side,g,g2,p,arrow,margin,locate,divisor,opposite);
+ };
+}
+
+ticks NoTicks()
+{
+ return new void(frame f, transform T, Label L, pair, path g, path, pen p,
+ arrowbar arrow, margin margin, ticklocate,
+ int[], bool opposite) {
+ path G=T*g;
+ if(opposite) draw(f,G,p);
+ else {
+ draw(f,margin(G,p).g,p,arrow);
+ if(L.s != "") {
+ Label L=L.copy();
+ L.p(p);
+ labelaxis(f,T,L,G);
+ }
+ }
+ };
+}
+
+ticks LeftTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks(-1,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,modify,Size,size,extend,pTick,ptick);
+}
+
+ticks RightTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks(1,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,modify,Size,size,extend,pTick,ptick);
+}
+
+ticks Ticks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks(0,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,modify,Size,size,extend,pTick,ptick);
+}
+
+ticks LeftTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks, real[] ticks=new real[],
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks(-1,format,ticklabel,beginlabel,endlabel,
+ Ticks,ticks,Size,size,extend,pTick,ptick);
+}
+
+ticks RightTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks, real[] ticks=new real[],
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks(1,format,ticklabel,beginlabel,endlabel,
+ Ticks,ticks,Size,size,extend,pTick,ptick);
+}
+
+ticks Ticks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks, real[] ticks=new real[],
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks(0,format,ticklabel,beginlabel,endlabel,
+ Ticks,ticks,Size,size,extend,pTick,ptick);
+}
+
+ticks NoTicks=NoTicks(),
+LeftTicks=LeftTicks(),
+RightTicks=RightTicks(),
+Ticks=Ticks();
+
+pair tickMin(picture pic)
+{
+ return minbound(pic.userMin(),(pic.scale.x.tickMin,pic.scale.y.tickMin));
+}
+
+pair tickMax(picture pic)
+{
+ return maxbound(pic.userMax(),(pic.scale.x.tickMax,pic.scale.y.tickMax));
+}
+
+int Min=-1;
+int Value=0;
+int Max=1;
+int Both=2;
+
+// Structure used to communicate axis and autoscale settings to tick routines.
+struct axisT {
+ int type; // -1 = min, 0 = given value, 1 = max, 2 = min/max
+ int type2; // for 3D axis
+ real value;
+ real value2;
+ pair side; // 2D tick label direction relative to path (left or right)
+ real position; // label position along axis
+ align align; // default axis label alignment and 3D tick label direction
+ int[] xdivisor;
+ int[] ydivisor;
+ int[] zdivisor;
+ bool extend; // extend axis to graph boundary?
+};
+
+axisT axis;
+typedef void axis(picture, axisT);
+
+axis Bottom(bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Min;
+ axis.position=0.5;
+ axis.side=right;
+ axis.align=S;
+ axis.extend=extend;
+ };
+}
+
+axis Top(bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Max;
+ axis.position=0.5;
+ axis.side=left;
+ axis.align=N;
+ axis.extend=extend;
+ };
+}
+
+axis BottomTop(bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Both;
+ axis.position=0.5;
+ axis.side=right;
+ axis.align=S;
+ axis.extend=extend;
+ };
+}
+
+axis Left(bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Min;
+ axis.position=0.5;
+ axis.side=left;
+ axis.align=W;
+ axis.extend=extend;
+ };
+}
+
+axis Right(bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Max;
+ axis.position=0.5;
+ axis.side=right;
+ axis.align=E;
+ axis.extend=extend;
+ };
+}
+
+axis LeftRight(bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Both;
+ axis.position=0.5;
+ axis.side=left;
+ axis.align=W;
+ axis.extend=extend;
+ };
+}
+
+axis XEquals(real x, bool extend=true)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.value=pic.scale.x.T(x);
+ axis.position=1;
+ axis.side=left;
+ axis.align=W;
+ axis.extend=extend;
+ };
+}
+
+axis YEquals(real y, bool extend=true)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.value=pic.scale.y.T(y);
+ axis.position=1;
+ axis.side=right;
+ axis.align=S;
+ axis.extend=extend;
+ };
+}
+
+axis XZero(bool extend=true)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.value=pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0);
+ axis.position=1;
+ axis.side=left;
+ axis.align=W;
+ axis.extend=extend;
+ };
+}
+
+axis YZero(bool extend=true)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.value=pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0);
+ axis.position=1;
+ axis.side=right;
+ axis.align=S;
+ axis.extend=extend;
+ };
+}
+
+axis Bottom=Bottom(),
+Top=Top(),
+BottomTop=BottomTop(),
+Left=Left(),
+Right=Right(),
+LeftRight=LeftRight(),
+XZero=XZero(),
+YZero=YZero();
+
+// Draw a general axis.
+void axis(picture pic=currentpicture, Label L="", path g, path g2=nullpath,
+ pen p=currentpen, ticks ticks, ticklocate locate,
+ arrowbar arrow=None, margin margin=NoMargin,
+ int[] divisor=new int[], bool above=false, bool opposite=false)
+{
+ Label L=L.copy();
+ real t=reltime(g,0.5);
+ if(L.defaultposition) L.position(t);
+ divisor=copy(divisor);
+ locate=locate.copy();
+ pic.add(new void (frame f, transform t, transform T, pair lb, pair rt) {
+ frame d;
+ ticks(d,t,L,0,g,g2,p,arrow,margin,locate,divisor,opposite);
+ (above ? add : prepend)(f,t*T*inverse(t)*d);
+ });
+
+ pic.addPath(g,p);
+
+ if(L.s != "") {
+ frame f;
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ pair pos=point(g,L.relative()*length(g));
+ pic.addBox(pos,pos,min(f),max(f));
+ }
+}
+
+real xtrans(transform t, real x)
+{
+ return (t*(x,0)).x;
+}
+
+real ytrans(transform t, real y)
+{
+ return (t*(0,y)).y;
+}
+
+// An internal routine to draw an x axis at a particular y value.
+void xaxisAt(picture pic=currentpicture, Label L="", axis axis,
+ real xmin=-infinity, real xmax=infinity, pen p=currentpen,
+ ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin,
+ bool above=true, bool opposite=false)
+{
+ real y=axis.value;
+ real y2;
+ Label L=L.copy();
+ int[] divisor=copy(axis.xdivisor);
+ pair side=axis.side;
+ int type=axis.type;
+
+ pic.add(new void (frame f, transform t, transform T, pair lb, pair rt) {
+ transform tinv=inverse(t);
+ pair a=xmin == -infinity ? tinv*(lb.x-min(p).x,ytrans(t,y)) : (xmin,y);
+ pair b=xmax == infinity ? tinv*(rt.x-max(p).x,ytrans(t,y)) : (xmax,y);
+ pair a2=xmin == -infinity ? tinv*(lb.x-min(p).x,ytrans(t,y2)) : (xmin,y2);
+ pair b2=xmax == infinity ? tinv*(rt.x-max(p).x,ytrans(t,y2)) : (xmax,y2);
+
+ if(xmin == -infinity || xmax == infinity) {
+ bounds mx=autoscale(a.x,b.x,pic.scale.x.scale);
+ pic.scale.x.tickMin=mx.min;
+ pic.scale.x.tickMax=mx.max;
+ divisor=mx.divisor;
+ }
+
+ real fuzz=epsilon*max(abs(a.x),abs(b.x));
+ a -= (fuzz,0);
+ b += (fuzz,0);
+
+ frame d;
+ ticks(d,t,L,side,a--b,finite(y2) ? a2--b2 : nullpath,p,arrow,margin,
+ ticklocate(a.x,b.x,pic.scale.x),divisor,opposite);
+ (above ? add : prepend)(f,t*T*tinv*d);
+ });
+
+ void bounds() {
+ if(type == Both) {
+ y2=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax().y;
+ y=opposite ? y2 :
+ (pic.scale.y.automin() ? tickMin(pic).y : pic.userMin().y);
+ }
+ else if(type == Min)
+ y=pic.scale.y.automin() ? tickMin(pic).y : pic.userMin().y;
+ else if(type == Max)
+ y=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax().y;
+
+ real Xmin=finite(xmin) ? xmin : pic.userMin().x;
+ real Xmax=finite(xmax) ? xmax : pic.userMax().x;
+
+ pair a=(Xmin,y);
+ pair b=(Xmax,y);
+ pair a2=(Xmin,y2);
+ pair b2=(Xmax,y2);
+
+ if(finite(a)) {
+ pic.addPoint(a,min(p));
+ pic.addPoint(a,max(p));
+ }
+
+ if(finite(b)) {
+ pic.addPoint(b,min(p));
+ pic.addPoint(b,max(p));
+ }
+
+ if(finite(a) && finite(b)) {
+ frame d;
+ ticks(d,pic.scaling(warn=false),L,side,
+ (a.x,0)--(b.x,0),(a2.x,0)--(b2.x,0),p,arrow,margin,
+ ticklocate(a.x,b.x,pic.scale.x),divisor,opposite);
+ frame f;
+ if(L.s != "") {
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ }
+ pair pos=a+L.relative()*(b-a);
+ pic.addBox(pos,pos,(min(f).x,min(d).y),(max(f).x,max(d).y));
+ }
+ }
+
+ // Process any queued y axis bound calculation requests.
+ for(int i=0; i < pic.scale.y.bound.length; ++i)
+ pic.scale.y.bound[i]();
+
+ pic.scale.y.bound.delete();
+
+ bounds();
+
+ // Request another x bounds calculation before final picture scaling.
+ pic.scale.x.bound.push(bounds);
+}
+
+// An internal routine to draw a y axis at a particular x value.
+void yaxisAt(picture pic=currentpicture, Label L="", axis axis,
+ real ymin=-infinity, real ymax=infinity, pen p=currentpen,
+ ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin,
+ bool above=true, bool opposite=false)
+{
+ real x=axis.value;
+ real x2;
+ Label L=L.copy();
+ int[] divisor=copy(axis.ydivisor);
+ pair side=axis.side;
+ int type=axis.type;
+
+ pic.add(new void (frame f, transform t, transform T, pair lb, pair rt) {
+ transform tinv=inverse(t);
+ pair a=ymin == -infinity ? tinv*(xtrans(t,x),lb.y-min(p).y) : (x,ymin);
+ pair b=ymax == infinity ? tinv*(xtrans(t,x),rt.y-max(p).y) : (x,ymax);
+ pair a2=ymin == -infinity ? tinv*(xtrans(t,x2),lb.y-min(p).y) : (x2,ymin);
+ pair b2=ymax == infinity ? tinv*(xtrans(t,x2),rt.y-max(p).y) : (x2,ymax);
+
+ if(ymin == -infinity || ymax == infinity) {
+ bounds my=autoscale(a.y,b.y,pic.scale.y.scale);
+ pic.scale.y.tickMin=my.min;
+ pic.scale.y.tickMax=my.max;
+ divisor=my.divisor;
+ }
+
+ real fuzz=epsilon*max(abs(a.y),abs(b.y));
+ a -= (0,fuzz);
+ b += (0,fuzz);
+
+ frame d;
+ ticks(d,t,L,side,a--b,finite(x2) ? a2--b2 : nullpath,p,arrow,margin,
+ ticklocate(a.y,b.y,pic.scale.y),divisor,opposite);
+ (above ? add : prepend)(f,t*T*tinv*d);
+ });
+
+ void bounds() {
+ if(type == Both) {
+ x2=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax().x;
+ x=opposite ? x2 :
+ (pic.scale.x.automin() ? tickMin(pic).x : pic.userMin().x);
+ } else if(type == Min)
+ x=pic.scale.x.automin() ? tickMin(pic).x : pic.userMin().x;
+ else if(type == Max)
+ x=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax().x;
+
+ real Ymin=finite(ymin) ? ymin : pic.userMin().y;
+ real Ymax=finite(ymax) ? ymax : pic.userMax().y;
+
+ pair a=(x,Ymin);
+ pair b=(x,Ymax);
+ pair a2=(x2,Ymin);
+ pair b2=(x2,Ymax);
+
+ if(finite(a)) {
+ pic.addPoint(a,min(p));
+ pic.addPoint(a,max(p));
+ }
+
+ if(finite(b)) {
+ pic.addPoint(b,min(p));
+ pic.addPoint(b,max(p));
+ }
+
+ if(finite(a) && finite(b)) {
+ frame d;
+ ticks(d,pic.scaling(warn=false),L,side,
+ (0,a.y)--(0,b.y),(0,a2.y)--(0,b2.y),p,arrow,margin,
+ ticklocate(a.y,b.y,pic.scale.y),divisor,opposite);
+ frame f;
+ if(L.s != "") {
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ }
+ pair pos=a+L.relative()*(b-a);
+ pic.addBox(pos,pos,(min(d).x,min(f).y),(max(d).x,max(f).y));
+ }
+ }
+
+ // Process any queued x axis bound calculation requests.
+ for(int i=0; i < pic.scale.x.bound.length; ++i)
+ pic.scale.x.bound[i]();
+
+ pic.scale.x.bound.delete();
+
+ bounds();
+
+ // Request another y bounds calculation before final picture scaling.
+ pic.scale.y.bound.push(bounds);
+}
+
+// Set the x limits of a picture.
+void xlimits(picture pic=currentpicture, real min=-infinity, real max=infinity,
+ bool crop=NoCrop)
+{
+ if(min > max) return;
+
+ pic.scale.x.automin=min <= -infinity;
+ pic.scale.x.automax=max >= infinity;
+
+ bounds mx;
+ if(pic.scale.x.automin() || pic.scale.x.automax())
+ mx=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale);
+
+ if(pic.scale.x.automin) {
+ if(pic.scale.x.automin()) pic.userMinx(mx.min);
+ } else pic.userMinx(min(pic.scale.x.T(min),pic.scale.x.T(max)));
+
+ if(pic.scale.x.automax) {
+ if(pic.scale.x.automax()) pic.userMaxx(mx.max);
+ } else pic.userMaxx(max(pic.scale.x.T(min),pic.scale.x.T(max)));
+
+ if(crop) {
+ pair userMin=pic.userMin();
+ pair userMax=pic.userMax();
+ pic.bounds.xclip(userMin.x,userMax.x);
+ pic.clip(userMin, userMax,
+ new void (frame f, transform t, transform T, pair, pair) {
+ frame Tinvf=T == identity() ? f : t*inverse(T)*inverse(t)*f;
+ clip(f,T*box(((t*userMin).x,(min(Tinvf)).y),
+ ((t*userMax).x,(max(Tinvf)).y)));
+ });
+ }
+}
+
+// Set the y limits of a picture.
+void ylimits(picture pic=currentpicture, real min=-infinity, real max=infinity,
+ bool crop=NoCrop)
+{
+ if(min > max) return;
+
+ pic.scale.y.automin=min <= -infinity;
+ pic.scale.y.automax=max >= infinity;
+
+ bounds my;
+ if(pic.scale.y.automin() || pic.scale.y.automax())
+ my=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale);
+
+ if(pic.scale.y.automin) {
+ if(pic.scale.y.automin()) pic.userMiny(my.min);
+ } else pic.userMiny(min(pic.scale.y.T(min),pic.scale.y.T(max)));
+
+ if(pic.scale.y.automax) {
+ if(pic.scale.y.automax()) pic.userMaxy(my.max);
+ } else pic.userMaxy(max(pic.scale.y.T(min),pic.scale.y.T(max)));
+
+ if(crop) {
+ pair userMin=pic.userMin();
+ pair userMax=pic.userMax();
+ pic.bounds.yclip(userMin.y,userMax.y);
+ pic.clip(userMin, userMax,
+ new void (frame f, transform t, transform T, pair, pair) {
+ frame Tinvf=T == identity() ? f : t*inverse(T)*inverse(t)*f;
+ clip(f,T*box(((min(Tinvf)).x,(t*userMin).y),
+ ((max(Tinvf)).x,(t*userMax).y)));
+ });
+ }
+}
+
+// Crop a picture to the current user-space picture limits.
+void crop(picture pic=currentpicture)
+{
+ xlimits(pic,false);
+ ylimits(pic,false);
+ if(pic.userSetx() && pic.userSety())
+ clip(pic,box(pic.userMin(),pic.userMax()));
+}
+
+// Restrict the x and y limits to box(min,max).
+void limits(picture pic=currentpicture, pair min, pair max, bool crop=NoCrop)
+{
+ xlimits(pic,min.x,max.x);
+ ylimits(pic,min.y,max.y);
+ if(crop && pic.userSetx() && pic.userSety())
+ clip(pic,box(pic.userMin(),pic.userMax()));
+}
+
+// Internal routine to autoscale the user limits of a picture.
+void autoscale(picture pic=currentpicture, axis axis)
+{
+ if(!pic.scale.set) {
+ bounds mx,my;
+ pic.scale.set=true;
+
+ if(pic.userSetx()) {
+ mx=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale);
+ if(pic.scale.x.scale.logarithmic &&
+ floor(pic.userMin().x) == floor(pic.userMax().x)) {
+ if(pic.scale.x.automin())
+ pic.userMinx(floor(pic.userMin().x));
+ if(pic.scale.x.automax())
+ pic.userMaxx(ceil(pic.userMax().x));
+ }
+ } else {mx.min=mx.max=0; pic.scale.set=false;}
+
+ if(pic.userSety()) {
+ my=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale);
+ if(pic.scale.y.scale.logarithmic &&
+ floor(pic.userMin().y) == floor(pic.userMax().y)) {
+ if(pic.scale.y.automin())
+ pic.userMiny(floor(pic.userMin().y));
+ if(pic.scale.y.automax())
+ pic.userMaxy(ceil(pic.userMax().y));
+ }
+ } else {my.min=my.max=0; pic.scale.set=false;}
+
+ pic.scale.x.tickMin=mx.min;
+ pic.scale.x.tickMax=mx.max;
+ pic.scale.y.tickMin=my.min;
+ pic.scale.y.tickMax=my.max;
+ axis.xdivisor=mx.divisor;
+ axis.ydivisor=my.divisor;
+ }
+}
+
+// Draw an x axis.
+void xaxis(picture pic=currentpicture, Label L="", axis axis=YZero,
+ real xmin=-infinity, real xmax=infinity, pen p=currentpen,
+ ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin,
+ bool above=false)
+{
+ if(xmin > xmax) return;
+
+ if(pic.scale.x.automin && xmin > -infinity) pic.scale.x.automin=false;
+ if(pic.scale.x.automax && xmax < infinity) pic.scale.x.automax=false;
+
+ if(!pic.scale.set) {
+ axis(pic,axis);
+ autoscale(pic,axis);
+ }
+
+ Label L=L.copy();
+ bool newticks=false;
+
+ if(xmin != -infinity) {
+ xmin=pic.scale.x.T(xmin);
+ newticks=true;
+ }
+
+ if(xmax != infinity) {
+ xmax=pic.scale.x.T(xmax);
+ newticks=true;
+ }
+
+ if(newticks && pic.userSetx() && ticks != NoTicks) {
+ if(xmin == -infinity) xmin=pic.userMin().x;
+ if(xmax == infinity) xmax=pic.userMax().x;
+ bounds mx=autoscale(xmin,xmax,pic.scale.x.scale);
+ pic.scale.x.tickMin=mx.min;
+ pic.scale.x.tickMax=mx.max;
+ axis.xdivisor=mx.divisor;
+ }
+
+ axis(pic,axis);
+
+ if(xmin == -infinity && !axis.extend) {
+ if(pic.scale.set)
+ xmin=pic.scale.x.automin() ? pic.scale.x.tickMin :
+ max(pic.scale.x.tickMin,pic.userMin().x);
+ else xmin=pic.userMin().x;
+ }
+
+ if(xmax == infinity && !axis.extend) {
+ if(pic.scale.set)
+ xmax=pic.scale.x.automax() ? pic.scale.x.tickMax :
+ min(pic.scale.x.tickMax,pic.userMax().x);
+ else xmax=pic.userMax().x;
+ }
+
+ if(L.defaultposition) L.position(axis.position);
+ L.align(L.align,axis.align);
+
+ xaxisAt(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above);
+ if(axis.type == Both)
+ xaxisAt(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,true);
+}
+
+// Draw a y axis.
+void yaxis(picture pic=currentpicture, Label L="", axis axis=XZero,
+ real ymin=-infinity, real ymax=infinity, pen p=currentpen,
+ ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin,
+ bool above=false, bool autorotate=true)
+{
+ if(ymin > ymax) return;
+
+ if(pic.scale.y.automin && ymin > -infinity) pic.scale.y.automin=false;
+ if(pic.scale.y.automax && ymax < infinity) pic.scale.y.automax=false;
+
+ if(!pic.scale.set) {
+ axis(pic,axis);
+ autoscale(pic,axis);
+ }
+
+ Label L=L.copy();
+ bool newticks=false;
+
+ if(ymin != -infinity) {
+ ymin=pic.scale.y.T(ymin);
+ newticks=true;
+ }
+
+ if(ymax != infinity) {
+ ymax=pic.scale.y.T(ymax);
+ newticks=true;
+ }
+
+ if(newticks && pic.userSety() && ticks != NoTicks) {
+ if(ymin == -infinity) ymin=pic.userMin().y;
+ if(ymax == infinity) ymax=pic.userMax().y;
+ bounds my=autoscale(ymin,ymax,pic.scale.y.scale);
+ pic.scale.y.tickMin=my.min;
+ pic.scale.y.tickMax=my.max;
+ axis.ydivisor=my.divisor;
+ }
+
+ axis(pic,axis);
+
+ if(ymin == -infinity && !axis.extend) {
+ if(pic.scale.set)
+ ymin=pic.scale.y.automin() ? pic.scale.y.tickMin :
+ max(pic.scale.y.tickMin,pic.userMin().y);
+ else ymin=pic.userMin().y;
+ }
+
+
+ if(ymax == infinity && !axis.extend) {
+ if(pic.scale.set)
+ ymax=pic.scale.y.automax() ? pic.scale.y.tickMax :
+ min(pic.scale.y.tickMax,pic.userMax().y);
+ else ymax=pic.userMax().y;
+ }
+
+ if(L.defaultposition) L.position(axis.position);
+ L.align(L.align,axis.align);
+
+ if(autorotate && L.defaulttransform) {
+ frame f;
+ add(f,Label(L.s,(0,0),L.p));
+ if(length(max(f)-min(f)) > ylabelwidth*fontsize(L.p))
+ L.transform(rotate(90));
+ }
+
+ yaxisAt(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above);
+ if(axis.type == Both)
+ yaxisAt(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,true);
+}
+
+// Draw x and y axes.
+void axes(picture pic=currentpicture, Label xlabel="", Label ylabel="",
+ 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);
+}
+
+// Draw a yaxis at x.
+void xequals(picture pic=currentpicture, Label L="", real x,
+ bool extend=false, real ymin=-infinity, real ymax=infinity,
+ pen p=currentpen, ticks ticks=NoTicks,
+ arrowbar arrow=None, margin margin=NoMargin, bool above=true)
+{
+ yaxis(pic,L,XEquals(x,extend),ymin,ymax,p,ticks,arrow,margin,above);
+}
+
+// Draw an xaxis at y.
+void yequals(picture pic=currentpicture, Label L="", real y,
+ bool extend=false, real xmin=-infinity, real xmax=infinity,
+ pen p=currentpen, ticks ticks=NoTicks,
+ arrowbar arrow=None, margin margin=NoMargin, bool above=true)
+{
+ xaxis(pic,L,YEquals(y,extend),xmin,xmax,p,ticks,arrow,margin,above);
+}
+
+pair Scale(picture pic=currentpicture, pair z)
+{
+ return (pic.scale.x.T(z.x),pic.scale.y.T(z.y));
+}
+
+real ScaleX(picture pic=currentpicture, real x)
+{
+ return pic.scale.x.T(x);
+}
+
+real ScaleY(picture pic=currentpicture, real y)
+{
+ return pic.scale.y.T(y);
+}
+
+// Draw a tick of length size at pair z in direction dir using pen p.
+void tick(picture pic=currentpicture, pair z, pair dir, real size=Ticksize,
+ pen p=currentpen)
+{
+ pair z=Scale(pic,z);
+ pic.add(new void (frame f, transform t) {
+ pair tz=t*z;
+ draw(f,tz--tz+unit(dir)*size,p);
+ });
+ pic.addPoint(z,p);
+ pic.addPoint(z,unit(dir)*size,p);
+}
+
+void xtick(picture pic=currentpicture, explicit pair z, pair dir=N,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,z,dir,size,p);
+}
+
+void xtick(picture pic=currentpicture, real x, pair dir=N,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,(x,pic.scale.y.scale.logarithmic ? 1 : 0),dir,size,p);
+}
+
+void ytick(picture pic=currentpicture, explicit pair z, pair dir=E,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,z,dir,size,p);
+}
+
+void ytick(picture pic=currentpicture, real y, pair dir=E,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,(pic.scale.x.scale.logarithmic ? 1 : 0,y),dir,size,p);
+}
+
+void tick(picture pic=currentpicture, Label L, real value, explicit pair z,
+ pair dir, string format="", real size=Ticksize, pen p=currentpen)
+{
+ Label L=L.copy();
+ L.position(Scale(pic,z));
+ L.align(L.align,-dir);
+ if(shift(L.T)*0 == 0)
+ L.T=shift(dot(dir,L.align.dir) > 0 ? dir*size :
+ ticklabelshift(L.align.dir,p))*L.T;
+ L.p(p);
+ if(L.s == "") L.s=format(format == "" ? defaultformat : format,value);
+ L.s=baseline(L.s,baselinetemplate);
+ add(pic,L);
+ tick(pic,z,dir,size,p);
+}
+
+void xtick(picture pic=currentpicture, Label L, explicit pair z, pair dir=N,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,L,z.x,z,dir,format,size,p);
+}
+
+void xtick(picture pic=currentpicture, Label L, real x, pair dir=N,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0),dir,size,p);
+}
+
+void ytick(picture pic=currentpicture, Label L, explicit pair z, pair dir=E,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,L,z.y,z,dir,format,size,p);
+}
+
+void ytick(picture pic=currentpicture, Label L, real y, pair dir=E,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y),dir,format,size,p);
+}
+
+private void label(picture pic, Label L, pair z, real x, align align,
+ string format, pen p)
+{
+ Label L=L.copy();
+ L.position(z);
+ L.align(align);
+ L.p(p);
+ if(shift(L.T)*0 == 0)
+ L.T=shift(ticklabelshift(L.align.dir,L.p))*L.T;
+ if(L.s == "") L.s=format(format == "" ? defaultformat : format,x);
+ L.s=baseline(L.s,baselinetemplate);
+ add(pic,L);
+}
+
+// Put a label on the x axis.
+void labelx(picture pic=currentpicture, Label L="", explicit pair z,
+ align align=S, string format="", pen p=currentpen)
+{
+ label(pic,L,Scale(pic,z),z.x,align,format,p);
+}
+
+void labelx(picture pic=currentpicture, Label L="", real x,
+ align align=S, string format="", pen p=currentpen)
+{
+ labelx(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0),align,format,p);
+}
+
+void labelx(picture pic=currentpicture, Label L,
+ string format="", explicit pen p=currentpen)
+{
+ labelx(pic,L,L.position.position,format,p);
+}
+
+// Put a label on the y axis.
+void labely(picture pic=currentpicture, Label L="", explicit pair z,
+ align align=W, string format="", pen p=currentpen)
+{
+ label(pic,L,Scale(pic,z),z.y,align,format,p);
+}
+
+void labely(picture pic=currentpicture, Label L="", real y,
+ align align=W, string format="", pen p=currentpen)
+{
+ labely(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y),align,format,p);
+}
+
+void labely(picture pic=currentpicture, Label L,
+ string format="", explicit pen p=currentpen)
+{
+ labely(pic,L,L.position.position,format,p);
+}
+
+private string noprimary="Primary axis must be drawn before secondary axis";
+
+// Construct a secondary X axis
+picture secondaryX(picture primary=currentpicture, void f(picture))
+{
+ if(!primary.scale.set) abort(noprimary);
+ picture pic;
+ if(primary.userMax().x == primary.userMin().x) return pic;
+ f(pic);
+ if(!pic.userSetx()) return pic;
+ bounds a=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale);
+ real bmin=pic.scale.x.automin() ? a.min : pic.userMin().x;
+ real bmax=pic.scale.x.automax() ? a.max : pic.userMax().x;
+
+ real denom=bmax-bmin;
+ if(denom != 0) {
+ pic.erase();
+ real m=(primary.userMax().x-primary.userMin().x)/denom;
+ pic.scale.x.postscale=Linear(m,bmin-primary.userMin().x/m);
+ pic.scale.set=true;
+ pic.scale.x.tickMin=pic.scale.x.postscale.T(a.min);
+ pic.scale.x.tickMax=pic.scale.x.postscale.T(a.max);
+ pic.scale.y.tickMin=primary.userMin().y;
+ pic.scale.y.tickMax=primary.userMax().y;
+ axis.xdivisor=a.divisor;
+ f(pic);
+ }
+ pic.userCopy(primary);
+ return pic;
+}
+
+// Construct a secondary Y axis
+picture secondaryY(picture primary=currentpicture, void f(picture))
+{
+ if(!primary.scale.set) abort(noprimary);
+ picture pic;
+ if(primary.userMax().y == primary.userMin().y) return pic;
+ f(pic);
+ if(!pic.userSety()) return pic;
+ bounds a=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale);
+ real bmin=pic.scale.y.automin() ? a.min : pic.userMin().y;
+ real bmax=pic.scale.y.automax() ? a.max : pic.userMax().y;
+
+ real denom=bmax-bmin;
+ if(denom != 0) {
+ pic.erase();
+ real m=(primary.userMax().y-primary.userMin().y)/denom;
+ pic.scale.y.postscale=Linear(m,bmin-primary.userMin().y/m);
+ pic.scale.set=true;
+ pic.scale.x.tickMin=primary.userMin().x;
+ pic.scale.x.tickMax=primary.userMax().x;
+ pic.scale.y.tickMin=pic.scale.y.postscale.T(a.min);
+ pic.scale.y.tickMax=pic.scale.y.postscale.T(a.max);
+ axis.ydivisor=a.divisor;
+ f(pic);
+ }
+ pic.userCopy(primary);
+ return pic;
+}
+
+typedef guide graph(pair f(real), real, real, int);
+typedef guide[] multigraph(pair f(real), real, real, int);
+
+graph graph(interpolate join)
+{
+ return new guide(pair f(real), real a, real b, int n) {
+ real width=b-a;
+ return n == 0 ? join(f(a)) :
+ join(...sequence(new guide(int i) {return f(a+(i/n)*width);},n+1));
+ };
+}
+
+multigraph graph(interpolate join, bool3 cond(real))
+{
+ return new guide[](pair f(real), real a, real b, int n) {
+ real width=b-a;
+ if(n == 0) return new guide[] {join(cond(a) ? f(a) : nullpath)};
+ guide[] G;
+ guide[] g;
+ for(int i=0; i < n+1; ++i) {
+ real t=a+(i/n)*width;
+ bool3 b=cond(t);
+ if(b)
+ g.push(f(t));
+ else {
+ if(g.length > 0) {
+ G.push(join(...g));
+ g=new guide[] {};
+ }
+ if(b == default)
+ g.push(f(t));
+ }
+ }
+ if(g.length > 0)
+ G.push(join(...g));
+ return G;
+ };
+}
+
+guide Straight(... guide[])=operator --;
+guide Spline(... guide[])=operator ..;
+
+interpolate Hermite(splinetype splinetype)
+{
+ return new guide(... guide[] a) {
+ int n=a.length;
+ if(n == 0) return nullpath;
+ real[] x,y;
+ guide G;
+ for(int i=0; i < n; ++i) {
+ guide g=a[i];
+ int m=size(g);
+ if(m == 0) continue;
+ pair z=point(g,0);
+ x.push(z.x);
+ y.push(z.y);
+ if(m > 1) {
+ G=G..hermite(x,y,splinetype) & g;
+ pair z=point(g,m);
+ x=new real[] {z.x};
+ y=new real[] {z.y};
+ }
+ }
+ return G & hermite(x,y,splinetype);
+ };
+}
+
+interpolate Hermite=Hermite(Spline);
+
+guide graph(picture pic=currentpicture, real f(real), real a, real b,
+ int n=ngraph, real T(real)=identity, interpolate join=operator --)
+{
+ if(T == identity)
+ return graph(join)(new pair(real x) {
+ return (x,pic.scale.y.T(f(pic.scale.x.Tinv(x))));},
+ pic.scale.x.T(a),pic.scale.x.T(b),n);
+ else
+ return graph(join)(new pair(real x) {
+ return Scale(pic,(T(x),f(T(x))));},
+ a,b,n);
+}
+
+guide[] graph(picture pic=currentpicture, real f(real), real a, real b,
+ int n=ngraph, real T(real)=identity,
+ bool3 cond(real), interpolate join=operator --)
+{
+ if(T == identity)
+ return graph(join,cond)(new pair(real x) {
+ return (x,pic.scale.y.T(f(pic.scale.x.Tinv(x))));},
+ pic.scale.x.T(a),pic.scale.x.T(b),n);
+ else
+ return graph(join,cond)(new pair(real x) {
+ return Scale(pic,(T(x),f(T(x))));},
+ a,b,n);
+}
+
+guide graph(picture pic=currentpicture, real x(real), real y(real), real a,
+ real b, int n=ngraph, real T(real)=identity,
+ interpolate join=operator --)
+{
+ if(T == identity)
+ return graph(join)(new pair(real t) {return Scale(pic,(x(t),y(t)));},a,b,n);
+ else
+ return graph(join)(new pair(real t) {
+ return Scale(pic,(x(T(t)),y(T(t))));
+ },a,b,n);
+}
+
+guide[] graph(picture pic=currentpicture, real x(real), real y(real), real a,
+ real b, int n=ngraph, real T(real)=identity, bool3 cond(real),
+ interpolate join=operator --)
+{
+ if(T == identity)
+ return graph(join,cond)(new pair(real t) {return Scale(pic,(x(t),y(t)));},
+ a,b,n);
+ else
+ return graph(join,cond)(new pair(real t) {
+ return Scale(pic,(x(T(t)),y(T(t))));},
+ a,b,n);
+}
+
+guide graph(picture pic=currentpicture, pair z(real), real a, real b,
+ int n=ngraph, real T(real)=identity, interpolate join=operator --)
+{
+ if(T == identity)
+ return graph(join)(new pair(real t) {return Scale(pic,z(t));},a,b,n);
+ else
+ return graph(join)(new pair(real t) {
+ return Scale(pic,z(T(t)));
+ },a,b,n);
+}
+
+guide[] graph(picture pic=currentpicture, pair z(real), real a, real b,
+ int n=ngraph, real T(real)=identity, bool3 cond(real),
+ interpolate join=operator --)
+{
+ if(T == identity)
+ return graph(join,cond)(new pair(real t) {return Scale(pic,z(t));},a,b,n);
+ else
+ return graph(join,cond)(new pair(real t) {
+ return Scale(pic,z(T(t)));
+ },a,b,n);
+}
+
+string conditionlength="condition array has different length than data";
+
+void checkconditionlength(int x, int y)
+{
+ checklengths(x,y,conditionlength);
+}
+
+guide graph(picture pic=currentpicture, pair[] z, interpolate join=operator --)
+{
+ int i=0;
+ return graph(join)(new pair(real) {
+ pair w=Scale(pic,z[i]);
+ ++i;
+ return w;
+ },0,0,z.length-1);
+}
+
+guide[] graph(picture pic=currentpicture, pair[] z, bool3[] cond,
+ interpolate join=operator --)
+{
+ int n=z.length;
+ int i=0;
+ pair w;
+ checkconditionlength(cond.length,n);
+ bool3 condition(real) {
+ bool3 b=cond[i];
+ if(b != false) w=Scale(pic,z[i]);
+ ++i;
+ return b;
+ }
+ return graph(join,condition)(new pair(real) {return w;},0,0,n-1);
+}
+
+guide graph(picture pic=currentpicture, real[] x, real[] y,
+ interpolate join=operator --)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ int i=0;
+ return graph(join)(new pair(real) {
+ pair w=Scale(pic,(x[i],y[i]));
+ ++i;
+ return w;
+ },0,0,n-1);
+}
+
+guide[] graph(picture pic=currentpicture, real[] x, real[] y, bool3[] cond,
+ interpolate join=operator --)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ int i=0;
+ pair w;
+ checkconditionlength(cond.length,n);
+ bool3 condition(real) {
+ bool3 b=cond[i];
+ if(b != false) w=Scale(pic,(x[i],y[i]));
+ ++i;
+ return b;
+ }
+ return graph(join,condition)(new pair(real) {return w;},0,0,n-1);
+}
+
+// Connect points in z into segments corresponding to consecutive true elements
+// of b using interpolation operator join.
+path[] segment(pair[] z, bool[] cond, interpolate join=operator --)
+{
+ checkconditionlength(cond.length,z.length);
+ int[][] segment=segment(cond);
+ return sequence(new path(int i) {return join(...z[segment[i]]);},
+ segment.length);
+}
+
+pair polar(real r, real theta)
+{
+ return r*expi(theta);
+}
+
+guide polargraph(picture pic=currentpicture, real r(real), real a, real b,
+ int n=ngraph, interpolate join=operator --)
+{
+ return graph(join)(new pair(real theta) {
+ return Scale(pic,polar(r(theta),theta));
+ },a,b,n);
+}
+
+guide polargraph(picture pic=currentpicture, real[] r, real[] theta,
+ interpolate join=operator--)
+{
+ int n=r.length;
+ checklengths(n,theta.length);
+ int i=0;
+ return graph(join)(new pair(real) {
+ pair w=Scale(pic,polar(r[i],theta[i]));
+ ++i;
+ return w;
+ },0,0,n-1);
+}
+
+void errorbar(picture pic, pair z, pair dp, pair dm, pen p=currentpen,
+ real size=0)
+{
+ real dmx=-abs(dm.x);
+ real dmy=-abs(dm.y);
+ real dpx=abs(dp.x);
+ real dpy=abs(dp.y);
+ if(dmx != dpx) draw(pic,Scale(pic,z+(dmx,0))--Scale(pic,z+(dpx,0)),p,
+ Bars(size));
+ if(dmy != dpy) draw(pic,Scale(pic,z+(0,dmy))--Scale(pic,z+(0,dpy)),p,
+ Bars(size));
+}
+
+void errorbars(picture pic=currentpicture, pair[] z, pair[] dp, pair[] dm={},
+ bool[] cond={}, pen p=currentpen, real size=0)
+{
+ if(dm.length == 0) dm=dp;
+ int n=z.length;
+ checklengths(n,dm.length);
+ checklengths(n,dp.length);
+ bool all=cond.length == 0;
+ if(!all)
+ checkconditionlength(cond.length,n);
+ for(int i=0; i < n; ++i) {
+ if(all || cond[i])
+ errorbar(pic,z[i],dp[i],dm[i],p,size);
+ }
+}
+
+void errorbars(picture pic=currentpicture, real[] x, real[] y,
+ real[] dpx, real[] dpy, real[] dmx={}, real[] dmy={},
+ bool[] cond={}, pen p=currentpen, real size=0)
+{
+ if(dmx.length == 0) dmx=dpx;
+ if(dmy.length == 0) dmy=dpy;
+ int n=x.length;
+ checklengths(n,y.length);
+ checklengths(n,dpx.length);
+ checklengths(n,dpy.length);
+ checklengths(n,dmx.length);
+ checklengths(n,dmy.length);
+ bool all=cond.length == 0;
+ if(!all)
+ checkconditionlength(cond.length,n);
+ for(int i=0; i < n; ++i) {
+ if(all || cond[i])
+ errorbar(pic,(x[i],y[i]),(dpx[i],dpy[i]),(dmx[i],dmy[i]),p,size);
+ }
+}
+
+void errorbars(picture pic=currentpicture, real[] x, real[] y,
+ real[] dpy, bool[] cond={}, pen p=currentpen, real size=0)
+{
+ errorbars(pic,x,y,0*x,dpy,cond,p,size);
+}
+
+// Return a vector field on path g, specifying the vector as a function of the
+// relative position along path g in [0,1].
+picture vectorfield(path vector(real), path g, int n, bool truesize=false,
+ pen p=currentpen, arrowbar arrow=Arrow,
+ margin margin=PenMargin)
+{
+ picture pic;
+ for(int i=0; i < n; ++i) {
+ real x=(n == 1) ? 0.5 : i/(n-1);
+ if(truesize)
+ draw(relpoint(g,x),pic,vector(x),p,arrow);
+ else
+ draw(pic,shift(relpoint(g,x))*vector(x),p,arrow,margin);
+ }
+ return pic;
+}
+
+real maxlength(pair a, pair b, int nx, int ny)
+{
+ return min((b.x-a.x)/nx,(b.y-a.y)/ny);
+}
+
+// return a vector field over box(a,b).
+picture vectorfield(path vector(pair), pair a, pair b,
+ int nx=nmesh, int ny=nx, bool truesize=false,
+ real maxlength=truesize ? 0 : maxlength(a,b,nx,ny),
+ bool cond(pair z)=null, pen p=currentpen,
+ arrowbar arrow=Arrow, margin margin=PenMargin)
+{
+ picture pic;
+ real dx=1/nx;
+ real dy=1/ny;
+ bool all=cond == null;
+ real scale;
+
+ if(maxlength > 0) {
+ real size(pair z) {
+ path g=vector(z);
+ return abs(point(g,size(g)-1)-point(g,0));
+ }
+ real max=size(a);
+ for(int i=0; i <= nx; ++i) {
+ real x=interp(a.x,b.x,i*dx);
+ for(int j=0; j <= ny; ++j)
+ max=max(max,size((x,interp(a.y,b.y,j*dy))));
+ }
+ scale=max > 0 ? maxlength/max : 1;
+ } else scale=1;
+
+ for(int i=0; i <= nx; ++i) {
+ real x=interp(a.x,b.x,i*dx);
+ for(int j=0; j <= ny; ++j) {
+ real y=interp(a.y,b.y,j*dy);
+ pair z=(x,y);
+ if(all || cond(z)) {
+ path g=scale(scale)*vector(z);
+ if(truesize)
+ draw(z,pic,g,p,arrow);
+ else
+ draw(pic,shift(z)*g,p,arrow,margin);
+ }
+ }
+ }
+ return pic;
+}
+
+// True arc
+path Arc(pair c, real r, real angle1, real angle2, bool direction,
+ int n=nCircle)
+{
+ angle1=radians(angle1);
+ angle2=radians(angle2);
+ if(direction) {
+ if(angle1 >= angle2) angle1 -= 2pi;
+ } else if(angle2 >= angle1) angle2 -= 2pi;
+ return shift(c)*polargraph(new real(real t){return r;},angle1,angle2,n,
+ operator ..);
+}
+
+path Arc(pair c, real r, real angle1, real angle2, int n=nCircle)
+{
+ return Arc(c,r,angle1,angle2,angle2 >= angle1 ? CCW : CW,n);
+}
+
+path Arc(pair c, explicit pair z1, explicit pair z2, bool direction=CCW,
+ int n=nCircle)
+{
+ return Arc(c,abs(z1-c),degrees(z1-c),degrees(z2-c),direction,n);
+}
+
+// True circle
+path Circle(pair c, real r, int n=nCircle)
+{
+ return Arc(c,r,0,360,n)&cycle;
+}
diff --git a/Master/texmf-dist/asymptote/graph3.asy b/Master/texmf-dist/asymptote/graph3.asy
new file mode 100644
index 00000000000..91e5194bea9
--- /dev/null
+++ b/Master/texmf-dist/asymptote/graph3.asy
@@ -0,0 +1,2041 @@
+// Three-dimensional graphing routines
+
+private import math;
+import graph;
+import three;
+
+triple zero3(real) {return O;}
+
+typedef triple direction3(real);
+direction3 Dir(triple dir) {return new triple(real) {return dir;};}
+
+ticklocate ticklocate(real a, real b, autoscaleT S=defaultS,
+ real tickmin=-infinity, real tickmax=infinity,
+ real time(real)=null, direction3 dir)
+{
+ if((valuetime) time == null) time=linear(S.T(),a,b);
+ ticklocate locate;
+ locate.a=a;
+ locate.b=b;
+ locate.S=S.copy();
+ if(finite(tickmin)) locate.S.tickMin=tickmin;
+ if(finite(tickmax)) locate.S.tickMax=tickmax;
+ locate.time=time;
+ locate.dir=zero;
+ locate.dir3=dir;
+ return locate;
+}
+
+private struct locateT {
+ real t; // tick location time
+ triple V; // tick location in frame coordinates
+ triple pathdir; // path direction in frame coordinates
+ triple dir; // tick direction in frame coordinates
+
+ void dir(transform3 T, path3 g, ticklocate locate, real t) {
+ pathdir=unit(shiftless(T)*dir(g,t));
+ triple Dir=locate.dir3(t);
+ dir=unit(Dir);
+ }
+ // Locate the desired position of a tick along a path.
+ void calc(transform3 T, path3 g, ticklocate locate, real val) {
+ t=locate.time(val);
+ V=T*point(g,t);
+ dir(T,g,locate,t);
+ }
+}
+
+void drawtick(picture pic, transform3 T, path3 g, path3 g2,
+ ticklocate locate, real val, real Size, int sign, pen p,
+ bool extend)
+{
+ locateT locate1,locate2;
+ locate1.calc(T,g,locate,val);
+ path3 G;
+ if(extend && size(g2) > 0) {
+ locate2.calc(T,g2,locate,val);
+ G=locate1.V--locate2.V;
+ } else
+ G=(sign == 0) ?
+ locate1.V-Size*locate1.dir--locate1.V+Size*locate1.dir :
+ locate1.V--locate1.V+Size*sign*locate1.dir;
+ draw(pic,G,p,name="tick");
+}
+
+triple ticklabelshift(triple align, pen p=currentpen)
+{
+ return 0.25*unit(align)*labelmargin(p);
+}
+
+// Signature of routines that draw labelled paths with ticks and tick labels.
+typedef void ticks3(picture, transform3, Label, path3, path3, pen,
+ arrowbar3, margin3, ticklocate, int[], bool opposite=false,
+ bool primary=true);
+
+// Label a tick on a frame.
+void labeltick(picture pic, transform3 T, path3 g,
+ ticklocate locate, real val, int sign, real Size,
+ ticklabel ticklabel, Label F, real norm=0)
+{
+ locateT locate1;
+ locate1.calc(T,g,locate,val);
+ triple align=F.align.dir3;
+ if(align == O) align=sign*locate1.dir;
+
+ triple shift=align*labelmargin(F.p);
+ if(dot(align,sign*locate1.dir) >= 0)
+ shift=sign*(Size)*locate1.dir;
+
+ real label;
+ if(locate.S.scale.logarithmic)
+ label=locate.S.scale.Tinv(val);
+ else {
+ label=val;
+ if(abs(label) < zerotickfuzz*norm) label=0;
+ // Fix epsilon errors at +/-1e-4
+ // default format changes to scientific notation here
+ if(abs(abs(label)-1e-4) < epsilon) label=sgn(label)*1e-4;
+ }
+
+ string s=ticklabel(label);
+ triple v=locate1.V+shift;
+ if(s != "")
+ label(pic,F.defaulttransform3 ? baseline(s,baselinetemplate) : F.T3*s,v,
+ align,F.p);
+}
+
+// Add axis label L to frame f.
+void labelaxis(picture pic, transform3 T, Label L, path3 g,
+ ticklocate locate=null, int sign=1, bool ticklabels=false)
+{
+ triple m=pic.min(identity4);
+ triple M=pic.max(identity4);
+ triple align=L.align.dir3;
+ Label L=L.copy();
+
+ pic.add(new void(frame f, transform3 T, picture pic2, projection P) {
+ path3 g=T*g;
+ real t=relative(L,g);
+ triple v=point(g,t);
+ picture F;
+ if(L.align.dir3 == O)
+ align=unit(invert(L.align.dir,v,P))*abs(L.align.dir);
+
+ if(ticklabels && locate != null && piecewisestraight(g)) {
+ locateT locate1;
+ locate1.dir(T,g,locate,t);
+ triple pathdir=locate1.pathdir;
+
+ triple perp=cross(pathdir,P.normal);
+ if(align == O)
+ align=unit(sgn(dot(sign*locate1.dir,perp))*perp);
+ path[] g=project(box(T*m,T*M),P);
+ pair z=project(v,P);
+ pair Ppathdir=project(v+pathdir,P)-z;
+ pair Perp=unit(I*Ppathdir);
+ real angle=degrees(Ppathdir,warn=false);
+ transform S=rotate(-angle,z);
+ path[] G=S*g;
+ pair Palign=project(v+align,P)-z;
+ pair Align=rotate(-angle)*dot(Palign,Perp)*Perp;
+ pair offset=unit(Palign)*
+ abs((Align.y >= 0 ? max(G).y : (Align.y < 0 ? min(G).y : 0))-z.y);
+ triple normal=cross(pathdir,align);
+ if(normal != O) v=invert(z+offset,normal,v,P);
+ }
+
+ label(F,L,v);
+ add(f,F.fit3(identity4,pic2,P));
+ },exact=false);
+
+ path3[] G=path3(texpath(L,bbox=true));
+ if(G.length > 0) {
+ G=L.align.is3D ? align(G,O,align,L.p) : L.T3*G;
+ triple v=point(g,relative(L,g));
+ pic.addBox(v,v,min(G),max(G));
+ }
+}
+
+// Tick construction routine for a user-specified array of tick values.
+ticks3 Ticks3(int sign, Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks=new real[], real[] ticks=new real[], int N=1,
+ bool begin=true, bool end=true,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return new void(picture pic, transform3 t, Label L, path3 g, path3 g2, pen p,
+ arrowbar3 arrow, margin3 margin, ticklocate locate,
+ int[] divisor, bool opposite, bool primary) {
+ // Use local copy of context variables:
+ int Sign=opposite ? -1 : 1;
+ int sign=Sign*sign;
+ pen pTick=pTick;
+ pen ptick=ptick;
+ ticklabel ticklabel=ticklabel;
+
+ real Size=Size;
+ real size=size;
+ if(Size == 0) Size=Ticksize;
+ if(size == 0) size=ticksize;
+
+ Label L=L.copy();
+ Label F=F.copy();
+ L.p(p);
+ F.p(p);
+ if(pTick == nullpen) pTick=p;
+ if(ptick == nullpen) ptick=pTick;
+
+ bool ticklabels=false;
+ path3 G=t*g;
+ path3 G2=t*g2;
+
+ scalefcn T;
+
+ real a,b;
+ if(locate.S.scale.logarithmic) {
+ a=locate.S.postscale.Tinv(locate.a);
+ b=locate.S.postscale.Tinv(locate.b);
+ T=locate.S.scale.T;
+ } else {
+ a=locate.S.Tinv(locate.a);
+ b=locate.S.Tinv(locate.b);
+ T=identity;
+ }
+
+ if(a > b) {real temp=a; a=b; b=temp;}
+
+ real norm=max(abs(a),abs(b));
+
+ string format=autoformat(F.s,norm...Ticks);
+ if(F.s == "%") F.s="";
+ if(ticklabel == null) {
+ if(locate.S.scale.logarithmic) {
+ int base=round(locate.S.scale.Tinv(1));
+ ticklabel=format == "%" ? Format("") : DefaultLogFormat(base);
+ } else ticklabel=Format(format);
+ }
+
+ bool labelaxis=L.s != "" && primary;
+
+ begingroup3(pic,"axis");
+
+ if(primary) draw(pic,margin(G,p).g,p,arrow);
+ else draw(pic,G,p);
+
+ for(int i=(begin ? 0 : 1); i < (end ? Ticks.length : Ticks.length-1); ++i) {
+ real val=T(Ticks[i]);
+ if(val >= a && val <= b)
+ drawtick(pic,t,g,g2,locate,val,Size,sign,pTick,extend);
+ }
+ for(int i=0; i < ticks.length; ++i) {
+ real val=T(ticks[i]);
+ if(val >= a && val <= b)
+ drawtick(pic,t,g,g2,locate,val,size,sign,ptick,extend);
+ }
+
+ if(N == 0) N=1;
+ if(Size > 0 && primary) {
+ for(int i=(beginlabel ? 0 : 1);
+ i < (endlabel ? Ticks.length : Ticks.length-1); i += N) {
+ real val=T(Ticks[i]);
+ if(val >= a && val <= b) {
+ ticklabels=true;
+ labeltick(pic,t,g,locate,val,Sign,Size,ticklabel,F,norm);
+ }
+ }
+ }
+ if(labelaxis)
+ labelaxis(pic,t,L,G,locate,Sign,ticklabels);
+
+ endgroup3(pic);
+ };
+}
+
+// Automatic tick construction routine.
+ticks3 Ticks3(int sign, Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return new void(picture pic, transform3 T, Label L,
+ path3 g, path3 g2, pen p,
+ arrowbar3 arrow, margin3 margin=NoMargin3, ticklocate locate,
+ int[] divisor, bool opposite, bool primary) {
+ path3 G=T*g;
+ real limit=Step == 0 ? axiscoverage*arclength(G) : 0;
+ tickvalues values=modify(generateticks(sign,F,ticklabel,N,n,Step,step,
+ Size,size,identity(),1,
+ project(G,currentprojection),
+ limit,p,locate,divisor,
+ opposite));
+ Ticks3(sign,F,ticklabel,beginlabel,endlabel,values.major,values.minor,
+ values.N,begin,end,Size,size,extend,pTick,ptick)
+ (pic,T,L,g,g2,p,arrow,margin,locate,divisor,opposite,primary);
+ };
+}
+
+ticks3 NoTicks3()
+{
+ return new void(picture pic, transform3 T, Label L, path3 g,
+ path3, pen p, arrowbar3 arrow, margin3 margin,
+ ticklocate, int[], bool opposite, bool primary) {
+ path3 G=T*g;
+ if(primary) draw(pic,margin(G,p).g,p,arrow,margin);
+ else draw(pic,G,p);
+ if(L.s != "" && primary) {
+ Label L=L.copy();
+ L.p(p);
+ labelaxis(pic,T,L,G,opposite ? -1 : 1);
+ }
+ };
+}
+
+ticks3 InTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks3(-1,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,modify,Size,size,extend,pTick,ptick);
+}
+
+ticks3 OutTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks3(1,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,modify,Size,size,extend,pTick,ptick);
+}
+
+ticks3 InOutTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true, tickmodifier modify=None,
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks3(0,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,modify,Size,size,extend,pTick,ptick);
+}
+
+ticks3 InTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks, real[] ticks=new real[],
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks3(-1,format,ticklabel,beginlabel,endlabel,
+ Ticks,ticks,Size,size,extend,pTick,ptick);
+}
+
+ticks3 OutTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks, real[] ticks=new real[],
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks3(1,format,ticklabel,beginlabel,endlabel,
+ Ticks,ticks,Size,size,extend,pTick,ptick);
+}
+
+ticks3 InOutTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ real[] Ticks, real[] ticks=new real[],
+ real Size=0, real size=0, bool extend=false,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return Ticks3(0,format,ticklabel,beginlabel,endlabel,
+ Ticks,ticks,Size,size,extend,pTick,ptick);
+}
+
+ticks3 NoTicks3=NoTicks3(),
+InTicks=InTicks(),
+OutTicks=OutTicks(),
+InOutTicks=InOutTicks();
+
+triple tickMin3(picture pic)
+{
+ return minbound(pic.userMin(),(pic.scale.x.tickMin,pic.scale.y.tickMin,
+ pic.scale.z.tickMin));
+}
+
+triple tickMax3(picture pic)
+{
+ return maxbound(pic.userMax(),(pic.scale.x.tickMax,pic.scale.y.tickMax,
+ pic.scale.z.tickMax));
+}
+
+axis Bounds(int type=Both, int type2=Both, triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=type;
+ axis.type2=type2;
+ axis.position=0.5;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis YZEquals(real y, real z, triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.type2=Value;
+ axis.value=pic.scale.y.T(y);
+ axis.value2=pic.scale.z.T(z);
+ axis.position=1;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis XZEquals(real x, real z, triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.type2=Value;
+ axis.value=pic.scale.x.T(x);
+ axis.value2=pic.scale.z.T(z);
+ axis.position=1;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis XYEquals(real x, real y, triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.type2=Value;
+ axis.value=pic.scale.x.T(x);
+ axis.value2=pic.scale.y.T(y);
+ axis.position=1;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis YZZero(triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.type2=Value;
+ axis.value=pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0);
+ axis.value2=pic.scale.z.T(pic.scale.z.scale.logarithmic ? 1 : 0);
+ axis.position=1;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis XZZero(triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.type2=Value;
+ axis.value=pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0);
+ axis.value2=pic.scale.z.T(pic.scale.z.scale.logarithmic ? 1 : 0);
+ axis.position=1;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis XYZero(triple align=O, bool extend=false)
+{
+ return new void(picture pic, axisT axis) {
+ axis.type=Value;
+ axis.type2=Value;
+ axis.value=pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0);
+ axis.value2=pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0);
+ axis.position=1;
+ axis.align=align;
+ axis.extend=extend;
+ };
+}
+
+axis
+Bounds=Bounds(),
+YZZero=YZZero(),
+XZZero=XZZero(),
+XYZero=XYZero();
+
+// Draw a general three-dimensional axis.
+void axis(picture pic=currentpicture, Label L="", path3 g, path3 g2=nullpath3,
+ pen p=currentpen, ticks3 ticks, ticklocate locate,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3,
+ int[] divisor=new int[], bool above=false, bool opposite=false)
+{
+ Label L=L.copy();
+ real t=reltime(g,0.5);
+ if(L.defaultposition) L.position(t);
+ divisor=copy(divisor);
+ locate=locate.copy();
+
+ pic.add(new void (picture f, transform3 t, transform3 T, triple, triple) {
+ picture d;
+ ticks(d,t,L,g,g2,p,arrow,margin,locate,divisor,opposite,true);
+ add(f,t*T*inverse(t)*d);
+ },above=above);
+
+ addPath(pic,g,p);
+
+ if(L.s != "") {
+ frame f;
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ triple pos=point(g,L.relative()*length(g));
+ pic.addBox(pos,pos,min3(f),max3(f));
+ }
+}
+
+real xtrans(transform3 t, real x)
+{
+ return (t*(x,0,0)).x;
+}
+
+real ytrans(transform3 t, real y)
+{
+ return (t*(0,y,0)).y;
+}
+
+real ztrans(transform3 t, real z)
+{
+ return (t*(0,0,z)).z;
+}
+
+private triple defaultdir(triple X, triple Y, triple Z, bool opposite=false,
+ projection P) {
+ triple u=cross(P.normal,Z);
+ return abs(dot(u,X)) > abs(dot(u,Y)) ? -X : (opposite ? Y : -Y);
+}
+
+// An internal routine to draw an x axis at a particular y value.
+void xaxis3At(picture pic=currentpicture, Label L="", axis axis,
+ real xmin=-infinity, real xmax=infinity, pen p=currentpen,
+ ticks3 ticks=NoTicks3,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=true,
+ bool opposite=false, bool opposite2=false, bool primary=true)
+{
+ int type=axis.type;
+ int type2=axis.type2;
+ triple dir=axis.align.dir3 == O ?
+ defaultdir(Y,Z,X,opposite^opposite2,currentprojection) : axis.align.dir3;
+ Label L=L.copy();
+ if(L.align.dir3 == O && L.align.dir == 0) L.align(opposite ? -dir : dir);
+
+ real y=axis.value;
+ real z=axis.value2;
+ real y2,z2;
+ int[] divisor=copy(axis.xdivisor);
+
+ pic.add(new void(picture f, transform3 t, transform3 T, triple lb,
+ triple rt) {
+ transform3 tinv=inverse(t);
+ triple a=xmin == -infinity ? tinv*(lb.x-min3(p).x,ytrans(t,y),
+ ztrans(t,z)) : (xmin,y,z);
+ triple b=xmax == infinity ? tinv*(rt.x-max3(p).x,ytrans(t,y),
+ ztrans(t,z)) : (xmax,y,z);
+ real y0;
+ real z0;
+ if(abs(dir.y) < abs(dir.z)) {
+ y0=y;
+ z0=z2;
+ } else {
+ y0=y2;
+ z0=z;
+ }
+
+ triple a2=xmin == -infinity ? tinv*(lb.x-min3(p).x,ytrans(t,y0),
+ ztrans(t,z0)) : (xmin,y0,z0);
+ triple b2=xmax == infinity ? tinv*(rt.x-max3(p).x,ytrans(t,y0),
+ ztrans(t,z0)) : (xmax,y0,z0);
+
+ if(xmin == -infinity || xmax == infinity) {
+ bounds mx=autoscale(a.x,b.x,pic.scale.x.scale);
+ pic.scale.x.tickMin=mx.min;
+ pic.scale.x.tickMax=mx.max;
+ divisor=mx.divisor;
+ }
+
+ triple fuzz=X*epsilon*max(abs(a.x),abs(b.x));
+ a -= fuzz;
+ b += fuzz;
+
+ picture d;
+ ticks(d,t,L,a--b,finite(y0) && finite(z0) ? a2--b2 : nullpath3,
+ p,arrow,margin,
+ ticklocate(a.x,b.x,pic.scale.x,Dir(dir)),divisor,
+ opposite,primary);
+ add(f,t*T*tinv*d);
+ },above=above);
+
+ void bounds() {
+ if(type == Min)
+ y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y;
+ else if(type == Max)
+ y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y;
+ else if(type == Both) {
+ y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y;
+ y=opposite ? y2 :
+ (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y);
+ }
+
+ if(type2 == Min)
+ z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z;
+ else if(type2 == Max)
+ z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z;
+ else if(type2 == Both) {
+ z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z;
+ z=opposite2 ? z2 :
+ (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z);
+ }
+
+ real Xmin=finite(xmin) ? xmin : pic.userMin().x;
+ real Xmax=finite(xmax) ? xmax : pic.userMax().x;
+
+ triple a=(Xmin,y,z);
+ triple b=(Xmax,y,z);
+ triple a2=(Xmin,y2,z2);
+ triple b2=(Xmax,y2,z2);
+
+ if(finite(a)) {
+ pic.addPoint(a,min3(p));
+ pic.addPoint(a,max3(p));
+ }
+
+ if(finite(b)) {
+ pic.addPoint(b,min3(p));
+ pic.addPoint(b,max3(p));
+ }
+
+ if(finite(a) && finite(b)) {
+ picture d;
+ ticks(d,pic.scaling3(warn=false),L,
+ (a.x,0,0)--(b.x,0,0),(a2.x,0,0)--(b2.x,0,0),p,arrow,margin,
+ ticklocate(a.x,b.x,pic.scale.x,Dir(dir)),divisor,
+ opposite,primary);
+ frame f;
+ if(L.s != "") {
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ }
+ triple pos=a+L.relative()*(b-a);
+ triple m=min3(d);
+ triple M=max3(d);
+ pic.addBox(pos,pos,(min3(f).x,m.y,m.z),(max3(f).x,m.y,m.z));
+ }
+ }
+
+ // Process any queued y and z axes bound calculation requests.
+ for(int i=0; i < pic.scale.y.bound.length; ++i)
+ pic.scale.y.bound[i]();
+ for(int i=0; i < pic.scale.z.bound.length; ++i)
+ pic.scale.z.bound[i]();
+
+ pic.scale.y.bound.delete();
+ pic.scale.z.bound.delete();
+
+ bounds();
+
+ // Request another x bounds calculation before final picture scaling.
+ pic.scale.x.bound.push(bounds);
+}
+
+// An internal routine to draw an x axis at a particular y value.
+void yaxis3At(picture pic=currentpicture, Label L="", axis axis,
+ real ymin=-infinity, real ymax=infinity, pen p=currentpen,
+ ticks3 ticks=NoTicks3,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=true,
+ bool opposite=false, bool opposite2=false, bool primary=true)
+{
+ int type=axis.type;
+ int type2=axis.type2;
+ triple dir=axis.align.dir3 == O ?
+ defaultdir(X,Z,Y,opposite^opposite2,currentprojection) : axis.align.dir3;
+ Label L=L.copy();
+ if(L.align.dir3 == O && L.align.dir == 0) L.align(opposite ? -dir : dir);
+
+ real x=axis.value;
+ real z=axis.value2;
+ real x2,z2;
+ int[] divisor=copy(axis.ydivisor);
+
+ pic.add(new void(picture f, transform3 t, transform3 T, triple lb,
+ triple rt) {
+ transform3 tinv=inverse(t);
+ triple a=ymin == -infinity ? tinv*(xtrans(t,x),lb.y-min3(p).y,
+ ztrans(t,z)) : (x,ymin,z);
+ triple b=ymax == infinity ? tinv*(xtrans(t,x),rt.y-max3(p).y,
+ ztrans(t,z)) : (x,ymax,z);
+ real x0;
+ real z0;
+ if(abs(dir.x) < abs(dir.z)) {
+ x0=x;
+ z0=z2;
+ } else {
+ x0=x2;
+ z0=z;
+ }
+
+ triple a2=ymin == -infinity ? tinv*(xtrans(t,x0),lb.y-min3(p).y,
+ ztrans(t,z0)) : (x0,ymin,z0);
+ triple b2=ymax == infinity ? tinv*(xtrans(t,x0),rt.y-max3(p).y,
+ ztrans(t,z0)) : (x0,ymax,z0);
+
+ if(ymin == -infinity || ymax == infinity) {
+ bounds my=autoscale(a.y,b.y,pic.scale.y.scale);
+ pic.scale.y.tickMin=my.min;
+ pic.scale.y.tickMax=my.max;
+ divisor=my.divisor;
+ }
+
+ triple fuzz=Y*epsilon*max(abs(a.y),abs(b.y));
+ a -= fuzz;
+ b += fuzz;
+
+ picture d;
+ ticks(d,t,L,a--b,finite(x0) && finite(z0) ? a2--b2 : nullpath3,
+ p,arrow,margin,
+ ticklocate(a.y,b.y,pic.scale.y,Dir(dir)),divisor,
+ opposite,primary);
+ add(f,t*T*tinv*d);
+ },above=above);
+
+ void bounds() {
+ if(type == Min)
+ x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x;
+ else if(type == Max)
+ x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x;
+ else if(type == Both) {
+ x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x;
+ x=opposite ? x2 :
+ (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x);
+ }
+
+ if(type2 == Min)
+ z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z;
+ else if(type2 == Max)
+ z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z;
+ else if(type2 == Both) {
+ z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z;
+ z=opposite2 ? z2 :
+ (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z);
+ }
+
+ real Ymin=finite(ymin) ? ymin : pic.userMin().y;
+ real Ymax=finite(ymax) ? ymax : pic.userMax().y;
+
+ triple a=(x,Ymin,z);
+ triple b=(x,Ymax,z);
+ triple a2=(x2,Ymin,z2);
+ triple b2=(x2,Ymax,z2);
+
+ if(finite(a)) {
+ pic.addPoint(a,min3(p));
+ pic.addPoint(a,max3(p));
+ }
+
+ if(finite(b)) {
+ pic.addPoint(b,min3(p));
+ pic.addPoint(b,max3(p));
+ }
+
+ if(finite(a) && finite(b)) {
+ picture d;
+ ticks(d,pic.scaling3(warn=false),L,
+ (0,a.y,0)--(0,b.y,0),(0,a2.y,0)--(0,a2.y,0),p,arrow,margin,
+ ticklocate(a.y,b.y,pic.scale.y,Dir(dir)),divisor,
+ opposite,primary);
+ frame f;
+ if(L.s != "") {
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ }
+ triple pos=a+L.relative()*(b-a);
+ triple m=min3(d);
+ triple M=max3(d);
+ pic.addBox(pos,pos,(m.x,min3(f).y,m.z),(m.x,max3(f).y,m.z));
+ }
+ }
+
+ // Process any queued x and z axis bound calculation requests.
+ for(int i=0; i < pic.scale.x.bound.length; ++i)
+ pic.scale.x.bound[i]();
+ for(int i=0; i < pic.scale.z.bound.length; ++i)
+ pic.scale.z.bound[i]();
+
+ pic.scale.x.bound.delete();
+ pic.scale.z.bound.delete();
+
+ bounds();
+
+ // Request another y bounds calculation before final picture scaling.
+ pic.scale.y.bound.push(bounds);
+}
+
+// An internal routine to draw an x axis at a particular y value.
+void zaxis3At(picture pic=currentpicture, Label L="", axis axis,
+ real zmin=-infinity, real zmax=infinity, pen p=currentpen,
+ ticks3 ticks=NoTicks3,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=true,
+ bool opposite=false, bool opposite2=false, bool primary=true)
+{
+ int type=axis.type;
+ int type2=axis.type2;
+ triple dir=axis.align.dir3 == O ?
+ defaultdir(X,Y,Z,opposite^opposite2,currentprojection) : axis.align.dir3;
+ Label L=L.copy();
+ if(L.align.dir3 == O && L.align.dir == 0) L.align(opposite ? -dir : dir);
+
+ real x=axis.value;
+ real y=axis.value2;
+ real x2,y2;
+ int[] divisor=copy(axis.zdivisor);
+
+ pic.add(new void(picture f, transform3 t, transform3 T, triple lb,
+ triple rt) {
+ transform3 tinv=inverse(t);
+ triple a=zmin == -infinity ? tinv*(xtrans(t,x),ytrans(t,y),
+ lb.z-min3(p).z) : (x,y,zmin);
+ triple b=zmax == infinity ? tinv*(xtrans(t,x),ytrans(t,y),
+ rt.z-max3(p).z) : (x,y,zmax);
+ real x0;
+ real y0;
+ if(abs(dir.x) < abs(dir.y)) {
+ x0=x;
+ y0=y2;
+ } else {
+ x0=x2;
+ y0=y;
+ }
+
+ triple a2=zmin == -infinity ? tinv*(xtrans(t,x0),ytrans(t,y0),
+ lb.z-min3(p).z) : (x0,y0,zmin);
+ triple b2=zmax == infinity ? tinv*(xtrans(t,x0),ytrans(t,y0),
+ rt.z-max3(p).z) : (x0,y0,zmax);
+
+ if(zmin == -infinity || zmax == infinity) {
+ bounds mz=autoscale(a.z,b.z,pic.scale.z.scale);
+ pic.scale.z.tickMin=mz.min;
+ pic.scale.z.tickMax=mz.max;
+ divisor=mz.divisor;
+ }
+
+ triple fuzz=Z*epsilon*max(abs(a.z),abs(b.z));
+ a -= fuzz;
+ b += fuzz;
+
+ picture d;
+ ticks(d,t,L,a--b,finite(x0) && finite(y0) ? a2--b2 : nullpath3,
+ p,arrow,margin,
+ ticklocate(a.z,b.z,pic.scale.z,Dir(dir)),divisor,
+ opposite,primary);
+ add(f,t*T*tinv*d);
+ },above=above);
+
+ void bounds() {
+ if(type == Min)
+ x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x;
+ else if(type == Max)
+ x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x;
+ else if(type == Both) {
+ x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x;
+ x=opposite ? x2 :
+ (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x);
+ }
+
+ if(type2 == Min)
+ y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y;
+ else if(type2 == Max)
+ y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y;
+ else if(type2 == Both) {
+ y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y;
+ y=opposite2 ? y2 :
+ (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y);
+ }
+
+ real Zmin=finite(zmin) ? zmin : pic.userMin().z;
+ real Zmax=finite(zmax) ? zmax : pic.userMax().z;
+
+ triple a=(x,y,Zmin);
+ triple b=(x,y,Zmax);
+ triple a2=(x2,y2,Zmin);
+ triple b2=(x2,y2,Zmax);
+
+ if(finite(a)) {
+ pic.addPoint(a,min3(p));
+ pic.addPoint(a,max3(p));
+ }
+
+ if(finite(b)) {
+ pic.addPoint(b,min3(p));
+ pic.addPoint(b,max3(p));
+ }
+
+ if(finite(a) && finite(b)) {
+ picture d;
+ ticks(d,pic.scaling3(warn=false),L,
+ (0,0,a.z)--(0,0,b.z),(0,0,a2.z)--(0,0,a2.z),p,arrow,margin,
+ ticklocate(a.z,b.z,pic.scale.z,Dir(dir)),divisor,
+ opposite,primary);
+ frame f;
+ if(L.s != "") {
+ Label L0=L.copy();
+ L0.position(0);
+ add(f,L0);
+ }
+ triple pos=a+L.relative()*(b-a);
+ triple m=min3(d);
+ triple M=max3(d);
+ pic.addBox(pos,pos,(m.x,m.y,min3(f).z),(m.x,m.y,max3(f).z));
+ }
+ }
+
+ // Process any queued x and y axes bound calculation requests.
+ for(int i=0; i < pic.scale.x.bound.length; ++i)
+ pic.scale.x.bound[i]();
+ for(int i=0; i < pic.scale.y.bound.length; ++i)
+ pic.scale.y.bound[i]();
+
+ pic.scale.x.bound.delete();
+ pic.scale.y.bound.delete();
+
+ bounds();
+
+ // Request another z bounds calculation before final picture scaling.
+ pic.scale.z.bound.push(bounds);
+}
+
+// Internal routine to autoscale the user limits of a picture.
+void autoscale3(picture pic=currentpicture, axis axis)
+{
+ bool set=pic.scale.set;
+ autoscale(pic,axis);
+
+ if(!set) {
+ bounds mz;
+ if(pic.userSetz()) {
+ mz=autoscale(pic.userMin().z,pic.userMax().z,pic.scale.z.scale);
+ if(pic.scale.z.scale.logarithmic &&
+ floor(pic.userMin().z) == floor(pic.userMax().z)) {
+ if(pic.scale.z.automin())
+ pic.userMinz(floor(pic.userMin().z));
+ if(pic.scale.z.automax())
+ pic.userMaxz(ceil(pic.userMax().z));
+ }
+ } else {mz.min=mz.max=0; pic.scale.set=false;}
+
+ pic.scale.z.tickMin=mz.min;
+ pic.scale.z.tickMax=mz.max;
+ axis.zdivisor=mz.divisor;
+ }
+}
+
+// Draw an x axis in three dimensions.
+void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero,
+ real xmin=-infinity, real xmax=infinity, pen p=currentpen,
+ ticks3 ticks=NoTicks3,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=false)
+{
+ if(xmin > xmax) return;
+
+ if(pic.scale.x.automin && xmin > -infinity) pic.scale.x.automin=false;
+ if(pic.scale.x.automax && xmax < infinity) pic.scale.x.automax=false;
+
+ if(!pic.scale.set) {
+ axis(pic,axis);
+ autoscale3(pic,axis);
+ }
+
+ bool newticks=false;
+
+ if(xmin != -infinity) {
+ xmin=pic.scale.x.T(xmin);
+ newticks=true;
+ }
+
+ if(xmax != infinity) {
+ xmax=pic.scale.x.T(xmax);
+ newticks=true;
+ }
+
+ if(newticks && pic.userSetx() && ticks != NoTicks3) {
+ if(xmin == -infinity) xmin=pic.userMin().x;
+ if(xmax == infinity) xmax=pic.userMax().x;
+ bounds mx=autoscale(xmin,xmax,pic.scale.x.scale);
+ pic.scale.x.tickMin=mx.min;
+ pic.scale.x.tickMax=mx.max;
+ axis.xdivisor=mx.divisor;
+ }
+
+ axis(pic,axis);
+
+ if(xmin == -infinity && !axis.extend) {
+ if(pic.scale.set)
+ xmin=pic.scale.x.automin() ? pic.scale.x.tickMin :
+ max(pic.scale.x.tickMin,pic.userMin().x);
+ else xmin=pic.userMin().x;
+ }
+
+ if(xmax == infinity && !axis.extend) {
+ if(pic.scale.set)
+ xmax=pic.scale.x.automax() ? pic.scale.x.tickMax :
+ min(pic.scale.x.tickMax,pic.userMax().x);
+ else xmax=pic.userMax().x;
+ }
+
+ if(L.defaultposition) {
+ L=L.copy();
+ L.position(axis.position);
+ }
+
+ bool back=false;
+ if(axis.type == Both) {
+ triple v=currentprojection.normal;
+ back=dot((0,pic.userMax().y-pic.userMin().y,0),v)*sgn(v.z) > 0;
+ }
+
+ xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,false,false,!back);
+ if(axis.type == Both)
+ xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,true,false,back);
+ if(axis.type2 == Both) {
+ xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,false,true,false);
+ if(axis.type == Both)
+ xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,true,true,false);
+ }
+}
+
+// Draw a y axis in three dimensions.
+void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero,
+ real ymin=-infinity, real ymax=infinity, pen p=currentpen,
+ ticks3 ticks=NoTicks3,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=false)
+{
+ if(ymin > ymax) return;
+
+ if(pic.scale.y.automin && ymin > -infinity) pic.scale.y.automin=false;
+ if(pic.scale.y.automax && ymax < infinity) pic.scale.y.automax=false;
+
+ if(!pic.scale.set) {
+ axis(pic,axis);
+ autoscale3(pic,axis);
+ }
+
+ bool newticks=false;
+
+ if(ymin != -infinity) {
+ ymin=pic.scale.y.T(ymin);
+ newticks=true;
+ }
+
+ if(ymax != infinity) {
+ ymax=pic.scale.y.T(ymax);
+ newticks=true;
+ }
+
+ if(newticks && pic.userSety() && ticks != NoTicks3) {
+ if(ymin == -infinity) ymin=pic.userMin().y;
+ if(ymax == infinity) ymax=pic.userMax().y;
+ bounds my=autoscale(ymin,ymax,pic.scale.y.scale);
+ pic.scale.y.tickMin=my.min;
+ pic.scale.y.tickMax=my.max;
+ axis.ydivisor=my.divisor;
+ }
+
+ axis(pic,axis);
+
+ if(ymin == -infinity && !axis.extend) {
+ if(pic.scale.set)
+ ymin=pic.scale.y.automin() ? pic.scale.y.tickMin :
+ max(pic.scale.y.tickMin,pic.userMin().y);
+ else ymin=pic.userMin().y;
+ }
+
+
+ if(ymax == infinity && !axis.extend) {
+ if(pic.scale.set)
+ ymax=pic.scale.y.automax() ? pic.scale.y.tickMax :
+ min(pic.scale.y.tickMax,pic.userMax().y);
+ else ymax=pic.userMax().y;
+ }
+
+ if(L.defaultposition) {
+ L=L.copy();
+ L.position(axis.position);
+ }
+
+ bool back=false;
+ if(axis.type == Both) {
+ triple v=currentprojection.normal;
+ back=dot((pic.userMax().x-pic.userMin().x,0,0),v)*sgn(v.z) > 0;
+ }
+
+ yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,false,false,!back);
+
+ if(axis.type == Both)
+ yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,true,false,back);
+ if(axis.type2 == Both) {
+ yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,false,true,false);
+ if(axis.type == Both)
+ yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,true,true,false);
+ }
+}
+// Draw a z axis in three dimensions.
+void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero,
+ real zmin=-infinity, real zmax=infinity, pen p=currentpen,
+ ticks3 ticks=NoTicks3,
+ arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=false)
+{
+ if(zmin > zmax) return;
+
+ if(pic.scale.z.automin && zmin > -infinity) pic.scale.z.automin=false;
+ if(pic.scale.z.automax && zmax < infinity) pic.scale.z.automax=false;
+
+ if(!pic.scale.set) {
+ axis(pic,axis);
+ autoscale3(pic,axis);
+ }
+
+ bool newticks=false;
+
+ if(zmin != -infinity) {
+ zmin=pic.scale.z.T(zmin);
+ newticks=true;
+ }
+
+ if(zmax != infinity) {
+ zmax=pic.scale.z.T(zmax);
+ newticks=true;
+ }
+
+ if(newticks && pic.userSetz() && ticks != NoTicks3) {
+ if(zmin == -infinity) zmin=pic.userMin().z;
+ if(zmax == infinity) zmax=pic.userMax().z;
+ bounds mz=autoscale(zmin,zmax,pic.scale.z.scale);
+ pic.scale.z.tickMin=mz.min;
+ pic.scale.z.tickMax=mz.max;
+ axis.zdivisor=mz.divisor;
+ }
+
+ axis(pic,axis);
+
+ if(zmin == -infinity && !axis.extend) {
+ if(pic.scale.set)
+ zmin=pic.scale.z.automin() ? pic.scale.z.tickMin :
+ max(pic.scale.z.tickMin,pic.userMin().z);
+ else zmin=pic.userMin().z;
+ }
+
+ if(zmax == infinity && !axis.extend) {
+ if(pic.scale.set)
+ zmax=pic.scale.z.automax() ? pic.scale.z.tickMax :
+ min(pic.scale.z.tickMax,pic.userMax().z);
+ else zmax=pic.userMax().z;
+ }
+
+ if(L.defaultposition) {
+ L=L.copy();
+ L.position(axis.position);
+ }
+
+ bool back=false;
+ if(axis.type == Both) {
+ triple v=currentprojection.vector();
+ back=dot((pic.userMax().x-pic.userMin().x,0,0),v)*sgn(v.y) > 0;
+ }
+
+ zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,false,false,!back);
+ if(axis.type == Both)
+ zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,true,false,back);
+ if(axis.type2 == Both) {
+ zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,false,true,false);
+ if(axis.type == Both)
+ zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,true,true,false);
+ }
+}
+
+// Set the z limits of a picture.
+void zlimits(picture pic=currentpicture, real min=-infinity, real max=infinity,
+ bool crop=NoCrop)
+{
+ if(min > max) return;
+
+ pic.scale.z.automin=min <= -infinity;
+ pic.scale.z.automax=max >= infinity;
+
+ bounds mz;
+ if(pic.scale.z.automin() || pic.scale.z.automax())
+ mz=autoscale(pic.userMin().z,pic.userMax().z,pic.scale.z.scale);
+
+ if(pic.scale.z.automin) {
+ if(pic.scale.z.automin()) pic.userMinz(mz.min);
+ } else pic.userMinz(min(pic.scale.z.T(min),pic.scale.z.T(max)));
+
+ if(pic.scale.z.automax) {
+ if(pic.scale.z.automax()) pic.userMaxz(mz.max);
+ } else pic.userMaxz(max(pic.scale.z.T(min),pic.scale.z.T(max)));
+}
+
+// Restrict the x, y, and z limits to box(min,max).
+void limits(picture pic=currentpicture, triple min, triple max)
+{
+ xlimits(pic,min.x,max.x);
+ ylimits(pic,min.y,max.y);
+ zlimits(pic,min.z,max.z);
+}
+
+// Draw x, y and z axes.
+void axes3(picture pic=currentpicture,
+ Label xlabel="", Label ylabel="", Label zlabel="",
+ 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);
+}
+
+triple Scale(picture pic=currentpicture, triple v)
+{
+ return (pic.scale.x.T(v.x),pic.scale.y.T(v.y),pic.scale.z.T(v.z));
+}
+
+real ScaleZ(picture pic=currentpicture, real z)
+{
+ return pic.scale.z.T(z);
+}
+
+// Draw a tick of length size at triple v in direction dir using pen p.
+void tick(picture pic=currentpicture, triple v, triple dir, real size=Ticksize,
+ pen p=currentpen)
+{
+ triple v=Scale(pic,v);
+ pic.add(new void (picture f, transform3 t) {
+ triple tv=t*v;
+ draw(f,tv--tv+unit(dir)*size,p);
+ });
+ pic.addPoint(v,p);
+ pic.addPoint(v,unit(dir)*size,p);
+}
+
+void xtick(picture pic=currentpicture, triple v, triple dir=Y,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,v,dir,size,p);
+}
+
+void xtick3(picture pic=currentpicture, real x, triple dir=Y,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,(x,pic.scale.y.scale.logarithmic ? 1 : 0,
+ pic.scale.z.scale.logarithmic ? 1 : 0),dir,size,p);
+}
+
+void ytick(picture pic=currentpicture, triple v, triple dir=X,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,v,dir,size,p);
+}
+
+void ytick3(picture pic=currentpicture, real y, triple dir=X,
+ real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,(pic.scale.x.scale.logarithmic ? 1 : 0,y,
+ pic.scale.z.scale.logarithmic ? 1 : 0),dir,size,p);
+}
+
+void ztick(picture pic=currentpicture, triple v, triple dir=X,
+ real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,v,dir,size,p);
+}
+
+void ztick3(picture pic=currentpicture, real z, triple dir=X,
+ real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,(pic.scale.x.scale.logarithmic ? 1 : 0,
+ pic.scale.y.scale.logarithmic ? 1 : 0,z),dir,size,p);
+}
+
+void tick(picture pic=currentpicture, Label L, real value, triple v,
+ triple dir, string format="", real size=Ticksize, pen p=currentpen)
+{
+ Label L=L.copy();
+ L.align(L.align,-dir);
+ if(shift(L.T3)*O == O)
+ L.T3=shift(dot(dir,L.align.dir3) > 0 ? dir*size :
+ ticklabelshift(L.align.dir3,p))*L.T3;
+ L.p(p);
+ if(L.s == "") L.s=format(format == "" ? defaultformat : format,value);
+ L.s=baseline(L.s,baselinetemplate);
+ label(pic,L,Scale(pic,v));
+ tick(pic,v,dir,size,p);
+}
+
+void xtick(picture pic=currentpicture, Label L, triple v, triple dir=Y,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,L,v.x,v,dir,format,size,p);
+}
+
+void xtick3(picture pic=currentpicture, Label L, real x, triple dir=Y,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0,
+ pic.scale.z.scale.logarithmic ? 1 : 0),dir,size,p);
+}
+
+void ytick(picture pic=currentpicture, Label L, triple v, triple dir=X,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,L,v.y,v,dir,format,size,p);
+}
+
+void ytick3(picture pic=currentpicture, Label L, real y, triple dir=X,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y,
+ pic.scale.z.scale.logarithmic ? 1 : 0),dir,format,size,p);
+}
+
+void ztick(picture pic=currentpicture, Label L, triple v, triple dir=X,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ tick(pic,L,v.z,v,dir,format,size,p);
+}
+
+void ztick3(picture pic=currentpicture, Label L, real z, triple dir=X,
+ string format="", real size=Ticksize, pen p=currentpen)
+{
+ xtick(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,
+ pic.scale.z.scale.logarithmic ? 1 : 0,z),dir,format,size,p);
+}
+
+private void label(picture pic, Label L, triple v, real x, align align,
+ string format, pen p)
+{
+ Label L=L.copy();
+ L.align(align);
+ L.p(p);
+ if(shift(L.T3)*O == O)
+ L.T3=shift(ticklabelshift(L.align.dir3,L.p))*L.T3;
+ if(L.s == "") L.s=format(format == "" ? defaultformat : format,x);
+ L.s=baseline(L.s,baselinetemplate);
+ label(pic,L,v);
+}
+
+void labelx(picture pic=currentpicture, Label L="", triple v,
+ align align=-Y, string format="", pen p=currentpen)
+{
+ label(pic,L,Scale(pic,v),v.x,align,format,p);
+}
+
+void labelx3(picture pic=currentpicture, Label L="", real x,
+ align align=-Y, string format="", pen p=currentpen)
+{
+ labelx(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0,
+ pic.scale.z.scale.logarithmic ? 1 : 0),align,format,p);
+}
+
+void labely(picture pic=currentpicture, Label L="", triple v,
+ align align=-X, string format="", pen p=currentpen)
+{
+ label(pic,L,Scale(pic,v),v.y,align,format,p);
+}
+
+void labely3(picture pic=currentpicture, Label L="", real y,
+ align align=-X, string format="", pen p=currentpen)
+{
+ labely(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y,
+ pic.scale.z.scale.logarithmic ? 1 : 0),align,format,p);
+}
+
+void labelz(picture pic=currentpicture, Label L="", triple v,
+ align align=-X, string format="", pen p=currentpen)
+{
+ label(pic,L,Scale(pic,v),v.z,align,format,p);
+}
+
+void labelz3(picture pic=currentpicture, Label L="", real z,
+ align align=-X, string format="", pen p=currentpen)
+{
+ labelz(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,
+ pic.scale.y.scale.logarithmic ? 1 : 0,z),align,format,p);
+}
+
+typedef guide3 graph(triple F(real), real, real, int);
+typedef guide3[] multigraph(triple F(real), real, real, int);
+
+graph graph(interpolate3 join)
+{
+ return new guide3(triple f(real), real a, real b, int n) {
+ real width=b-a;
+ return n == 0 ? join(f(a)) :
+ join(...sequence(new guide3(int i) {return f(a+(i/n)*width);},n+1));
+ };
+}
+
+multigraph graph(interpolate3 join, bool3 cond(real))
+{
+ return new guide3[](triple f(real), real a, real b, int n) {
+ real width=b-a;
+ if(n == 0) return new guide3[] {join(cond(a) ? f(a) : nullpath3)};
+ guide3[] G;
+ guide3[] g;
+ for(int i=0; i < n+1; ++i) {
+ real t=a+(i/n)*width;
+ bool3 b=cond(t);
+ if(b)
+ g.push(f(t));
+ else {
+ if(g.length > 0) {
+ G.push(join(...g));
+ g=new guide3[] {};
+ }
+ if(b == default)
+ g.push(f(t));
+ }
+ }
+ if(g.length > 0)
+ G.push(join(...g));
+ return G;
+ };
+}
+
+guide3 Straight(... guide3[])=operator --;
+guide3 Spline(... guide3[])=operator ..;
+
+guide3 graph(picture pic=currentpicture, real x(real), real y(real),
+ real z(real), real a, real b, int n=ngraph,
+ interpolate3 join=operator --)
+{
+ return graph(join)(new triple(real t) {return Scale(pic,(x(t),y(t),z(t)));},
+ a,b,n);
+}
+
+guide3[] graph(picture pic=currentpicture, real x(real), real y(real),
+ real z(real), real a, real b, int n=ngraph,
+ bool3 cond(real), interpolate3 join=operator --)
+{
+ return graph(join,cond)(new triple(real t) {
+ return Scale(pic,(x(t),y(t),z(t)));
+ },a,b,n);
+}
+
+guide3 graph(picture pic=currentpicture, triple v(real), real a, real b,
+ int n=ngraph, interpolate3 join=operator --)
+{
+ return graph(join)(new triple(real t) {return Scale(pic,v(t));},a,b,n);
+}
+
+guide3[] graph(picture pic=currentpicture, triple v(real), real a, real b,
+ int n=ngraph, bool3 cond(real), interpolate3 join=operator --)
+{
+ return graph(join,cond)(new triple(real t) {
+ return Scale(pic,v(t));
+ },a,b,n);
+}
+
+guide3 graph(picture pic=currentpicture, triple[] v,
+ interpolate3 join=operator --)
+{
+ int i=0;
+ return graph(join)(new triple(real) {
+ triple w=Scale(pic,v[i]);
+ ++i;
+ return w;
+ },0,0,v.length-1);
+}
+
+guide3[] graph(picture pic=currentpicture, triple[] v, bool3[] cond,
+ interpolate3 join=operator --)
+{
+ int n=v.length;
+ int i=0;
+ triple w;
+ checkconditionlength(cond.length,n);
+ bool3 condition(real) {
+ bool b=cond[i];
+ if(b) w=Scale(pic,v[i]);
+ ++i;
+ return b;
+ }
+ return graph(join,condition)(new triple(real) {return w;},0,0,n-1);
+}
+
+guide3 graph(picture pic=currentpicture, real[] x, real[] y, real[] z,
+ interpolate3 join=operator --)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checklengths(n,z.length);
+ int i=0;
+ return graph(join)(new triple(real) {
+ triple w=Scale(pic,(x[i],y[i],z[i]));
+ ++i;
+ return w;
+ },0,0,n-1);
+}
+
+guide3[] graph(picture pic=currentpicture, real[] x, real[] y, real[] z,
+ bool3[] cond, interpolate3 join=operator --)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checklengths(n,z.length);
+ int i=0;
+ triple w;
+ checkconditionlength(cond.length,n);
+ bool3 condition(real) {
+ bool3 b=cond[i];
+ if(b != false) w=Scale(pic,(x[i],y[i],z[i]));
+ ++i;
+ return b;
+ }
+ return graph(join,condition)(new triple(real) {return w;},0,0,n-1);
+}
+
+// The graph of a function along a path.
+guide3 graph(triple F(path, real), path p, int n=1,
+ interpolate3 join=operator --)
+{
+ guide3 g=join(...sequence(new guide3(int i) {
+ return F(p,i/n);
+ },n*length(p)));
+ return cyclic(p) ? join(g,cycle) : join(g,F(p,length(p)));
+}
+
+guide3 graph(triple F(pair), path p, int n=1, interpolate3 join=operator --)
+{
+ return graph(new triple(path p, real position)
+ {return F(point(p,position));},p,n,join);
+}
+
+guide3 graph(picture pic=currentpicture, real f(pair), path p, int n=1,
+ interpolate3 join=operator --)
+{
+ return graph(new triple(pair z) {return Scale(pic,(z.x,z.y,f(z)));},p,n,
+ join);
+}
+
+guide3 graph(real f(pair), path p, int n=1, real T(pair),
+ interpolate3 join=operator --)
+{
+ return graph(new triple(pair z) {pair w=T(z); return (w.x,w.y,f(w));},p,n,
+ join);
+}
+
+// Connect points in v into segments corresponding to consecutive true elements
+// of b using interpolation operator join.
+path3[] segment(triple[] v, bool[] cond, interpolate3 join=operator --)
+{
+ checkconditionlength(cond.length,v.length);
+ int[][] segment=segment(cond);
+ return sequence(new path3(int i) {return join(...v[segment[i]]);},
+ segment.length);
+}
+
+bool uperiodic(triple[][] a) {
+ int n=a.length;
+ if(n == 0) return false;
+ int m=a[0].length;
+ triple[] a0=a[0];
+ triple[] a1=a[n-1];
+ real epsilon=sqrtEpsilon*norm(a);
+ for(int j=0; j < m; ++j)
+ if(abs(a0[j]-a1[j]) > epsilon) return false;
+ return true;
+}
+bool vperiodic(triple[][] a) {
+ int n=a.length;
+ if(n == 0) return false;
+ int m=a[0].length-1;
+ real epsilon=sqrtEpsilon*norm(a);
+ for(int i=0; i < n; ++i)
+ if(abs(a[i][0]-a[i][m]) > epsilon) return false;
+ return true;
+}
+
+// return the surface described by a matrix f
+surface surface(triple[][] f, bool[][] cond={})
+{
+ if(!rectangular(f)) abort("matrix is not rectangular");
+
+ int nx=f.length-1;
+ int ny=nx > 0 ? f[0].length-1 : 0;
+
+ bool all=cond.length == 0;
+
+ int count;
+ if(all)
+ count=nx*ny;
+ else {
+ count=0;
+ for(int i=0; i < nx; ++i) {
+ bool[] condi=cond[i];
+ bool[] condp=cond[i+1];
+ for(int j=0; j < ny; ++j)
+ if(condi[j] && condi[j+1] && condp[j] && condp[j+1]) ++count;
+ }
+ }
+
+ surface s=surface(count);
+ s.index=new int[nx][ny];
+ int k=-1;
+ for(int i=0; i < nx; ++i) {
+ bool[] condi,condp;
+ if(!all) {
+ condi=cond[i];
+ condp=cond[i+1];
+ }
+ triple[] fi=f[i];
+ triple[] fp=f[i+1];
+ int[] indexi=s.index[i];
+ for(int j=0; j < ny; ++j) {
+ if(all || (condi[j] && condi[j+1] && condp[j] && condp[j+1]))
+ s.s[++k]=patch(new triple[] {fi[j],fp[j],fp[j+1],fi[j+1]});
+ indexi[j]=k;
+ }
+ }
+
+ if(count == nx*ny) {
+ if(uperiodic(f)) s.ucyclic(true);
+ if(vperiodic(f)) s.vcyclic(true);
+ }
+
+ return s;
+}
+
+surface bispline(real[][] z, real[][] p, real[][] q, real[][] r,
+ real[] x, real[] y, bool[][] cond={})
+{ // z[i][j] is the value at (x[i],y[j])
+ // p and q are the first derivatives with respect to x and y, respectively
+ // r is the second derivative ddu/dxdy
+ int n=x.length-1;
+ int m=y.length-1;
+
+ bool all=cond.length == 0;
+
+ int count;
+ if(all)
+ count=n*m;
+ else {
+ count=0;
+ for(int i=0; i < n; ++i) {
+ bool[] condi=cond[i];
+ for(int j=0; j < m; ++j)
+ if(condi[j]) ++count;
+ }
+ }
+
+ surface s=surface(count);
+ s.index=new int[n][m];
+ int k=0;
+ for(int i=0; i < n; ++i) {
+ int ip=i+1;
+ real xi=x[i];
+ real xp=x[ip];
+ real x1=interp(xi,xp,1/3);
+ real x2=interp(xi,xp,2/3);
+ real hx=x1-xi;
+ real[] zi=z[i];
+ real[] zp=z[ip];
+ real[] ri=r[i];
+ real[] rp=r[ip];
+ real[] pi=p[i];
+ real[] pp=p[ip];
+ real[] qi=q[i];
+ real[] qp=q[ip];
+ int[] indexi=s.index[i];
+ bool[] condi=all ? null : cond[i];
+ for(int j=0; j < m; ++j) {
+ if(all || condi[j]) {
+ real yj=y[j];
+ int jp=j+1;
+ real yp=y[jp];
+ real y1=interp(yj,yp,1/3);
+ real y2=interp(yj,yp,2/3);
+ real hy=y1-yj;
+ real hxy=hx*hy;
+ real zij=zi[j];
+ real zip=zi[jp];
+ real zpj=zp[j];
+ real zpp=zp[jp];
+ real pij=hx*pi[j];
+ real ppj=hx*pp[j];
+ real qip=hy*qi[jp];
+ real qpp=hy*qp[jp];
+ real zippip=zip+hx*pi[jp];
+ real zppmppp=zpp-hx*pp[jp];
+ real zijqij=zij+hy*qi[j];
+ real zpjqpj=zpj+hy*qp[j];
+
+ s.s[k]=patch(new triple[][] {
+ {(xi,yj,zij),(xi,y1,zijqij),(xi,y2,zip-qip),(xi,yp,zip)},
+ {(x1,yj,zij+pij),(x1,y1,zijqij+pij+hxy*ri[j]),
+ (x1,y2,zippip-qip-hxy*ri[jp]),(x1,yp,zippip)},
+ {(x2,yj,zpj-ppj),(x2,y1,zpjqpj-ppj-hxy*rp[j]),
+ (x2,y2,zppmppp-qpp+hxy*rp[jp]),(x2,yp,zppmppp)},
+ {(xp,yj,zpj),(xp,y1,zpjqpj),(xp,y2,zpp-qpp),(xp,yp,zpp)}},copy=false);
+ indexi[j]=k;
+ ++k;
+ }
+ }
+ }
+
+ return s;
+}
+
+// return the surface described by a real matrix f, interpolated with
+// xsplinetype and ysplinetype.
+surface surface(real[][] f, real[] x, real[] y,
+ splinetype xsplinetype=null, splinetype ysplinetype=xsplinetype,
+ bool[][] cond={})
+{
+ real epsilon=sqrtEpsilon*norm(y);
+ if(xsplinetype == null)
+ xsplinetype=(abs(x[0]-x[x.length-1]) <= epsilon) ? periodic : notaknot;
+ if(ysplinetype == null)
+ ysplinetype=(abs(y[0]-y[y.length-1]) <= epsilon) ? periodic : notaknot;
+ int n=x.length; int m=y.length;
+ real[][] ft=transpose(f);
+ real[][] tp=new real[m][];
+ for(int j=0; j < m; ++j)
+ tp[j]=xsplinetype(x,ft[j]);
+ real[][] q=new real[n][];
+ for(int i=0; i < n; ++i)
+ q[i]=ysplinetype(y,f[i]);
+ real[][] qt=transpose(q);
+ real[] d1=xsplinetype(x,qt[0]);
+ real[] d2=xsplinetype(x,qt[m-1]);
+ real[][] r=new real[n][];
+ real[][] p=transpose(tp);
+ for(int i=0; i < n; ++i)
+ r[i]=clamped(d1[i],d2[i])(y,p[i]);
+ surface s=bispline(f,p,q,r,x,y,cond);
+ if(xsplinetype == periodic) s.ucyclic(true);
+ if(ysplinetype == periodic) s.vcyclic(true);
+ return s;
+}
+
+// return the surface described by a real matrix f, interpolated with
+// xsplinetype and ysplinetype.
+surface surface(real[][] f, pair a, pair b, splinetype xsplinetype,
+ splinetype ysplinetype=xsplinetype, bool[][] cond={})
+{
+ if(!rectangular(f)) abort("matrix is not rectangular");
+
+ int nx=f.length-1;
+ int ny=nx > 0 ? f[0].length-1 : 0;
+
+ if(nx == 0 || ny == 0) return nullsurface;
+
+ real[] x=uniform(a.x,b.x,nx);
+ real[] y=uniform(a.y,b.y,ny);
+ return surface(f,x,y,xsplinetype,ysplinetype,cond);
+}
+
+// return the surface described by a real matrix f, interpolated linearly.
+surface surface(real[][] f, pair a, pair b, bool[][] cond={})
+{
+ if(!rectangular(f)) abort("matrix is not rectangular");
+
+ int nx=f.length-1;
+ int ny=nx > 0 ? f[0].length-1 : 0;
+
+ if(nx == 0 || ny == 0) return nullsurface;
+
+ bool all=cond.length == 0;
+
+ triple[][] v=new triple[nx+1][ny+1];
+ for(int i=0; i <= nx; ++i) {
+ real x=interp(a.x,b.x,i/nx);
+ bool[] condi=all ? null : cond[i];
+ triple[] vi=v[i];
+ real[] fi=f[i];
+ for(int j=0; j <= ny; ++j)
+ if(all || condi[j])
+ vi[j]=(x,interp(a.y,b.y,j/ny),fi[j]);
+ }
+ return surface(v,cond);
+}
+
+// return the surface described by a parametric function f over box(a,b),
+// interpolated linearly.
+surface surface(triple f(pair z), pair a, pair b, int nu=nmesh, int nv=nu,
+ bool cond(pair z)=null)
+{
+ if(nu <= 0 || nv <= 0) return nullsurface;
+
+ bool[][] active;
+ bool all=cond == null;
+ if(!all) active=new bool[nu+1][nv+1];
+
+ real du=1/nu;
+ real dv=1/nv;
+ pair Idv=(0,dv);
+ pair dz=(du,dv);
+
+ triple[][] v=new triple[nu+1][nv+1];
+
+ for(int i=0; i <= nu; ++i) {
+ real x=interp(a.x,b.x,i*du);
+ bool[] activei=all ? null : active[i];
+ triple[] vi=v[i];
+ for(int j=0; j <= nv; ++j) {
+ pair z=(x,interp(a.y,b.y,j*dv));
+ if(all || (activei[j]=cond(z))) vi[j]=f(z);
+ }
+ }
+ return surface(v,active);
+}
+
+// return the surface described by a parametric function f over box(a,b),
+// interpolated with usplinetype and vsplinetype.
+surface surface(triple f(pair z), pair a, pair b, int nu=nmesh, int nv=nu,
+ splinetype[] usplinetype, splinetype[] vsplinetype=Spline,
+ bool cond(pair z)=null)
+{
+ return surface(f,uniform(a.x,b.x,nu),uniform(a.y,b.y,nv),
+ usplinetype,vsplinetype,cond);
+}
+
+// return the surface described by a real function f over box(a,b),
+// interpolated linearly.
+surface surface(real f(pair z), pair a, pair b, int nx=nmesh, int ny=nx,
+ bool cond(pair z)=null)
+{
+ return surface(new triple(pair z) {return (z.x,z.y,f(z));},a,b,nx,ny,cond);
+}
+
+// return the surface described by a real function f over box(a,b),
+// interpolated with xsplinetype and ysplinetype.
+surface surface(real f(pair z), pair a, pair b, int nx=nmesh, int ny=nx,
+ splinetype xsplinetype, splinetype ysplinetype=xsplinetype,
+ bool cond(pair z)=null)
+{
+ bool[][] active;
+ bool all=cond == null;
+ if(!all) active=new bool[nx+1][ny+1];
+
+ real dx=1/nx;
+ real dy=1/ny;
+ pair Idy=(0,dy);
+ pair dz=(dx,dy);
+
+ real[][] F=new real[nx+1][ny+1];
+ real[] x=uniform(a.x,b.x,nx);
+ real[] y=uniform(a.y,b.y,ny);
+ for(int i=0; i <= nx; ++i) {
+ bool[] activei=all ? null : active[i];
+ real[] Fi=F[i];
+ real x=x[i];
+ for(int j=0; j <= ny; ++j) {
+ pair z=(x,y[j]);
+ Fi[j]=f(z);
+ if(!all) activei[j]=cond(z);
+ }
+ }
+ return surface(F,x,y,xsplinetype,ysplinetype,active);
+}
+
+guide3[][] lift(real f(real x, real y), guide[][] g,
+ interpolate3 join=operator --)
+{
+ guide3[][] G=new guide3[g.length][];
+ for(int cnt=0; cnt < g.length; ++cnt) {
+ guide[] gcnt=g[cnt];
+ guide3[] Gcnt=new guide3[gcnt.length];
+ for(int i=0; i < gcnt.length; ++i) {
+ guide gcnti=gcnt[i];
+ guide3 Gcnti=join(...sequence(new guide3(int j) {
+ pair z=point(gcnti,j);
+ return (z.x,z.y,f(z.x,z.y));
+ },size(gcnti)));
+ if(cyclic(gcnti)) Gcnti=Gcnti..cycle;
+ Gcnt[i]=Gcnti;
+ }
+ G[cnt]=Gcnt;
+ }
+ return G;
+}
+
+guide3[][] lift(real f(pair z), guide[][] g, interpolate3 join=operator --)
+{
+ return lift(new real(real x, real y) {return f((x,y));},g,join);
+}
+
+void draw(picture pic=currentpicture, Label[] L=new Label[],
+ guide3[][] g, pen[] p, light light=currentlight, string name="",
+ render render=defaultrender,
+ interaction interaction=LabelInteraction())
+{
+ pen thin=is3D() ? thin() : defaultpen;
+ if(g.length > 1)
+ begingroup3(pic,name == "" ? "contours" : name,render);
+ for(int cnt=0; cnt < g.length; ++cnt) {
+ guide3[] gcnt=g[cnt];
+ pen pcnt=thin+p[cnt];
+ for(int i=0; i < gcnt.length; ++i)
+ draw(pic,gcnt[i],pcnt,light,name);
+ if(L.length > 0) {
+ Label Lcnt=L[cnt];
+ for(int i=0; i < gcnt.length; ++i) {
+ if(Lcnt.s != "" && size(gcnt[i]) > 1)
+ label(pic,Lcnt,gcnt[i],pcnt,name,interaction);
+ }
+ }
+ }
+ if(g.length > 1)
+ endgroup3(pic);
+}
+
+void draw(picture pic=currentpicture, Label[] L=new Label[],
+ guide3[][] g, pen p=currentpen, light light=currentlight,
+ string name="", render render=defaultrender,
+ interaction interaction=LabelInteraction())
+{
+ draw(pic,L,g,sequence(new pen(int) {return p;},g.length),light,name,
+ render,interaction);
+}
+
+real maxlength(triple f(pair z), pair a, pair b, int nu, int nv)
+{
+ return min(abs(f((b.x,a.y))-f(a))/nu,abs(f((a.x,b.y))-f(a))/nv);
+}
+
+// return a vector field on a parametric surface f over box(a,b).
+picture vectorfield(path3 vector(pair v), triple f(pair z), pair a, pair b,
+ int nu=nmesh, int nv=nu, bool truesize=false,
+ real maxlength=truesize ? 0 : maxlength(f,a,b,nu,nv),
+ bool cond(pair z)=null, pen p=currentpen,
+ arrowbar3 arrow=Arrow3, margin3 margin=PenMargin3,
+ string name="", render render=defaultrender)
+{
+ picture pic;
+ real du=1/nu;
+ real dv=1/nv;
+ bool all=cond == null;
+ real scale;
+
+ if(maxlength > 0) {
+ real size(pair z) {
+ path3 g=vector(z);
+ return abs(point(g,size(g)-1)-point(g,0));
+ }
+ real max=size((0,0));
+ for(int i=0; i <= nu; ++i) {
+ real x=interp(a.x,b.x,i*du);
+ for(int j=0; j <= nv; ++j)
+ max=max(max,size((x,interp(a.y,b.y,j*dv))));
+ }
+ scale=max > 0 ? maxlength/max : 1;
+ } else scale=1;
+
+ 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) {
+ pair z=(x,interp(a.y,b.y,j*dv));
+ if(all || cond(z)) {
+ path3 g=scale3(scale)*vector(z);
+ string name="vector";
+ if(truesize) {
+ picture opic;
+ draw(opic,g,p,arrow,margin,name,render);
+ add(pic,opic,f(z));
+ } else
+ draw(pic,shift(f(z))*g,p,arrow,margin,name,render);
+ }
+ }
+ }
+ endgroup3(pic);
+ return pic;
+}
+
+triple polar(real r, real theta, real phi)
+{
+ return r*expi(theta,phi);
+}
+
+guide3 polargraph(real r(real,real), real theta(real), real phi(real),
+ int n=ngraph, interpolate3 join=operator --)
+{
+ return graph(join)(new triple(real t) {
+ return polar(r(theta(t),phi(t)),theta(t),phi(t));
+ },0,1,n);
+}
+
+// True arc
+path3 Arc(triple c, triple v1, triple v2, triple normal=O, bool direction=CCW,
+ int n=nCircle)
+{
+ v1 -= c;
+ real r=abs(v1);
+ v1=unit(v1);
+ v2=unit(v2-c);
+
+ if(normal == O) {
+ normal=cross(v1,v2);
+ if(normal == O) abort("explicit normal required for these endpoints");
+ }
+
+ transform3 T=align(unit(normal));
+ transform3 Tinv=transpose(T);
+ v1=Tinv*v1;
+ v2=Tinv*v2;
+
+ real fuzz=sqrtEpsilon*max(abs(v1),abs(v2));
+ if(abs(v1.z) > fuzz || abs(v2.z) > fuzz)
+ abort("invalid normal vector");
+
+ real phi1=radians(longitude(v1,warn=false));
+ real phi2=radians(longitude(v2,warn=false));
+ if(direction) {
+ if(phi1 >= phi2) phi1 -= 2pi;
+ } else if(phi2 >= phi1) phi2 -= 2pi;
+
+ static real piby2=pi/2;
+ return shift(c)*T*polargraph(new real(real theta, real phi) {return r;},
+ new real(real t) {return piby2;},
+ new real(real t) {return interp(phi1,phi2,t);},
+ n,operator ..);
+}
+
+path3 Arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
+ triple normal=O, bool direction, int n=nCircle)
+{
+ return Arc(c,c+r*dir(theta1,phi1),c+r*dir(theta2,phi2),normal,direction,n);
+}
+
+path3 Arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
+ triple normal=O, int n=nCircle)
+{
+ return Arc(c,r,theta1,phi1,theta2,phi2,normal,
+ theta2 > theta1 || (theta2 == theta1 && phi2 >= phi1) ? CCW : CW,
+ n);
+}
+
+// True circle
+path3 Circle(triple c, real r, triple normal=Z, int n=nCircle)
+{
+ static real piby2=pi/2;
+ return shift(c)*align(unit(normal))*
+ polargraph(new real(real theta, real phi) {return r;},
+ new real(real t) {return piby2;},
+ new real(real t) {return interp(0,2pi,t);},n,operator ..);
+
+}
diff --git a/Master/texmf-dist/asymptote/graph_settings.asy b/Master/texmf-dist/asymptote/graph_settings.asy
new file mode 100644
index 00000000000..f5d04d0797b
--- /dev/null
+++ b/Master/texmf-dist/asymptote/graph_settings.asy
@@ -0,0 +1,18 @@
+// Number of function samples.
+int ngraph=100;
+int nCircle=400;
+
+// Number of mesh intervals.
+int nmesh=10;
+
+real ticksize=1mm;
+real Ticksize=2*ticksize;
+real ylabelwidth=2.0;
+real axislabelfactor=1.5;
+real axiscoverage=0.8;
+
+real epsilon=10*realEpsilon;
+
+restricted bool Crop=true;
+restricted bool NoCrop=false;
+
diff --git a/Master/texmf-dist/asymptote/graph_splinetype.asy b/Master/texmf-dist/asymptote/graph_splinetype.asy
new file mode 100644
index 00000000000..02f780ca02e
--- /dev/null
+++ b/Master/texmf-dist/asymptote/graph_splinetype.asy
@@ -0,0 +1,264 @@
+private import math;
+
+typedef real[] splinetype(real[], real[]);
+
+restricted real[] Spline(real[] x, real[] y);
+restricted splinetype[] Spline;
+
+string morepoints="interpolation requires at least 2 points";
+string differentlengths="arrays have different lengths";
+void checklengths(int x, int y, string text=differentlengths)
+{
+ if(x != y)
+ abort(text+": "+string(x)+" != "+string(y));
+}
+
+void checkincreasing(real[] x)
+{
+ if(!increasing(x,true))
+ abort("strictly increasing array expected");
+}
+
+// Linear interpolation
+real[] linear(real[] x, real[] y)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ real[] d=new real[n];
+ for(int i=0; i < n-1; ++i)
+ d[i]=(y[i+1]-y[i])/(x[i+1]-x[i]);
+ d[n-1]=d[n-2];
+ return d;
+}
+
+// Standard cubic spline interpolation with not-a-knot condition:
+// s'''(x_2^-)=s'''(x_2^+) et s'''(x_(n_2)^-)=s'''(x_(n-2)^+)
+// if n=2, linear interpolation is returned
+// if n=3, an interpolation polynomial of degree <= 2 is returned:
+// p(x_1)=y_1, p(x_2)=y_2, p(x_3)=y_3
+real[] notaknot(real[] x, real[] y)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checkincreasing(x);
+ real[] d;
+ if(n > 3) {
+ real[] a=new real[n];
+ real[] b=new real[n];
+ real[] c=new real[n];
+ real[] g=new real[n];
+ b[0]=x[2]-x[1];
+ c[0]=x[2]-x[0];
+ a[0]=0;
+ g[0]=((x[1]-x[0])^2*(y[2]-y[1])/b[0]+b[0]*(2*b[0]+3*(x[1]-x[0]))*
+ (y[1]-y[0])/(x[1]-x[0]))/c[0];
+ for(int i=1; i < n-1; ++i) {
+ a[i]=x[i+1]-x[i];
+ c[i]=x[i]-x[i-1];
+ b[i]=2*(a[i]+c[i]);
+ g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]);
+ }
+ c[n-1]=0;
+ b[n-1]=x[n-2]-x[n-3];
+ a[n-1]=x[n-1]-x[n-3];
+ g[n-1]=((x[n-1]-x[n-2])^2*(y[n-2]-y[n-3])/b[n-1]+
+ b[n-1]*(2*b[n-1]+3(x[n-1]-x[n-2]))*
+ (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))/a[n-1];
+ d=tridiagonal(a,b,c,g);
+ } else if(n == 2) {
+ real val=(y[1]-y[0])/(x[1]-x[0]);
+ d=new real[] {val,val};
+ } else if(n == 3) {
+ real a=(y[1]-y[0])/(x[1]-x[0]);
+ real b=(y[2]-y[1])/(x[2]-x[1]);
+ real c=(b-a)/(x[2]-x[0]);
+ d=new real[] {a+c*(x[0]-x[1]),a+c*(x[1]-x[0]),a+c*(2*x[2]-x[0]-x[1])};
+ } else abort(morepoints);
+ return d;
+}
+
+// Standard cubic spline interpolation with periodic condition
+// s'(a)=s'(b), s''(a)=s''(b), assuming that f(a)=f(b)
+// if n=2, linear interpolation is returned
+real[] periodic(real[] x, real[] y)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checkincreasing(x);
+ if(abs(y[n-1]-y[0]) > sqrtEpsilon*norm(y))
+ abort("function values are not periodic");
+ real[] d;
+ if(n > 2) {
+ real[] a=new real[n-1];
+ real[] b=new real[n-1];
+ real[] c=new real[n-1];
+ real[] g=new real[n-1];
+ c[0]=x[n-1]-x[n-2];
+ a[0]=x[1]-x[0];
+ b[0]=2*(a[0]+c[0]);
+ g[0]=3*c[0]*(y[1]-y[0])/a[0]+3*a[0]*(y[n-1]-y[n-2])/c[0];
+ for(int i=1; i < n-1; ++i) {
+ a[i]=x[i+1]-x[i];
+ c[i]=x[i]-x[i-1];
+ b[i]=2*(a[i]+c[i]);
+ g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]);
+ }
+ d=tridiagonal(a,b,c,g);
+ d.push(d[0]);
+ } else if(n == 2) {
+ d=new real[] {0,0};
+ } else abort(morepoints);
+ return d;
+}
+
+// Standard cubic spline interpolation with the natural condition
+// s''(a)=s''(b)=0.
+// if n=2, linear interpolation is returned
+// Don't use the natural type unless the underlying function
+// has zero second end points derivatives.
+real[] natural(real[] x, real[] y)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checkincreasing(x);
+ real[] d;
+ if(n > 2) {
+ real[] a=new real[n];
+ real[] b=new real[n];
+ real[] c=new real[n];
+ real[] g=new real[n];
+ b[0]=2*(x[1]-x[0]);
+ c[0]=x[1]-x[0];
+ a[0]=0;
+ g[0]=3*(y[1]-y[0]);
+ for(int i=1; i < n-1; ++i) {
+ a[i]=x[i+1]-x[i];
+ c[i]=x[i]-x[i-1];
+ b[i]=2*(a[i]+c[i]);
+ g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]);
+ }
+ c[n-1]=0;
+ a[n-1]=x[n-1]-x[n-2];
+ b[n-1]=2*a[n-1];
+ g[n-1]=3*(y[n-1]-y[n-2]);
+ d=tridiagonal(a,b,c,g);
+ } else if(n == 2) {
+ real val=(y[1]-y[0])/(x[1]-x[0]);
+ d=new real[] {val,val};
+ } else abort(morepoints);
+ return d;
+}
+
+// Standard cubic spline interpolation with clamped conditions f'(a), f'(b)
+splinetype clamped(real slopea, real slopeb)
+{
+ return new real[] (real[] x, real[] y) {
+ int n=x.length;
+ checklengths(n,y.length);
+ checkincreasing(x);
+ real[] d;
+ if(n > 2) {
+ real[] a=new real[n];
+ real[] b=new real[n];
+ real[] c=new real[n];
+ real[] g=new real[n];
+ b[0]=x[1]-x[0];
+ g[0]=b[0]*slopea;
+ c[0]=0;
+ a[0]=0;
+ for(int i=1; i < n-1; ++i) {
+ a[i]=x[i+1]-x[i];
+ c[i]=x[i]-x[i-1];
+ b[i]=2*(a[i]+c[i]);
+ g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]);
+ }
+ c[n-1]=0;
+ a[n-1]=0;
+ b[n-1]=x[n-1]-x[n-2];
+ g[n-1]=b[n-1]*slopeb;
+ d=tridiagonal(a,b,c,g);
+ } else if(n == 2) {
+ d=new real[] {slopea,slopeb};
+ } else abort(morepoints);
+ return d;
+ };
+}
+
+// Piecewise Cubic Hermite Interpolating Polynomial (PCHIP)
+// Modified MATLAB code
+// [1] Fritsch, F. N. and R. E. Carlson,
+// "Monotone Piecewise Cubic Interpolation,"
+// SIAM J. Numerical Analysis, Vol. 17, 1980, pp.238-246.
+// [2] Kahaner, David, Cleve Moler, Stephen Nash,
+// Numerical Methods and Software, Prentice Hall, 1988.
+real[] monotonic(real[] x, real[] y)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checkincreasing(x);
+ real[] d=new real[n];
+ if(n > 2) {
+ real[] h=new real[n-1];
+ real[] del=new real[n-1];
+ for(int i=0; i < n-1; ++i) {
+ h[i]=x[i+1]-x[i];
+ del[i]=(y[i+1]-y[i])/h[i];
+ }
+ int j=0;
+ int k[]=new int[];
+ for(int i=0; i < n-2; ++i)
+ if((sgn(del[i])*sgn(del[i+1])) > 0) {k[j]=i; j=j+1;}
+
+ real[] hs=new real[j];
+ for(int i=0; i < j; ++i) hs[i]=h[k[i]]+h[k[i]+1];
+ real w1[]=new real[j];
+ real w2[]=new real[j];
+ real dmax[]=new real[j];
+ real dmin[]=new real[j];
+ for(int i=0; i < j; ++i) {
+ w1[i]=(h[k[i]]+hs[i])/(3*hs[i]);
+ w2[i]=(h[k[i]+1]+hs[i])/(3*hs[i]);
+ dmax[i]=max(abs(del[k[i]]),abs(del[k[i]+1]));
+ dmin[i]=min(abs(del[k[i]]),abs(del[k[i]+1]));
+ }
+ for(int i=0; i < n; ++i) d[i]=0;
+ for(int i=0; i < j; ++i)
+ d[k[i]+1]=dmin[i]/(w1[i]*(del[k[i]]/dmax[i])+w2[i]*(del[k[i]+1]/dmax[i]));
+ d[0]=((2*h[0]+h[1])*del[0]-h[0]*del[1])/(h[0]+h[1]);
+ if(sgn(d[0]) != sgn(del[0])) {d[0]=0;}
+ else if((sgn(del[0]) != sgn(del[1])) && (abs(d[0]) > abs(3*del[0])))
+ d[0]=3*del[0];
+
+ d[n-1]=((2*h[n-2]+h[n-3])*del[n-2]-h[n-2]*del[n-2])/(h[n-2]+h[n-3]);
+ if(sgn(d[n-1]) != sgn(del[n-2])) {d[n-1]=0;}
+ else if((sgn(del[n-2]) != sgn(del[n-3])) &&
+ (abs(d[n-1]) > abs(3*del[n-2])))
+ d[n-1]=3*del[n-2];
+ } else if(n == 2) {
+ d[0]=d[1]=(y[1]-y[0])/(x[1]-x[0]);
+ } else abort(morepoints);
+ return d;
+}
+
+// Return standard cubic spline interpolation as a guide
+guide hermite(real[] x, real[] y, splinetype splinetype=null)
+{
+ int n=x.length;
+ if(n == 0) return nullpath;
+
+ guide g=(x[0],y[0]);
+ if(n == 1) return g;
+ if(n == 2) return g--(x[1],y[1]);
+
+ if(splinetype == null)
+ splinetype=(x[0] == x[x.length-1] && y[0] == y[y.length-1]) ?
+ periodic : notaknot;
+
+ real[] dy=splinetype(x,y);
+ for(int i=1; i < n; ++i) {
+ pair z=(x[i],y[i]);
+ real dx=x[i]-x[i-1];
+ g=g..controls((x[i-1],y[i-1])+dx*(1,dy[i-1])/3) and (z-dx*(1,dy[i])/3)..z;
+ }
+ return g;
+}
diff --git a/Master/texmf-dist/asymptote/grid3.asy b/Master/texmf-dist/asymptote/grid3.asy
new file mode 100644
index 00000000000..98e043a71d7
--- /dev/null
+++ b/Master/texmf-dist/asymptote/grid3.asy
@@ -0,0 +1,412 @@
+// grid3.asy
+// Author: Philippe Ivaldi (Grids in 3D)
+// http://www.piprime.fr/
+// Created: 10 janvier 2007
+
+import graph3;
+
+struct grid3 {
+ path3 axea,axeb;
+ bounds bds;
+ triple dir;
+ valuetime vt;
+ ticklocate locate;
+ void create(picture pic, path3 axea, path3 axeb, path3 axelevel,
+ real min, real max, position pos, autoscaleT t) {
+ real position=pos.position.x;
+ triple level;
+ if(pos.relative) {
+ position=reltime(axelevel,position);
+ level=point(axelevel,position)-point(axelevel,0);
+ } else {
+ triple v=unit(point(axelevel,1)-point(axelevel,0));
+ triple zerolevel=dot(-point(axelevel,0),v)*v;
+ level=zerolevel+position*v;
+ }
+ this.axea=shift(level)*axea;
+ this.axeb=shift(level)*axeb;
+ bds=autoscale(min,max,t.scale);
+ locate=ticklocate(min,max,t,bds.min,bds.max,
+ Dir(point(axeb,0)-point(axea,0)));
+ }
+};
+
+typedef grid3 grid3routine(picture pic);
+
+triple X(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMin().z);}
+triple XY(picture pic) {return (pic.userMax().x,pic.userMax().y,pic.userMin().z);}
+triple Y(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMin().z);}
+triple YZ(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMax().z);}
+triple Z(picture pic) {return (pic.userMin().x,pic.userMin().y,pic.userMax().z);}
+triple ZX(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMax().z);}
+
+grid3routine XYgrid(position pos=Relative(0)) {
+ return new grid3(picture pic) {
+ grid3 og;
+ triple m=pic.userMin();
+ triple M=pic.userMax();
+ og.create(pic,m--X(pic),Y(pic)--XY(pic),m--Z(pic),
+ m.x,M.x,pos,pic.scale.x);
+ return og;
+ };
+};
+grid3routine XYgrid=XYgrid();
+
+grid3routine YXgrid(position pos=Relative(0)) {
+ return new grid3(picture pic) {
+ grid3 og;
+ triple m=pic.userMin();
+ triple M=pic.userMax();
+ og.create(pic,m--Y(pic),X(pic)--XY(pic),m--Z(pic),
+ m.y,M.y,pos,pic.scale.y);
+ return og;
+ };
+};
+grid3routine YXgrid=YXgrid();
+
+
+grid3routine XZgrid(position pos=Relative(0)) {
+ return new grid3(picture pic) {
+ grid3 og;
+ triple m=pic.userMin();
+ triple M=pic.userMax();
+ og.create(pic,m--X(pic),Z(pic)--ZX(pic),m--Y(pic),
+ m.x,M.x,pos,pic.scale.x);
+ return og;
+ };
+};
+grid3routine XZgrid=XZgrid();
+
+grid3routine ZXgrid(position pos=Relative(0)) {
+ return new grid3(picture pic) {
+ grid3 og;
+ triple m=pic.userMin();
+ triple M=pic.userMax();
+ og.create(pic,m--Z(pic),X(pic)--ZX(pic),m--Y(pic),
+ m.z,M.z,pos,pic.scale.z);
+ return og;
+ };
+};
+grid3routine ZXgrid=ZXgrid();
+
+grid3routine YZgrid(position pos=Relative(0)) {
+ return new grid3(picture pic) {
+ grid3 og;
+ triple m=pic.userMin();
+ triple M=pic.userMax();
+ og.create(pic,m--Y(pic),Z(pic)--YZ(pic),m--X(pic),
+ m.y,M.y,pos,pic.scale.y);
+ return og;
+ };
+};
+grid3routine YZgrid=YZgrid();
+
+grid3routine ZYgrid(position pos=Relative(0)) {
+ return new grid3(picture pic) {
+ grid3 og;
+ triple m=pic.userMin();
+ triple M=pic.userMax();
+ og.create(pic,m--Z(pic),Y(pic)--YZ(pic),m--X(pic),
+ m.z,M.z,pos,pic.scale.z);
+ return og;
+ };
+};
+grid3routine ZYgrid=ZYgrid();
+
+typedef grid3routine grid3routines[] ;
+
+grid3routines XYXgrid(position pos=Relative(0)) {
+ grid3routines ogs=new grid3routine[] {XYgrid(pos),YXgrid(pos)};
+ return ogs;
+};
+grid3routines XYXgrid=XYXgrid();
+grid3routines YXYgrid(position pos=Relative(0)) {return XYXgrid(pos);};
+grid3routines YXYgrid=XYXgrid();
+
+grid3routines ZXZgrid(position pos=Relative(0)) {
+ grid3routines ogs=new grid3routine[] {ZXgrid(pos),XZgrid(pos)};
+ return ogs;
+};
+grid3routines ZXZgrid=ZXZgrid();
+grid3routines XZXgrid(position pos=Relative(0)) {return ZXZgrid(pos);};
+grid3routines XZXgrid=XZXgrid();
+
+grid3routines ZYZgrid(position pos=Relative(0)) {
+ grid3routines ogs=new grid3routine[] {ZYgrid(pos),YZgrid(pos)};
+ return ogs;
+};
+grid3routines ZYZgrid=ZYZgrid();
+grid3routines YZYgrid(position pos=Relative(0)) {return ZYZgrid(pos);};
+grid3routines YZYgrid=YZYgrid();
+
+grid3routines XY_XZgrid(position posa=Relative(0), position posb=Relative(0)) {
+ grid3routines ogs=new grid3routine[] {XYgrid(posa),XZgrid(posb)};
+ return ogs;
+};
+grid3routines XY_XZgrid=XY_XZgrid();
+
+grid3routines YX_YZgrid(position posa=Relative(0), position posb=Relative(0)) {
+ grid3routines ogs=new grid3routine[] {YXgrid(posa),YZgrid(posb)};
+ return ogs;
+};
+grid3routines YX_YZgrid=YX_YZgrid();
+
+grid3routines ZX_ZYgrid(position posa=Relative(0), position posb=Relative(0)) {
+ grid3routines ogs=new grid3routine[] {ZXgrid(posa),ZYgrid(posb)};
+ return ogs;
+};
+grid3routines ZX_ZYgrid=ZX_ZYgrid();
+
+typedef grid3routines[] grid3routinetype;
+
+grid3routinetype XYZgrid(position pos=Relative(0))
+{
+ grid3routinetype ogs=new grid3routines[] {YZYgrid(pos),XYXgrid(pos),
+ XZXgrid(pos)};
+ return ogs;
+}
+grid3routinetype XYZgrid=XYZgrid();
+
+grid3routines operator cast(grid3routine gridroutine) {
+ grid3routines og=new grid3routine[] {gridroutine};
+ return og;
+}
+
+grid3routinetype operator cast(grid3routines gridroutine) {
+ grid3routinetype og=new grid3routines[] {gridroutine};
+ return og;
+}
+
+grid3routinetype operator cast(grid3routine gridroutine) {
+ grid3routinetype og=(grid3routinetype)(grid3routines) gridroutine;
+ return og;
+}
+
+void grid3(picture pic=currentpicture,
+ grid3routinetype gridroutine=XYZgrid,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true,
+ pen pGrid=grey, pen pgrid=lightgrey,
+ bool above=false)
+{
+ for(int j=0; j < gridroutine.length; ++j) {
+ grid3routines gridroutinej=gridroutine[j];
+ for(int i=0; i < gridroutinej.length; ++i) {
+ grid3 gt=gridroutinej[i](pic);
+ pic.add(new void(picture f, transform3 t, transform3 T, triple, triple) {
+ picture d;
+ ticks3 ticks=Ticks3(1,F="%",ticklabel=null,
+ beginlabel=false,endlabel=false,
+ N=N,n=n,Step=Step,step=step,
+ begin=begin,end=end,
+ Size=0,size=0,extend=true,
+ pTick=pGrid,ptick=pgrid);
+ ticks(d,t,"",gt.axea,gt.axeb,nullpen,None,NoMargin3,gt.locate,
+ gt.bds.divisor,opposite=true,primary=false);
+ add(f,t*T*inverse(t)*d);
+ },above=above);
+ addPath(pic,gt.axea,pGrid);
+ addPath(pic,gt.axeb,pGrid);
+ }
+ }
+}
+
+void grid3(picture pic=currentpicture,
+ grid3routinetype gridroutine,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true,
+ pen[] pGrid, pen[] pgrid,
+ bool above=false)
+{
+ if(pGrid.length != gridroutine.length || pgrid.length != gridroutine.length)
+ abort("pen array has different length than grid");
+ for(int i=0; i < gridroutine.length; ++i) {
+ grid3(pic=pic,gridroutine=gridroutine[i],
+ N=N,n=n,Step=Step,step=step,
+ begin=begin,end=end,
+ pGrid=pGrid[i],pgrid=pgrid[i],
+ above=above);
+ }
+}
+
+position top=Relative(1);
+position bottom=Relative(0);
+position middle=Relative(0.5);
+
+// Structure used to communicate ticks and axis settings to grid3 routines.
+struct ticksgridT {
+ ticks3 ticks;
+ // Other arguments of grid3 are define by ticks and axis settings
+ void grid3(picture, bool);
+};
+
+typedef ticksgridT ticksgrid();
+
+
+ticksgrid InOutTicks(Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true,
+ real Size=0, real size=0,
+ pen pTick=nullpen, pen ptick=nullpen,
+ grid3routinetype gridroutine,
+ pen pGrid=grey, pen pgrid=lightgrey)
+{
+ return new ticksgridT()
+ {
+ ticksgridT otg;
+ otg.ticks=Ticks3(0,F,ticklabel,beginlabel,endlabel,
+ N,n,Step,step,begin,end,
+ Size,size,false,pTick,ptick);
+ otg.grid3=new void(picture pic, bool above) {
+ grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above);
+ };
+ return otg;
+ };
+}
+
+ticksgrid InTicks(Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true,
+ real Size=0, real size=0,
+ pen pTick=nullpen, pen ptick=nullpen,
+ grid3routinetype gridroutine,
+ pen pGrid=grey, pen pgrid=lightgrey)
+{
+ return new ticksgridT()
+ {
+ ticksgridT otg;
+ otg.ticks=Ticks3(-1,F,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,Size,size,false,pTick,ptick);
+ otg.grid3=new void(picture pic, bool above) {
+ grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above);
+ };
+ return otg;
+ };
+}
+
+ticksgrid OutTicks(Label F="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ bool begin=true, bool end=true,
+ real Size=0, real size=0,
+ pen pTick=nullpen, pen ptick=nullpen,
+ grid3routinetype gridroutine,
+ pen pGrid=grey, pen pgrid=lightgrey)
+{
+ return new ticksgridT()
+ {
+ ticksgridT otg;
+ otg.ticks=Ticks3(1,F,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ begin,end,Size,size,false,pTick,ptick);
+ otg.grid3=new void(picture pic, bool above) {
+ grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above);
+ };
+ return otg;
+ };
+}
+
+void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero,
+ pen p=currentpen, ticksgrid ticks,
+ arrowbar3 arrow=None, bool above=false)
+{
+ xaxis3(pic,L,axis,p,ticks().ticks,arrow,above);
+ ticks().grid3(pic,above);
+}
+
+void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero,
+ pen p=currentpen, ticksgrid ticks,
+ arrowbar3 arrow=None, bool above=false)
+{
+ yaxis3(pic,L,axis,p,ticks().ticks,arrow,above);
+ ticks().grid3(pic,above);
+}
+
+void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero,
+ pen p=currentpen, ticksgrid ticks,
+ arrowbar3 arrow=None, bool above=false)
+{
+ zaxis3(pic,L,axis,p,ticks().ticks,arrow,above);
+ ticks().grid3(pic,above);
+}
+
+/* Example:
+
+ import grid3;
+
+ size(8cm,0);
+ currentprojection=orthographic(0.5,1,0.5);
+
+ defaultpen(overwrite(SuppressQuiet));
+
+ scale(Linear, Linear, Log);
+
+ grid3(pic=currentpicture, // picture
+ gridroutine=XYZgrid(// grid3routine
+ // or grid3routine[] (alias grid3routines)
+ // or grid3routines[]:
+ // The routine(s) to draw the grid(s):
+ // *XYgrid: draw grid from X in direction of Y
+ // *YXgrid: draw grid from Y in direction of X, ...
+ // *An array of previous values XYgrid, YXgrid, ...
+ // *XYXgrid: draw XYgrid and YXgrid grids
+ // *YXYgrid: draw XYgrid and YXgrid grids
+ // *ZXZgrid: draw ZXgrid and XZgrid grids
+ // *YX_YZgrid: draw YXgrid and YZgrid grids
+ // *XY_XZgrid: draw XYgrid and XZgrid grids
+ // *YX_YZgrid: draw YXgrid and YZgrid grids
+ // *An array of previous values XYXgrid,...
+ // *XYZgrid: draw XYXgrid, ZYZgrid, XZXgrid grids.
+ pos=Relative(0)),
+ // the position of the grid relative to the axis
+ // perpendicular to the grid; a real number
+ // specifies a coordinate relative to this axis.
+ // Aliases: top=Relative(1), middle=Relative(0.5)
+ // and bottom=Relative(0).
+
+ // These arguments are similar to those of InOutTicks():
+ N=0, // int
+ n=0, // int
+ Step=0, // real
+ step=0, // real
+ begin=true, // bool
+ end=true, // bool
+ pGrid=grey, // pen
+ pgrid=lightgrey, // pen
+ above=false // bool
+ );
+
+ xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
+ yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks());
+ zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
+*/
+
+/* Other examples:
+
+ int N=10, n=2;
+ xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
+ yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks(N=N,n=n));
+ zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
+ grid3(N=N,n=n);
+
+ xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
+ yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks());
+ zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
+ grid3(new grid3routines[] {XYXgrid(top),XZXgrid(0)});
+
+ xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks());
+ yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks());
+ zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks());
+ grid3(new grid3routines[] {XYXgrid(-0.5),XYXgrid(1.5)},
+ pGrid=new pen[] {red,blue},
+ pgrid=new pen[] {0.5red,0.5blue});
+
+ // Axes with grids:
+
+ xaxis3(Label("$x$",position=EndPoint,align=S),
+ OutTicks(Step=0.5,gridroutine=XYgrid));
+ yaxis3(Label("$y$",position=EndPoint,align=S),
+ InOutTicks(Label("",align=0.5X),N=8,n=2,gridroutine=YX_YZgrid));
+ zaxis3("$z$",OutTicks(ZYgrid));
+*/
diff --git a/Master/texmf-dist/asymptote/interpolate.asy b/Master/texmf-dist/asymptote/interpolate.asy
new file mode 100644
index 00000000000..a600c94c125
--- /dev/null
+++ b/Master/texmf-dist/asymptote/interpolate.asy
@@ -0,0 +1,140 @@
+// Lagrange and Hermite interpolation in Asymptote
+// Author: Olivier Guibé
+// Acknowledgements: Philippe Ivaldi
+
+// diffdiv(x,y) computes Newton's Divided Difference for
+// Lagrange interpolation with distinct values {x_0,..,x_n} in the array x
+// and values y_0,...,y_n in the array y,
+
+// hdiffdiv(x,y,dyp) computes Newton's Divided Difference for
+// Hermite interpolation where dyp={dy_0,...,dy_n}.
+//
+// fhorner(x,coeff) uses Horner's rule to compute the polynomial
+// a_0+a_1(x-x_0)+a_2(x-x_0)(x-x_1)+...+a_n(x-x_0)..(x-x_{n-1}),
+// where coeff={a_0,a_1,...,a_n}.
+
+// fspline does standard cubic spline interpolation of a function f
+// on the interval [a,b].
+// The points a=x_1 < x_2 < .. < x_n=b form the array x;
+// the points y_1=f(x_1),....,y_n=f(x_n) form the array y
+// We use the Hermite form for the spline.
+
+// The syntax is:
+// s=fspline(x,y); default not_a_knot condition
+// s=fspline(x,y,natural); natural spline
+// s=fspline(x,y,periodic); periodic spline
+// s=fspline(x,y,clamped(1,1)); clamped spline
+// s=fspline(x,y,monotonic); piecewise monotonic spline
+
+// Here s is a real function that is constant on (-infinity,a] and [b,infinity).
+
+private import math;
+import graph_splinetype;
+
+typedef real fhorner(real);
+
+struct horner {
+ // x={x0,..,xn}(not necessarily distinct)
+ // a={a0,..,an} corresponds to the polyonmial
+ // a_0+a_1(x-x_0)+a_2(x-x_0)(x-x_1)+...+a_n(x-x_0)..(x-x_{n-1}),
+ real[] x;
+ real[] a;
+}
+
+// Evaluate p(x)=d0+(x-x0)(d1+(x-x1)+...+(d(n-1)+(x-x(n-1))*dn)))
+// via Horner's rule: n-1 multiplications, 2n-2 additions.
+fhorner fhorner(horner sh)
+{
+ int n=sh.x.length;
+ checklengths(n,sh.a.length);
+ return new real(real x) {
+ real s=sh.a[n-1];
+ for(int k=n-2; k >= 0; --k)
+ s=sh.a[k]+(x-sh.x[k])*s;
+ return s;
+ };
+}
+
+// Newton's Divided Difference method: n(n-1)/2 divisions, n(n-1) additions.
+horner diffdiv(real[] x, real[] y)
+{
+ int n=x.length;
+ horner s;
+ checklengths(n,y.length);
+ for(int i=0; i < n; ++i)
+ s.a[i]=y[i];
+ for(int k=0; k < n-1; ++k) {
+ for(int i=n-1; i > k; --i) {
+ s.a[i]=(s.a[i]-s.a[i-1])/(x[i]-x[i-k-1]);
+ }
+ }
+ s.x=x;
+ return s;
+}
+
+// Newton's Divided Difference for simple Hermite interpolation,
+// where one specifies both p(x_i) and p'(x_i).
+horner hdiffdiv(real[] x, real[] y, real[] dy)
+{
+ int n=x.length;
+ horner s;
+ checklengths(n,y.length);
+ checklengths(n,dy.length);
+ for(int i=0; i < n; ++i) {
+ s.a[2*i]=y[i];
+ s.a[2*i+1]=dy[i];
+ s.x[2*i]=x[i];
+ s.x[2*i+1]=x[i];
+ }
+
+ for(int i=n-1; i > 0; --i)
+ s.a[2*i]=(s.a[2*i]-s.a[2*i-2])/(x[i]-x[i-1]);
+
+ int stop=2*n-1;
+ for(int k=1; k < stop; ++k) {
+ for(int i=stop; i > k; --i) {
+ s.a[i]=(s.a[i]-s.a[i-1])/(s.x[i]-s.x[i-k-1]);
+ }
+ }
+ return s;
+}
+
+typedef real realfunction(real);
+
+// piecewise Hermite interpolation:
+// return the piecewise polynomial p(x), where on [x_i,x_i+1], deg(p) <= 3,
+// p(x_i)=y_i, p(x_{i+1})=y_i+1, p'(x_i)=dy_i, and p'(x_{i+1})=dy_i+1.
+// Outside [x_1,x_n] the returned function is constant: y_1 on (infinity,x_1]
+// and y_n on [x_n,infinity).
+realfunction pwhermite(real[] x, real[] y, real[] dy)
+{
+ int n=x.length;
+ checklengths(n,y.length);
+ checklengths(n,dy.length);
+ if(n < 2) abort(morepoints);
+ if(!increasing(x,strict=true)) abort("array x is not strictly increasing");
+ return new real(real t) {
+ int i=search(x,t);
+ if(i == n-1) {
+ i=n-2;
+ t=x[n-1];
+ } else if(i == -1) {
+ i=0;
+ t=x[0];
+ }
+ real h=x[i+1]-x[i];
+ real delta=(y[i+1]-y[i])/h;
+ real e=(3*delta-2*dy[i]-dy[i+1])/h;
+ real f=(dy[i]-2*delta+dy[i+1])/h^2;
+ real s=t-x[i];
+ return y[i]+s*(dy[i]+s*(e+s*f));
+ };
+}
+
+realfunction fspline(real[] x, real[] y, splinetype splinetype=notaknot)
+{
+ real[] dy=splinetype(x,y);
+ return new real(real t) {
+ return pwhermite(x,y,dy)(t);
+ };
+}
diff --git a/Master/texmf-dist/asymptote/labelpath.asy b/Master/texmf-dist/asymptote/labelpath.asy
new file mode 100644
index 00000000000..39c1908efd3
--- /dev/null
+++ b/Master/texmf-dist/asymptote/labelpath.asy
@@ -0,0 +1,28 @@
+usepackage("pstricks");
+usepackage("pst-text");
+
+string LeftJustified="l";
+string RightJustified="r";
+string Centered="c";
+
+void labelpath(frame f, Label L, path g, string justify=Centered,
+ pen p=currentpen)
+{
+ if(latex() && !pdf()) {
+ _labelpath(f,L.s,L.size,g,justify,(L.T.x,L.T.y+0.5linewidth(p)),p);
+ return;
+ }
+ warning("labelpathlatex","labelpath requires -tex latex");
+}
+
+void labelpath(picture pic=currentpicture, Label L, path g,
+ string justify=Centered, pen p=currentpen)
+{
+ pic.add(new void(frame f, transform t) {
+ labelpath(f,L,t*g,justify,p);
+ });
+ frame f;
+ label(f,Label(L.s,L.size));
+ real w=size(f).y+L.T.y+0.5linewidth(p);
+ pic.addBox(min(g),max(g),-w,w);
+}
diff --git a/Master/texmf-dist/asymptote/labelpath3.asy b/Master/texmf-dist/asymptote/labelpath3.asy
new file mode 100644
index 00000000000..63b2290f1f2
--- /dev/null
+++ b/Master/texmf-dist/asymptote/labelpath3.asy
@@ -0,0 +1,83 @@
+// Fit a label to a path3.
+// Author: Jens Schwaiger
+
+import three;
+private real eps=100*realEpsilon;
+
+triple nextnormal(triple p, triple q)
+{
+ triple nw=p-(dot(p,q)*q);
+ return abs(nw) < 0.0001 ? p : unit(nw);
+}
+
+triple[] firstframe(path3 p, triple optional=O)
+{
+ triple[] start=new triple[3];
+ start[0]=dir(p,reltime(p,0));
+ start[1]=(abs(cross(start[0],optional)) < eps) ? perp(start[0]) :
+ unit(cross(start[0],optional));
+ start[2]=cross(start[0],start[1]);
+ return start;
+}
+
+// Modification of the bishop frame construction contained in
+// space_tube.asy (from Philippe Ivaldi's modules).
+// For noncyclic path3s only
+triple[] nextframe(path3 p, real reltimestart, triple[] start, real
+ reltimeend, int subdiv=20)
+{
+ triple[][] bf=new triple[subdiv+1][3];
+ real lg=reltimeend-reltimestart;
+ if(lg <= 0) return start;
+ bf[0]=start;
+ int n=subdiv+1;
+ for(int i=1; i < n; ++i)
+ bf[i][0]=dir(p,reltime(p,reltimestart+(i/subdiv)*lg));
+
+ for(int i=1; i < n; ++i) {
+ bf[i][1]=nextnormal(bf[i-1][1],bf[i][0]);
+ bf[i][2]=cross(bf[i][0],bf[i][1]);
+ }
+ return bf[subdiv];
+}
+
+surface labelpath(string s, path3 p, real angle=90, triple optional=O)
+{
+ real Cos=Cos(angle);
+ real Sin=Sin(angle);
+ path[] text=texpath(Label(s,(0,0),Align,basealign));
+ text=scale(1/(max(text).x-min(text).x))*text;
+ path[][] decompose=containmentTree(text);
+
+ real[][] xpos=new real[decompose.length][2];
+ surface sf;
+ for(int i=0; i < decompose.length; ++i) {// Identify positions along x-axis
+ xpos[i][1]=i;
+ real pos0=0.5(max(decompose[i]).x+min(decompose[i]).x);
+ xpos[i][0]=pos0;
+ }
+ xpos=sort(xpos); // sort by distance from 0;
+ triple[] pos=new triple[decompose.length];
+ real lg=arclength(p);
+ //create frames;
+ triple[] first=firstframe(p,optional);
+ triple[] t0=first;
+ real tm0=0;
+ triple[][] bfr=new triple[decompose.length][3];
+ for(int j=0; j < decompose.length; ++j) {
+ bfr[j]=nextframe(p,tm0,t0,xpos[j][0]);
+ tm0=xpos[j][0]; t0=bfr[j];
+ }
+ transform3[] mt=new transform3[bfr.length];
+ for(int j=0; j < bfr.length; ++j) {
+ triple f2=Cos*bfr[j][1]+Sin*bfr[j][2];
+ triple f3=Sin*bfr[j][1]+Cos*bfr[j][2];
+ mt[j]=shift(relpoint(p,xpos[j][0]))*transform3(bfr[j][0],f2,f3);
+ }
+ for(int j=0; j < bfr.length; ++j) {
+ path[] dc=decompose[(int) xpos[j][1]];
+ pair pos0=(0.5(max(dc).x+min(dc).x),0);
+ sf.append(mt[j]*surface(scale(lg)*shift(-pos0)*dc));
+ }
+ return sf;
+}
diff --git a/Master/texmf-dist/asymptote/latin1.asy b/Master/texmf-dist/asymptote/latin1.asy
new file mode 100644
index 00000000000..3d76925be24
--- /dev/null
+++ b/Master/texmf-dist/asymptote/latin1.asy
@@ -0,0 +1,2 @@
+usepackage("fontenc","T1");
+usepackage("inputenc","latin1");
diff --git a/Master/texmf-dist/asymptote/lmfit.asy b/Master/texmf-dist/asymptote/lmfit.asy
new file mode 100644
index 00000000000..e70421074e5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/lmfit.asy
@@ -0,0 +1,881 @@
+/*
+ Copyright (c) 2009 Philipp Stephani
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+/*
+ Fitting $n$ data points $(x_1, y_1 \pm \Delta y_1), \dots, (x_n, y_n \pm \Delta y_n)$
+ to a function $f$ that depends on $m$ parameters $a_1, \dots, a_m$ means minimizing
+ the least-squares sum
+ %
+ \begin{equation*}
+ \sum_{i = 1}^n \left( \frac{y_i - f(a_1, \dots, a_m; x_i)}{\Delta y_i} \right)^2
+ \end{equation*}
+ %
+ with respect to the parameters $a_1, \dots, a_m$.
+*/
+
+/*
+ This module provides an implementation of the Levenberg--Marquardt
+ (LM) algorithm, converted from the C lmfit routine by Joachim Wuttke
+ (see http://www.messen-und-deuten.de/lmfit/).
+
+ Implementation strategy: Fortunately, Asymptote's syntax is very
+ similar to C, and the original code cleanly separates the
+ customizable parts (user-provided data, output routines, etc.) from
+ the dirty number crunching. Thus, mst of the code was just copied
+ and slightly modified from the original source files. I have
+ amended the lm_data_type structure and the callback routines with a
+ weight array that can be used to provide experimental errors. I
+ have also created two simple wrapper functions.
+*/
+
+
+// copied from the C code
+private real LM_MACHEP = realEpsilon;
+private real LM_DWARF = realMin;
+private real LM_SQRT_DWARF = sqrt(realMin);
+private real LM_SQRT_GIANT = sqrt(realMax);
+private real LM_USERTOL = 30 * LM_MACHEP;
+
+restricted string lm_infmsg[] = {
+ "improper input parameters",
+ "the relative error in the sum of squares is at most tol",
+ "the relative error between x and the solution is at most tol",
+ "both errors are at most tol",
+ "fvec is orthogonal to the columns of the jacobian to machine precision",
+ "number of calls to fcn has reached or exceeded maxcall*(n+1)",
+ "ftol is too small: no further reduction in the sum of squares is possible",
+ "xtol too small: no further improvement in approximate solution x possible",
+ "gtol too small: no further improvement in approximate solution x possible",
+ "not enough memory",
+ "break requested within function evaluation"
+};
+
+restricted string lm_shortmsg[] = {
+ "invalid input",
+ "success (f)",
+ "success (p)",
+ "success (f,p)",
+ "degenerate",
+ "call limit",
+ "failed (f)",
+ "failed (p)",
+ "failed (o)",
+ "no memory",
+ "user break"
+};
+
+
+// copied from the C code and amended with the weight (user_w) array
+struct lm_data_type {
+ real[] user_t;
+ real[] user_y;
+ real[] user_w;
+ real user_func(real user_t_point, real[] par);
+};
+
+
+// Asymptote has no pointer support, so we need reference wrappers for
+// the int and real types
+struct lm_int_type {
+ int val;
+
+ void operator init(int val) {
+ this.val = val;
+ }
+};
+
+
+struct lm_real_type {
+ real val;
+
+ void operator init(real val) {
+ this.val = val;
+ }
+};
+
+
+// copied from the C code; the lm_initialize_control function turned
+// into a constructor
+struct lm_control_type {
+ real ftol;
+ real xtol;
+ real gtol;
+ real epsilon;
+ real stepbound;
+ real fnorm;
+ int maxcall;
+ lm_int_type nfev;
+ lm_int_type info;
+
+ void operator init() {
+ maxcall = 100;
+ epsilon = LM_USERTOL;
+ stepbound = 100;
+ ftol = LM_USERTOL;
+ xtol = LM_USERTOL;
+ gtol = LM_USERTOL;
+ }
+};
+
+
+// copied from the C code
+typedef void lm_evaluate_ftype(real[] par, int m_dat, real[] fvec, lm_data_type data, lm_int_type info);
+typedef void lm_print_ftype(int n_par, real[] par, int m_dat, real[] fvec, lm_data_type data, int iflag, int iter, int nfev);
+
+
+// copied from the C code
+private real SQR(real x) {
+ return x * x;
+}
+
+
+// Asymptote doesn't support pointers to arbitrary array elements, so
+// we provide an offset parameter.
+private real lm_enorm(int n, real[] x, int offset=0) {
+ real s1 = 0;
+ real s2 = 0;
+ real s3 = 0;
+ real x1max = 0;
+ real x3max = 0;
+ real agiant = LM_SQRT_GIANT / n;
+ real xabs, temp;
+
+ for (int i = 0; i < n; ++i) {
+ xabs = fabs(x[offset + i]);
+ if (xabs > LM_SQRT_DWARF && xabs < agiant) {
+ s2 += SQR(xabs);
+ continue;
+ }
+
+ if (xabs > LM_SQRT_DWARF) {
+ if (xabs > x1max) {
+ temp = x1max / xabs;
+ s1 = 1 + s1 * SQR(temp);
+ x1max = xabs;
+ } else {
+ temp = xabs / x1max;
+ s1 += SQR(temp);
+ }
+ continue;
+ }
+ if (xabs > x3max) {
+ temp = x3max / xabs;
+ s3 = 1 + s3 * SQR(temp);
+ x3max = xabs;
+ } else {
+ if (xabs != 0.0) {
+ temp = xabs / x3max;
+ s3 += SQR(temp);
+ }
+ }
+ }
+
+ if (s1 != 0)
+ return x1max * sqrt(s1 + (s2 / x1max) / x1max);
+ if (s2 != 0) {
+ if (s2 >= x3max)
+ return sqrt(s2 * (1 + (x3max / s2) * (x3max * s3)));
+ else
+ return sqrt(x3max * ((s2 / x3max) + (x3max * s3)));
+ }
+
+ return x3max * sqrt(s3);
+}
+
+
+// This function calculated the vector whose square sum is to be
+// minimized. We use a slight modification of the original code that
+// includes the weight factor. The user may provide different
+// customizations.
+void lm_evaluate_default(real[] par, int m_dat, real[] fvec, lm_data_type data, lm_int_type info) {
+ for (int i = 0; i < m_dat; ++i) {
+ fvec[i] = data.user_w[i] * (data.user_y[i] - data.user_func(data.user_t[i], par));
+ }
+}
+
+
+// Helper functions to print padded strings and numbers (until
+// Asymptote provides a real printf function)
+private string pad(string str, int count, string pad=" ") {
+ string res = str;
+ while (length(res) < count)
+ res = pad + res;
+ return res;
+}
+
+
+private string pad(int num, int digits, string pad=" ") {
+ return pad(string(num), digits, pad);
+}
+
+
+private string pad(real num, int digits, string pad=" ") {
+ return pad(string(num), digits, pad);
+}
+
+
+// Similar to the C code, also prints weights
+void lm_print_default(int n_par, real[] par, int m_dat, real[] fvec, lm_data_type data, int iflag, int iter, int nfev) {
+ real f, y, t, w;
+ int i;
+
+ if (iflag == 2) {
+ write("trying step in gradient direction");
+ } else if (iflag == 1) {
+ write(format("determining gradient (iteration %d)", iter));
+ } else if (iflag == 0) {
+ write("starting minimization");
+ } else if (iflag == -1) {
+ write(format("terminated after %d evaluations", nfev));
+ }
+
+ write(" par: ", none);
+ for (i = 0; i < n_par; ++i) {
+ write(" " + pad(par[i], 12), none);
+ }
+ write(" => norm: " + pad(lm_enorm(m_dat, fvec), 12));
+
+ if (iflag == -1) {
+ write(" fitting data as follows:");
+ for (i = 0; i < m_dat; ++i) {
+ t = data.user_t[i];
+ y = data.user_y[i];
+ w = data.user_w[i];
+ f = data.user_func(t, par);
+ write(format(" t[%2d]=", i) + pad(t, 12) + " y=" + pad(y, 12) + " w=" + pad(w, 12) + " fit=" + pad(f, 12) + " residue=" + pad(y - f, 12));
+ }
+ }
+}
+
+
+// Prints nothing
+void lm_print_quiet(int n_par, real[] par, int m_dat, real[] fvec, lm_data_type data, int iflag, int iter, int nfev) {
+}
+
+
+// copied from the C code
+private void lm_qrfac(int m, int n, real[] a, bool pivot, int[] ipvt, real[] rdiag, real[] acnorm, real[] wa) {
+ int i, j, k, kmax, minmn;
+ real ajnorm, sum, temp;
+ static real p05 = 0.05;
+
+ for (j = 0; j < n; ++j) {
+ acnorm[j] = lm_enorm(m, a, j * m);
+ rdiag[j] = acnorm[j];
+ wa[j] = rdiag[j];
+ if (pivot)
+ ipvt[j] = j;
+ }
+
+ minmn = min(m, n);
+ for (j = 0; j < minmn; ++j) {
+ while (pivot) {
+ kmax = j;
+ for (k = j + 1; k < n; ++k)
+ if (rdiag[k] > rdiag[kmax])
+ kmax = k;
+ if (kmax == j)
+ break;
+
+ for (i = 0; i < m; ++i) {
+ temp = a[j * m + i];
+ a[j * m + i] = a[kmax * m + i];
+ a[kmax * m + i] = temp;
+ }
+ rdiag[kmax] = rdiag[j];
+ wa[kmax] = wa[j];
+ k = ipvt[j];
+ ipvt[j] = ipvt[kmax];
+ ipvt[kmax] = k;
+
+ break;
+ }
+
+ ajnorm = lm_enorm(m - j, a, j * m + j);
+ if (ajnorm == 0.0) {
+ rdiag[j] = 0;
+ continue;
+ }
+
+ if (a[j * m + j] < 0.0)
+ ajnorm = -ajnorm;
+ for (i = j; i < m; ++i)
+ a[j * m + i] /= ajnorm;
+ a[j * m + j] += 1;
+
+ for (k = j + 1; k < n; ++k) {
+ sum = 0;
+
+ for (i = j; i < m; ++i)
+ sum += a[j * m + i] * a[k * m + i];
+
+ temp = sum / a[j + m * j];
+
+ for (i = j; i < m; ++i)
+ a[k * m + i] -= temp * a[j * m + i];
+
+ if (pivot && rdiag[k] != 0.0) {
+ temp = a[m * k + j] / rdiag[k];
+ temp = max(0.0, 1 - SQR(temp));
+ rdiag[k] *= sqrt(temp);
+ temp = rdiag[k] / wa[k];
+ if (p05 * SQR(temp) <= LM_MACHEP) {
+ rdiag[k] = lm_enorm(m - j - 1, a, m * k + j + 1);
+ wa[k] = rdiag[k];
+ }
+ }
+ }
+
+ rdiag[j] = -ajnorm;
+ }
+}
+
+
+// copied from the C code
+private void lm_qrsolv(int n, real[] r, int ldr, int[] ipvt, real[] diag, real[] qtb, real[] x, real[] sdiag, real[] wa) {
+ static real p25 = 0.25;
+ static real p5 = 0.5;
+
+ int i, kk, j, k, nsing;
+ real qtbpj, sum, temp;
+ real _sin, _cos, _tan, _cot;
+
+ for (j = 0; j < n; ++j) {
+ for (i = j; i < n; ++i)
+ r[j * ldr + i] = r[i * ldr + j];
+ x[j] = r[j * ldr + j];
+ wa[j] = qtb[j];
+ }
+
+ for (j = 0; j < n; ++j) {
+ while (diag[ipvt[j]] != 0.0) {
+ for (k = j; k < n; ++k)
+ sdiag[k] = 0.0;
+ sdiag[j] = diag[ipvt[j]];
+
+ qtbpj = 0.;
+ for (k = j; k < n; ++k) {
+ if (sdiag[k] == 0.)
+ continue;
+ kk = k + ldr * k;
+ if (fabs(r[kk]) < fabs(sdiag[k])) {
+ _cot = r[kk] / sdiag[k];
+ _sin = p5 / sqrt(p25 + p25 * _cot * _cot);
+ _cos = _sin * _cot;
+ } else {
+ _tan = sdiag[k] / r[kk];
+ _cos = p5 / sqrt(p25 + p25 * _tan * _tan);
+ _sin = _cos * _tan;
+ }
+
+ r[kk] = _cos * r[kk] + _sin * sdiag[k];
+ temp = _cos * wa[k] + _sin * qtbpj;
+ qtbpj = -_sin * wa[k] + _cos * qtbpj;
+ wa[k] = temp;
+
+ for (i = k + 1; i < n; ++i) {
+ temp = _cos * r[k * ldr + i] + _sin * sdiag[i];
+ sdiag[i] = -_sin * r[k * ldr + i] + _cos * sdiag[i];
+ r[k * ldr + i] = temp;
+ }
+ }
+ break;
+ }
+
+ sdiag[j] = r[j * ldr + j];
+ r[j * ldr + j] = x[j];
+ }
+
+ nsing = n;
+ for (j = 0; j < n; ++j) {
+ if (sdiag[j] == 0.0 && nsing == n)
+ nsing = j;
+ if (nsing < n)
+ wa[j] = 0;
+ }
+
+ for (j = nsing - 1; j >= 0; --j) {
+ sum = 0;
+ for (i = j + 1; i < nsing; ++i)
+ sum += r[j * ldr + i] * wa[i];
+ wa[j] = (wa[j] - sum) / sdiag[j];
+ }
+
+ for (j = 0; j < n; ++j)
+ x[ipvt[j]] = wa[j];
+}
+
+
+// copied from the C code
+private void lm_lmpar(int n, real[] r, int ldr, int[] ipvt, real[] diag, real[] qtb, real delta, lm_real_type par, real[] x, real[] sdiag, real[] wa1, real[] wa2) {
+ static real p1 = 0.1;
+ static real p001 = 0.001;
+
+ int nsing = n;
+ real parl = 0.0;
+
+ int i, iter, j;
+ real dxnorm, fp, fp_old, gnorm, parc, paru;
+ real sum, temp;
+
+ for (j = 0; j < n; ++j) {
+ wa1[j] = qtb[j];
+ if (r[j * ldr + j] == 0 && nsing == n)
+ nsing = j;
+ if (nsing < n)
+ wa1[j] = 0;
+ }
+ for (j = nsing - 1; j >= 0; --j) {
+ wa1[j] = wa1[j] / r[j + ldr * j];
+ temp = wa1[j];
+ for (i = 0; i < j; ++i)
+ wa1[i] -= r[j * ldr + i] * temp;
+ }
+
+ for (j = 0; j < n; ++j)
+ x[ipvt[j]] = wa1[j];
+
+ iter = 0;
+ for (j = 0; j < n; ++j)
+ wa2[j] = diag[j] * x[j];
+ dxnorm = lm_enorm(n, wa2);
+ fp = dxnorm - delta;
+ if (fp <= p1 * delta) {
+ par.val = 0;
+ return;
+ }
+
+ if (nsing >= n) {
+ for (j = 0; j < n; ++j)
+ wa1[j] = diag[ipvt[j]] * wa2[ipvt[j]] / dxnorm;
+
+ for (j = 0; j < n; ++j) {
+ sum = 0.0;
+ for (i = 0; i < j; ++i)
+ sum += r[j * ldr + i] * wa1[i];
+ wa1[j] = (wa1[j] - sum) / r[j + ldr * j];
+ }
+ temp = lm_enorm(n, wa1);
+ parl = fp / delta / temp / temp;
+ }
+
+ for (j = 0; j < n; ++j) {
+ sum = 0;
+ for (i = 0; i <= j; ++i)
+ sum += r[j * ldr + i] * qtb[i];
+ wa1[j] = sum / diag[ipvt[j]];
+ }
+ gnorm = lm_enorm(n, wa1);
+ paru = gnorm / delta;
+ if (paru == 0.0)
+ paru = LM_DWARF / min(delta, p1);
+
+ par.val = max(par.val, parl);
+ par.val = min(par.val, paru);
+ if (par.val == 0.0)
+ par.val = gnorm / dxnorm;
+
+ for (;; ++iter) {
+ if (par.val == 0.0)
+ par.val = max(LM_DWARF, p001 * paru);
+ temp = sqrt(par.val);
+ for (j = 0; j < n; ++j)
+ wa1[j] = temp * diag[j];
+ lm_qrsolv(n, r, ldr, ipvt, wa1, qtb, x, sdiag, wa2);
+ for (j = 0; j < n; ++j)
+ wa2[j] = diag[j] * x[j];
+ dxnorm = lm_enorm(n, wa2);
+ fp_old = fp;
+ fp = dxnorm - delta;
+
+ if (fabs(fp) <= p1 * delta || (parl == 0.0 && fp <= fp_old && fp_old < 0.0) || iter == 10)
+ break;
+
+ for (j = 0; j < n; ++j)
+ wa1[j] = diag[ipvt[j]] * wa2[ipvt[j]] / dxnorm;
+
+ for (j = 0; j < n; ++j) {
+ wa1[j] = wa1[j] / sdiag[j];
+ for (i = j + 1; i < n; ++i)
+ wa1[i] -= r[j * ldr + i] * wa1[j];
+ }
+ temp = lm_enorm(n, wa1);
+ parc = fp / delta / temp / temp;
+
+ if (fp > 0)
+ parl = max(parl, par.val);
+ else if (fp < 0)
+ paru = min(paru, par.val);
+
+ par.val = max(parl, par.val + parc);
+ }
+}
+
+
+// copied from the C code; the main function
+void lm_lmdif(int m, int n, real[] x, real[] fvec, real ftol, real xtol, real gtol, int maxfev, real epsfcn, real[] diag, int mode, real factor, lm_int_type info, lm_int_type nfev, real[] fjac, int[] ipvt, real[] qtf, real[] wa1, real[] wa2, real[] wa3, real[] wa4, lm_evaluate_ftype evaluate, lm_print_ftype printout, lm_data_type data) {
+ static real p1 = 0.1;
+ static real p5 = 0.5;
+ static real p25 = 0.25;
+ static real p75 = 0.75;
+ static real p0001 = 1.0e-4;
+
+ nfev.val = 0;
+ int iter = 1;
+ lm_real_type par = lm_real_type(0);
+ real delta = 0;
+ real xnorm = 0;
+ real temp = max(epsfcn, LM_MACHEP);
+ real eps = sqrt(temp);
+ int i, j;
+ real actred, dirder, fnorm, fnorm1, gnorm, pnorm, prered, ratio, step, sum, temp1, temp2, temp3;
+
+ if ((n <= 0) || (m < n) || (ftol < 0.0) || (xtol < 0.0) || (gtol < 0.0) || (maxfev <= 0) || (factor <= 0)) {
+ info.val = 0;
+ return;
+ }
+ if (mode == 2) {
+ for (j = 0; j < n; ++j) {
+ if (diag[j] <= 0.0) {
+ info.val = 0;
+ return;
+ }
+ }
+ }
+
+ info.val = 0;
+ evaluate(x, m, fvec, data, info);
+ printout(n, x, m, fvec, data, 0, 0, ++nfev.val);
+ if (info.val < 0)
+ return;
+ fnorm = lm_enorm(m, fvec);
+
+ do {
+ for (j = 0; j < n; ++j) {
+ temp = x[j];
+ step = eps * fabs(temp);
+ if (step == 0.0)
+ step = eps;
+ x[j] = temp + step;
+ info.val = 0;
+ evaluate(x, m, wa4, data, info);
+ printout(n, x, m, wa4, data, 1, iter, ++nfev.val);
+ if (info.val < 0)
+ return;
+ for (i = 0; i < m; ++i)
+ fjac[j * m + i] = (wa4[i] - fvec[i]) / (x[j] - temp);
+ x[j] = temp;
+ }
+
+ lm_qrfac(m, n, fjac, true, ipvt, wa1, wa2, wa3);
+
+ if (iter == 1) {
+ if (mode != 2) {
+ for (j = 0; j < n; ++j) {
+ diag[j] = wa2[j];
+ if (wa2[j] == 0.0)
+ diag[j] = 1.0;
+ }
+ }
+ for (j = 0; j < n; ++j)
+ wa3[j] = diag[j] * x[j];
+ xnorm = lm_enorm(n, wa3);
+ delta = factor * xnorm;
+ if (delta == 0.0)
+ delta = factor;
+ }
+
+ for (i = 0; i < m; ++i)
+ wa4[i] = fvec[i];
+
+ for (j = 0; j < n; ++j) {
+ temp3 = fjac[j * m + j];
+ if (temp3 != 0.0) {
+ sum = 0;
+ for (i = j; i < m; ++i)
+ sum += fjac[j * m + i] * wa4[i];
+ temp = -sum / temp3;
+ for (i = j; i < m; ++i)
+ wa4[i] += fjac[j * m + i] * temp;
+ }
+ fjac[j * m + j] = wa1[j];
+ qtf[j] = wa4[j];
+ }
+
+ gnorm = 0;
+ if (fnorm != 0) {
+ for (j = 0; j < n; ++j) {
+ if (wa2[ipvt[j]] == 0) continue;
+ sum = 0.0;
+ for (i = 0; i <= j; ++i)
+ sum += fjac[j * m + i] * qtf[i] / fnorm;
+ gnorm = max(gnorm, fabs(sum / wa2[ipvt[j]]));
+ }
+ }
+
+ if (gnorm <= gtol) {
+ info.val = 4;
+ return;
+ }
+
+ if (mode != 2) {
+ for (j = 0; j < n; ++j)
+ diag[j] = max(diag[j], wa2[j]);
+ }
+
+ do {
+ lm_lmpar(n, fjac, m, ipvt, diag, qtf, delta, par, wa1, wa2, wa3, wa4);
+
+ for (j = 0; j < n; ++j) {
+ wa1[j] = -wa1[j];
+ wa2[j] = x[j] + wa1[j];
+ wa3[j] = diag[j] * wa1[j];
+ }
+ pnorm = lm_enorm(n, wa3);
+
+ if (nfev.val <= 1 + n)
+ delta = min(delta, pnorm);
+
+ info.val = 0;
+ evaluate(wa2, m, wa4, data, info);
+ printout(n, x, m, wa4, data, 2, iter, ++nfev.val);
+ if (info.val < 0)
+ return;
+
+ fnorm1 = lm_enorm(m, wa4);
+
+ if (p1 * fnorm1 < fnorm)
+ actred = 1 - SQR(fnorm1 / fnorm);
+ else
+ actred = -1;
+
+ for (j = 0; j < n; ++j) {
+ wa3[j] = 0;
+ for (i = 0; i <= j; ++i)
+ wa3[i] += fjac[j * m + i] * wa1[ipvt[j]];
+ }
+ temp1 = lm_enorm(n, wa3) / fnorm;
+ temp2 = sqrt(par.val) * pnorm / fnorm;
+ prered = SQR(temp1) + 2 * SQR(temp2);
+ dirder = -(SQR(temp1) + SQR(temp2));
+
+ ratio = prered != 0 ? actred / prered : 0;
+
+ if (ratio <= p25) {
+ if (actred >= 0.0)
+ temp = p5;
+ else
+ temp = p5 * dirder / (dirder + p5 * actred);
+ if (p1 * fnorm1 >= fnorm || temp < p1)
+ temp = p1;
+ delta = temp * min(delta, pnorm / p1);
+ par.val /= temp;
+ } else if (par.val == 0.0 || ratio >= p75) {
+ delta = pnorm / p5;
+ par.val *= p5;
+ }
+
+ if (ratio >= p0001) {
+ for (j = 0; j < n; ++j) {
+ x[j] = wa2[j];
+ wa2[j] = diag[j] * x[j];
+ }
+ for (i = 0; i < m; ++i)
+ fvec[i] = wa4[i];
+ xnorm = lm_enorm(n, wa2);
+ fnorm = fnorm1;
+ ++iter;
+ }
+
+ info.val = 0;
+ if (fabs(actred) <= ftol && prered <= ftol && p5 * ratio <= 1)
+ info.val = 1;
+ if (delta <= xtol * xnorm)
+ info.val += 2;
+ if (info.val != 0)
+ return;
+
+ if (nfev.val >= maxfev)
+ info.val = 5;
+ if (fabs(actred) <= LM_MACHEP && prered <= LM_MACHEP && p5 * ratio <= 1)
+ info.val = 6;
+ if (delta <= LM_MACHEP * xnorm)
+ info.val = 7;
+ if (gnorm <= LM_MACHEP)
+ info.val = 8;
+ if (info.val != 0)
+ return;
+ } while (ratio < p0001);
+ } while (true);
+}
+
+
+// copied from the C code; wrapper of lm_lmdif
+void lm_minimize(int m_dat, int n_par, real[] par, lm_evaluate_ftype evaluate, lm_print_ftype printout, lm_data_type data, lm_control_type control) {
+ int n = n_par;
+ int m = m_dat;
+
+ real[] fvec = new real[m];
+ real[] diag = new real[n];
+ real[] qtf = new real[n];
+ real[] fjac = new real[n * m];
+ real[] wa1 = new real[n];
+ real[] wa2 = new real[n];
+ real[] wa3 = new real[n];
+ real[] wa4 = new real[m];
+ int[] ipvt = new int[n];
+
+ control.info.val = 0;
+ control.nfev.val = 0;
+
+ lm_lmdif(m, n, par, fvec, control.ftol, control.xtol, control.gtol, control.maxcall * (n + 1), control.epsilon, diag, 1, control.stepbound, control.info, control.nfev, fjac, ipvt, qtf, wa1, wa2, wa3, wa4, evaluate, printout, data);
+
+ printout(n, par, m, fvec, data, -1, 0, control.nfev.val);
+ control.fnorm = lm_enorm(m, fvec);
+ if (control.info.val < 0)
+ control.info.val = 10;
+}
+
+
+// convenience functions; wrappers of lm_minimize
+
+/*
+ The structure FitControl specifies various control parameters.
+*/
+struct FitControl {
+ real squareSumTolerance; // relative error desired in the sum of squares
+ real approximationTolerance; // relative error between last two approximations
+ real desiredOrthogonality; // orthogonality desired between the residue vector and its derivatives
+ real epsilon; // step used to calculate the jacobian
+ real stepBound; // initial bound to steps in the outer loop
+ int maxIterations; // maximum number of iterations
+ bool verbose; // whether to print detailed information about every iteration, or nothing
+
+ void operator init(real squareSumTolerance=LM_USERTOL, real approximationTolerance=LM_USERTOL, real desiredOrthogonality=LM_USERTOL, real epsilon=LM_USERTOL, real stepBound=100, int maxIterations=100, bool verbose=false) {
+ this.squareSumTolerance = squareSumTolerance;
+ this.approximationTolerance = approximationTolerance;
+ this.desiredOrthogonality = desiredOrthogonality;
+ this.epsilon = epsilon;
+ this.stepBound = stepBound;
+ this.maxIterations = maxIterations;
+ this.verbose = verbose;
+ }
+
+ FitControl copy() {
+ FitControl result = new FitControl;
+ result.squareSumTolerance = this.squareSumTolerance;
+ result.approximationTolerance = this.approximationTolerance;
+ result.desiredOrthogonality = this.desiredOrthogonality;
+ result.epsilon = this.epsilon;
+ result.stepBound = this.stepBound;
+ result.maxIterations = this.maxIterations;
+ result.verbose = this.verbose;
+ return result;
+ }
+};
+
+FitControl operator init() {
+ return FitControl();
+}
+
+FitControl defaultControl;
+
+
+/*
+ Upon returning, this structure provides information about the fit.
+*/
+struct FitResult {
+ real norm; // norm of the residue vector
+ int iterations; // actual number of iterations
+ int status; // status of minimization
+
+ void operator init(real norm, int iterations, int status) {
+ this.norm = norm;
+ this.iterations = iterations;
+ this.status = status;
+ }
+};
+
+
+/*
+ Fits data points to a function that depends on some parameters.
+
+ Parameters:
+ - xdata: Array of x values.
+ - ydata: Array of y values.
+ - errors: Array of experimental errors; each element must be strictly positive
+ - function: Fit function.
+ - parameters: Parameter array. Before calling fit(), this must contain the initial guesses for the parameters.
+ Upon return, it will contain the solution parameters.
+ - control: object of type FitControl that controls various aspects of the fitting procedure.
+
+ Returns:
+ An object of type FitResult that conveys information about the fitting process.
+*/
+FitResult fit(real[] xdata, real[] ydata, real[] errors, real function(real[], real), real[] parameters, FitControl control=defaultControl) {
+ int m_dat = min(xdata.length, ydata.length);
+ int n_par = parameters.length;
+ lm_evaluate_ftype evaluate = lm_evaluate_default;
+ lm_print_ftype printout = control.verbose ? lm_print_default : lm_print_quiet;
+
+ lm_data_type data;
+ data.user_t = xdata;
+ data.user_y = ydata;
+ data.user_w = 1 / errors;
+ data.user_func = new real(real x, real[] params) {
+ return function(params, x);
+ };
+
+ lm_control_type ctrl;
+ ctrl.ftol = control.squareSumTolerance;
+ ctrl.xtol = control.approximationTolerance;
+ ctrl.gtol = control.desiredOrthogonality;
+ ctrl.epsilon = control.epsilon;
+ ctrl.stepbound = control.stepBound;
+ ctrl.maxcall = control.maxIterations;
+
+ lm_minimize(m_dat, n_par, parameters, evaluate, printout, data, ctrl);
+
+ return FitResult(ctrl.fnorm, ctrl.nfev.val, ctrl.info.val);
+}
+
+
+/*
+ Fits data points to a function that depends on some parameters.
+
+ Parameters:
+ - xdata: Array of x values.
+ - ydata: Array of y values.
+ - function: Fit function.
+ - parameters: Parameter array. Before calling fit(), this must contain the initial guesses for the parameters.
+ Upon return, it will contain the solution parameters.
+ - control: object of type FitControl that controls various aspects of the fitting procedure.
+
+ Returns:
+ An object of type FitResult that conveys information about the fitting process.
+*/
+FitResult fit(real[] xdata, real[] ydata, real function(real[], real), real[] parameters, FitControl control=defaultControl) {
+ return fit(xdata, ydata, array(min(xdata.length, ydata.length), 1.0), function, parameters, control);
+}
+
diff --git a/Master/texmf-dist/asymptote/markers.asy b/Master/texmf-dist/asymptote/markers.asy
new file mode 100644
index 00000000000..84adc8e6cf6
--- /dev/null
+++ b/Master/texmf-dist/asymptote/markers.asy
@@ -0,0 +1,218 @@
+// Mark routines and markers written by Philippe Ivaldi.
+// http://www.piprime.fr/
+
+marker operator * (transform T, marker m)
+{
+ marker M=new marker;
+ M.f=T*m.f;
+ M.above=m.above;
+ M.markroutine=m.markroutine;
+ return M;
+}
+
+// Add n frames f midway (in arclength) between n+1 uniformly spaced marks.
+markroutine markinterval(int n=1, frame f, bool rotated=false)
+{
+ return new void(picture pic=currentpicture, frame mark, path g) {
+ markuniform(n+1,rotated)(pic,mark,g);
+ markuniform(centered=true,n,rotated)(pic,f,g);
+ };
+}
+
+// Return a frame containing n copies of the path g shifted by space
+// drawn with pen p.
+frame duplicate(path g, int n=1, pair space=0, pen p=currentpen)
+{
+ if(space == 0) space=dotsize(p);
+ frame f;
+ int pos=0;
+ int sign=1;
+ int m=(n+1) % 2;
+ for(int i=1; i <= n; ++i) {
+ draw(f,shift(space*(pos-0.5*m))*g,p);
+ pos += i*sign;
+ sign *= -1;
+ }
+ return f;
+}
+
+real tildemarksizefactor=5;
+real tildemarksize(pen p=currentpen)
+{
+ static real golden=(1+sqrt(5))/2;
+ return (1mm+tildemarksizefactor*sqrt(linewidth(p)))/golden;
+}
+frame tildeframe(int n=1, real size=0, pair space=0,
+ real angle=0, pair offset=0, pen p=currentpen)
+{
+ size=(size == 0) ? tildemarksize(p) : size;
+ space=(space == 0) ? 1.5*size : space;
+ path g=yscale(1.25)*((-1.5,-0.5)..(-0.75,0.5)..(0,0)..(0.75,-0.5)..(1.5,0.5));
+ return duplicate(shift(offset)*rotate(angle)*scale(size)*g,n,space,p);
+}
+
+frame tildeframe=tildeframe();
+
+real stickmarkspacefactor=4;
+real stickmarksizefactor=10;
+real stickmarksize(pen p=currentpen)
+{
+ return 1mm+stickmarksizefactor*sqrt(linewidth(p));
+}
+real stickmarkspace(pen p=currentpen)
+{
+ return stickmarkspacefactor*sqrt(linewidth(p));
+}
+frame stickframe(int n=1, real size=0, pair space=0, real angle=0,
+ pair offset=0, pen p=currentpen)
+{
+ if(size == 0) size=stickmarksize(p);
+ if(space == 0) space=stickmarkspace(p);
+ return duplicate(shift(offset)*rotate(angle)*scale(0.5*size)*(N--S),n,
+ space,p);
+}
+
+frame stickframe=stickframe();
+
+real circlemarkradiusfactor=stickmarksizefactor/2;
+real circlemarkradius(pen p=currentpen)
+{
+ static real golden=(1+sqrt(5))/2;
+ return (1mm+circlemarkradiusfactor*sqrt(linewidth(p)))/golden;
+}
+real barmarksizefactor=stickmarksizefactor;
+real barmarksize(pen p=currentpen)
+{
+ return 1mm+barmarksizefactor*sqrt(linewidth(p));
+}
+frame circlebarframe(int n=1, real barsize=0,
+ real radius=0,real angle=0,
+ pair offset=0, pen p=currentpen,
+ filltype filltype=NoFill, bool above=false)
+{
+ if(barsize == 0) barsize=barmarksize(p);
+ if(radius == 0) radius=circlemarkradius(p);
+ frame opic;
+ path g=circle(offset,radius);
+ frame f=stickframe(n,barsize,space=2*radius/(n+1),angle,offset,p);
+ if(above) {
+ add(opic,f);
+ filltype.fill(opic,g,p);
+ } else {
+ filltype.fill(opic,g,p);
+ add(opic,f);
+ }
+ return opic;
+}
+
+real crossmarksizefactor=5;
+real crossmarksize(pen p=currentpen)
+{
+ return 1mm+crossmarksizefactor*sqrt(linewidth(p));
+}
+frame crossframe(int n=3, real size=0, pair space=0,
+ real angle=0, pair offset=0, pen p=currentpen)
+{
+ if(size == 0) size=crossmarksize(p);
+ frame opic;
+ draw(opic,shift(offset)*rotate(angle)*scale(size)*cross(n),p);
+ return opic;
+}
+
+real markanglespacefactor=4;
+real markangleradiusfactor=8;
+real markangleradius(pen p=currentpen)
+{
+ return 8mm+markangleradiusfactor*sqrt(linewidth(p));
+}
+real markangleradius=markangleradius();
+real markanglespace(pen p=currentpen)
+{
+ return markanglespacefactor*sqrt(linewidth(p));
+}
+real markanglespace=markanglespace();
+// Mark the oriented angle AOB counterclockwise with optional Label, arrows, and markers.
+// With radius < 0, AOB-2pi is marked clockwise.
+void markangle(picture pic=currentpicture, Label L="",
+ int n=1, real radius=0, real space=0,
+ pair A, pair O, pair B, arrowbar arrow=None,
+ pen p=currentpen, filltype filltype=NoFill,
+ margin margin=NoMargin, marker marker=nomarker)
+{
+ if(space == 0) space=markanglespace(p);
+ if(radius == 0) radius=markangleradius(p);
+ picture lpic,phantom;
+ frame ff;
+ path lpth;
+ p=squarecap+p;
+ pair OB=unit(B-O), OA=unit(A-O);
+ real xoa=degrees(OA,false);
+ real gle=degrees(acos(dot(OA,OB)));
+ if((conj(OA)*OB).y < 0) gle *= -1;
+ bool ccw=radius > 0;
+ if(!ccw) radius=-radius;
+ bool drawarrow = !arrow(phantom,arc((0,0),radius,xoa,xoa+gle,ccw),p,margin);
+ if(drawarrow && margin == NoMargin) margin=TrueMargin(0,0.5linewidth(p));
+ if(filltype != NoFill) {
+ lpth=margin(arc((0,0),radius+(n-1)*space,xoa,xoa+gle,ccw),p).g;
+ pair p0=relpoint(lpth,0), p1=relpoint(lpth,1);
+ pair ac=p0-p0-A+O, bd=p1-p1-B+O, det=(conj(ac)*bd).y;
+ pair op=(det == 0) ? O : p0+(conj(p1-p0)*bd).y*ac/det;
+ filltype.fill(ff,op--lpth--relpoint(lpth,1)--cycle,p);
+ add(lpic,ff);
+ }
+ for(int i=0; i < n; ++i) {
+ lpth=margin(arc((0,0),radius+i*space,xoa,xoa+gle,ccw),p).g;
+ draw(lpic,lpth,p=p,arrow=arrow,margin=NoMargin,marker=marker);
+ }
+ Label lL=L.copy();
+ real position=lL.position.position.x;
+ if(lL.defaultposition) {lL.position.relative=true; position=0.5;}
+ if(lL.position.relative) position=reltime(lpth,position);
+ if(lL.align.default) {
+ lL.align.relative=true;
+ lL.align.dir=unit(point(lpth,position));
+ }
+ label(lpic,lL,point(lpth,position),align=NoAlign, p=p);
+ add(pic,lpic,O);
+}
+
+marker StickIntervalMarker(int i=2, int n=1, real size=0, real space=0,
+ real angle=0, pair offset=0, bool rotated=true,
+ pen p=currentpen, frame uniform=newframe,
+ bool above=true)
+{
+ return marker(uniform,markinterval(i,stickframe(n,size,space,angle,offset,p),
+ rotated),above);
+}
+
+
+marker CrossIntervalMarker(int i=2, int n=3, real size=0, real space=0,
+ real angle=0, pair offset=0, bool rotated=true,
+ pen p=currentpen, frame uniform=newframe,
+ bool above=true)
+{
+ return marker(uniform,markinterval(i,crossframe(n,size,space,angle,offset,p),
+ rotated=rotated),above);
+}
+
+marker CircleBarIntervalMarker(int i=2, int n=1, real barsize=0, real radius=0,
+ real angle=0, pair offset=0, bool rotated=true,
+ pen p=currentpen, filltype filltype=NoFill,
+ bool circleabove=false, frame uniform=newframe,
+ bool above=true)
+{
+ return marker(uniform,markinterval(i,circlebarframe(n,barsize,radius,angle,
+ offset,p,filltype,
+ circleabove),
+ rotated),above);
+}
+
+marker TildeIntervalMarker(int i=2, int n=1, real size=0, real space=0,
+ real angle=0, pair offset=0, bool rotated=true,
+ pen p=currentpen, frame uniform=newframe,
+ bool above=true)
+{
+ return marker(uniform,markinterval(i,tildeframe(n,size,space,angle,offset,p),
+ rotated),above);
+}
diff --git a/Master/texmf-dist/asymptote/math.asy b/Master/texmf-dist/asymptote/math.asy
new file mode 100644
index 00000000000..77c0664895f
--- /dev/null
+++ b/Master/texmf-dist/asymptote/math.asy
@@ -0,0 +1,417 @@
+// Asymptote mathematics routines
+
+int quadrant(real degrees)
+{
+ return floor(degrees/90) % 4;
+}
+
+// Roots of unity.
+pair unityroot(int n, int k=1)
+{
+ return expi(2pi*k/n);
+}
+
+real csc(real x) {return 1/sin(x);}
+real sec(real x) {return 1/cos(x);}
+real cot(real x) {return tan(pi/2-x);}
+
+real acsc(real x) {return asin(1/x);}
+real asec(real x) {return acos(1/x);}
+real acot(real x) {return pi/2-atan(x);}
+
+real frac(real x) {return x-(int)x;}
+
+pair exp(explicit pair z) {return exp(z.x)*expi(z.y);}
+pair log(explicit pair z) {return log(abs(z))+I*angle(z);}
+
+// Return an Nx by Ny unit square lattice with lower-left corner at (0,0).
+picture grid(int Nx, int Ny, pen p=currentpen)
+{
+ picture pic;
+ for(int i=0; i <= Nx; ++i) draw(pic,(i,0)--(i,Ny),p);
+ for(int j=0; j <= Ny; ++j) draw(pic,(0,j)--(Nx,j),p);
+ return pic;
+}
+
+bool polygon(path p)
+{
+ return cyclic(p) && piecewisestraight(p);
+}
+
+// Return the intersection time of the point on the line through p and q
+// that is closest to z.
+real intersect(pair p, pair q, pair z)
+{
+ pair u=q-p;
+ real denom=dot(u,u);
+ return denom == 0 ? infinity : dot(z-p,u)/denom;
+}
+
+// Return the intersection time of the extension of the line segment PQ
+// with the plane perpendicular to n and passing through Z.
+real intersect(triple P, triple Q, triple n, triple Z)
+{
+ real d=n.x*Z.x+n.y*Z.y+n.z*Z.z;
+ real denom=n.x*(Q.x-P.x)+n.y*(Q.y-P.y)+n.z*(Q.z-P.z);
+ return denom == 0 ? infinity : (d-n.x*P.x-n.y*P.y-n.z*P.z)/denom;
+}
+
+// Return any point on the intersection of the two planes with normals
+// n0 and n1 passing through points P0 and P1, respectively.
+// If the planes are parallel return (infinity,infinity,infinity).
+triple intersectionpoint(triple n0, triple P0, triple n1, triple P1)
+{
+ real Dx=n0.y*n1.z-n1.y*n0.z;
+ real Dy=n0.z*n1.x-n1.z*n0.x;
+ real Dz=n0.x*n1.y-n1.x*n0.y;
+ if(abs(Dx) > abs(Dy) && abs(Dx) > abs(Dz)) {
+ Dx=1/Dx;
+ real d0=n0.y*P0.y+n0.z*P0.z;
+ real d1=n1.y*P1.y+n1.z*P1.z+n1.x*(P1.x-P0.x);
+ real y=(d0*n1.z-d1*n0.z)*Dx;
+ real z=(d1*n0.y-d0*n1.y)*Dx;
+ return (P0.x,y,z);
+ } else if(abs(Dy) > abs(Dz)) {
+ Dy=1/Dy;
+ real d0=n0.z*P0.z+n0.x*P0.x;
+ real d1=n1.z*P1.z+n1.x*P1.x+n1.y*(P1.y-P0.y);
+ real z=(d0*n1.x-d1*n0.x)*Dy;
+ real x=(d1*n0.z-d0*n1.z)*Dy;
+ return (x,P0.y,z);
+ } else {
+ if(Dz == 0) return (infinity,infinity,infinity);
+ Dz=1/Dz;
+ real d0=n0.x*P0.x+n0.y*P0.y;
+ real d1=n1.x*P1.x+n1.y*P1.y+n1.z*(P1.z-P0.z);
+ real x=(d0*n1.y-d1*n0.y)*Dz;
+ real y=(d1*n0.x-d0*n1.x)*Dz;
+ return (x,y,P0.z);
+ }
+}
+
+// Given a real array a, return its partial sums.
+real[] partialsum(real[] a)
+{
+ real[] b=new real[a.length];
+ real sum=0;
+ for(int i=0; i < a.length; ++i) {
+ sum += a[i];
+ b[i]=sum;
+ }
+ return b;
+}
+
+// Given a real array a, return its partial dx-weighted sums.
+real[] partialsum(real[] a, real[] dx)
+{
+ real[] b=new real[a.length];
+ real sum=0;
+ for(int i=0; i < a.length; ++i) {
+ sum += a[i]*dx[i];
+ b[i]=sum;
+ }
+ return b;
+}
+
+// Given an integer array a, return its partial sums.
+int[] partialsum(int[] a)
+{
+ int[] b=new int[a.length];
+ int sum=0;
+ for(int i=0; i < a.length; ++i) {
+ sum += a[i];
+ b[i]=sum;
+ }
+ return b;
+}
+
+// Given an integer array a, return its partial dx-weighted sums.
+int[] partialsum(int[] a, int[] dx)
+{
+ int[] b=new int[a.length];
+ int sum=0;
+ for(int i=0; i < a.length; ++i) {
+ sum += a[i]*dx[i];
+ b[i]=sum;
+ }
+ return b;
+}
+
+// If strict=false, return whether i > j implies a[i] >= a[j]
+// If strict=true, return whether i > j implies a[i] > a[j]
+bool increasing(real[] a, bool strict=false)
+{
+ real[] ap=copy(a);
+ ap.delete(0);
+ ap.push(0);
+ bool[] b=strict ? (ap > a) : (ap >= a);
+ b[a.length-1]=true;
+ return all(b);
+}
+
+// Return the indices of consecutive true-element segments of bool[] b.
+int[][] segment(bool[] b)
+{
+ int[][] segment;
+ bool[] n=copy(b);
+ n.delete(0);
+ n.push(!b[b.length-1]);
+ int[] edge=(b != n) ? sequence(1,b.length) : null;
+ edge.insert(0,0);
+ int stop=edge[0];
+ for(int i=0; i < edge.length-1;) {
+ int start=stop;
+ stop=edge[++i];
+ if(b[start])
+ segment.push(sequence(start,stop-1));
+ }
+ return segment;
+}
+
+// If the sorted array a does not contain x, insert it sequentially,
+// returning the index of x in the resulting array.
+int unique(real[] a, real x) {
+ int i=search(a,x);
+ if(i == -1 || x != a[i]) {
+ ++i;
+ a.insert(i,x);
+ return i;
+ }
+ return i;
+}
+
+int unique(string[] a, string x) {
+ int i=search(a,x);
+ if(i == -1 || x != a[i]) {
+ ++i;
+ a.insert(i,x);
+ return i;
+ }
+ return i;
+}
+
+bool lexorder(pair a, pair b) {
+ return a.x < b.x || (a.x == b.x && a.y < b.y);
+}
+
+bool lexorder(triple a, triple b) {
+ return a.x < b.x || (a.x == b.x && (a.y < b.y || (a.y == b.y && a.z < b.z)));
+}
+
+real[] zero(int n)
+{
+ return sequence(new real(int) {return 0;},n);
+}
+
+real[][] zero(int n, int m)
+{
+ real[][] M=new real[n][];
+ for(int i=0; i < n; ++i)
+ M[i]=sequence(new real(int) {return 0;},m);
+ return M;
+}
+
+bool square(real[][] m)
+{
+ int n=m.length;
+ for(int i=0; i < n; ++i)
+ if(m[i].length != n) return false;
+ return true;
+}
+
+bool rectangular(real[][] m)
+{
+ int n=m.length;
+ if(n > 0) {
+ int m0=m[0].length;
+ for(int i=1; i < n; ++i)
+ if(m[i].length != m0) return false;
+ }
+ return true;
+}
+
+bool rectangular(pair[][] m)
+{
+ int n=m.length;
+ if(n > 0) {
+ int m0=m[0].length;
+ for(int i=1; i < n; ++i)
+ if(m[i].length != m0) return false;
+ }
+ return true;
+}
+
+bool rectangular(triple[][] m)
+{
+ int n=m.length;
+ if(n > 0) {
+ int m0=m[0].length;
+ for(int i=1; i < n; ++i)
+ if(m[i].length != m0) return false;
+ }
+ return true;
+}
+
+// draw the (infinite) line going through P and Q, without altering the
+// size of picture pic.
+void drawline(picture pic=currentpicture, pair P, pair Q, pen p=currentpen)
+{
+ pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
+ // Reduce the bounds by the size of the pen.
+ m -= min(p); M -= max(p);
+
+ // Calculate the points and direction vector in the transformed space.
+ t=t*T;
+ pair z=t*P;
+ pair v=t*Q-z;
+
+ // Handle horizontal and vertical lines.
+ if(v.x == 0) {
+ if(m.x <= z.x && z.x <= M.x)
+ draw(f,(z.x,m.y)--(z.x,M.y),p);
+ } else if(v.y == 0) {
+ if(m.y <= z.y && z.y <= M.y)
+ draw(f,(m.x,z.y)--(M.x,z.y),p);
+ } else {
+ // Calculate the maximum and minimum t values allowed for the
+ // parametric equation z + t*v
+ real mx=(m.x-z.x)/v.x, Mx=(M.x-z.x)/v.x;
+ real my=(m.y-z.y)/v.y, My=(M.y-z.y)/v.y;
+ real tmin=max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My);
+ real tmax=min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my);
+ if(tmin <= tmax)
+ draw(f,z+tmin*v--z+tmax*v,p);
+ }
+ },true);
+}
+
+real interpolate(real[] x, real[] y, real x0, int i)
+{
+ int n=x.length;
+ if(n == 0) abort("Zero data points in interpolate");
+ if(n == 1) return y[0];
+ if(i < 0) {
+ real dx=x[1]-x[0];
+ return y[0]+(y[1]-y[0])/dx*(x0-x[0]);
+ }
+ if(i >= n-1) {
+ real dx=x[n-1]-x[n-2];
+ return y[n-1]+(y[n-1]-y[n-2])/dx*(x0-x[n-1]);
+ }
+
+ real D=x[i+1]-x[i];
+ real B=(x0-x[i])/D;
+ real A=1.0-B;
+ return A*y[i]+B*y[i+1];
+}
+
+// Linearly interpolate data points (x,y) to (x0,y0), where the elements of
+// real[] x are listed in ascending order and return y0. Values outside the
+// available data range are linearly extrapolated using the first derivative
+// at the nearest endpoint.
+real interpolate(real[] x, real[] y, real x0)
+{
+ return interpolate(x,y,x0,search(x,x0));
+}
+
+private string nopoint="point not found";
+
+// Return the nth intersection time of path g with the vertical line through x.
+real time(path g, real x, int n=0)
+{
+ real[] t=times(g,x);
+ if(t.length <= n) abort(nopoint);
+ return t[n];
+}
+
+// Return the nth intersection time of path g with the horizontal line through
+// (0,z.y).
+real time(path g, explicit pair z, int n=0)
+{
+ real[] t=times(g,z);
+ if(t.length <= n) abort(nopoint);
+ return t[n];
+}
+
+// Return the nth y value of g at x.
+real value(path g, real x, int n=0)
+{
+ return point(g,time(g,x,n)).y;
+}
+
+// Return the nth x value of g at y=z.y.
+real value(path g, explicit pair z, int n=0)
+{
+ return point(g,time(g,(0,z.y),n)).x;
+}
+
+// Return the nth slope of g at x.
+real slope(path g, real x, int n=0)
+{
+ pair a=dir(g,time(g,x,n));
+ return a.y/a.x;
+}
+
+// Return the nth slope of g at y=z.y.
+real slope(path g, explicit pair z, int n=0)
+{
+ pair a=dir(g,time(g,(0,z.y),n));
+ return a.y/a.x;
+}
+
+// A quartic complex root solver based on these references:
+// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html
+// Neumark, S., Solution of Cubic and Quartic Equations, Pergamon Press
+// Oxford (1965).
+pair[] quarticroots(real a, real b, real c, real d, real e)
+{
+ real Fuzz=100000*realEpsilon;
+
+ // Remove roots at numerical infinity.
+ if(abs(a) <= Fuzz*(abs(b)+Fuzz*(abs(c)+Fuzz*(abs(d)+Fuzz*abs(e)))))
+ return cubicroots(b,c,d,e);
+
+ // Detect roots at numerical zero.
+ if(abs(e) <= Fuzz*(abs(d)+Fuzz*(abs(c)+Fuzz*(abs(b)+Fuzz*abs(a)))))
+ return cubicroots(a,b,c,d);
+
+ real ainv=1/a;
+ b *= ainv;
+ c *= ainv;
+ d *= ainv;
+ e *= ainv;
+
+ pair[] roots;
+ real[] T=cubicroots(1,-2c,c^2+b*d-4e,d^2+b^2*e-b*c*d);
+ if(T.length == 0) return roots;
+
+ real t0=T[0];
+ pair[] sum=quadraticroots((1,0),(b,0),(t0,0));
+ pair[] product=quadraticroots((1,0),(t0-c,0),(e,0));
+
+ if(abs(sum[0]*product[0]+sum[1]*product[1]+d) <
+ abs(sum[0]*product[1]+sum[1]*product[0]+d))
+ product=reverse(product);
+
+ for(int i=0; i < 2; ++i)
+ roots.append(quadraticroots((1,0),-sum[i],product[i]));
+
+ return roots;
+}
+
+pair[][] fft(pair[][] a, int sign=1)
+{
+ pair[][] A=new pair[a.length][];
+ int k=0;
+ for(pair[] v : a) {
+ A[k]=fft(v,sign);
+ ++k;
+ }
+ a=transpose(A);
+ k=0;
+ for(pair[] v : a) {
+ A[k]=fft(v,sign);
+ ++k;
+ }
+ return transpose(A);
+}
diff --git a/Master/texmf-dist/asymptote/metapost.asy b/Master/texmf-dist/asymptote/metapost.asy
new file mode 100644
index 00000000000..525ffe755e1
--- /dev/null
+++ b/Master/texmf-dist/asymptote/metapost.asy
@@ -0,0 +1,19 @@
+// MetaPost compatibility routines
+
+path cuttings;
+
+path cutbefore(path p, path q)
+{
+ slice s=firstcut(p,q);
+ cuttings=s.before;
+ return s.after;
+}
+
+path cutafter(path p, path q)
+{
+ slice s=lastcut(p,q);
+ cuttings=s.after;
+ return s.before;
+}
+
+
diff --git a/Master/texmf-dist/asymptote/nopapersize.ps b/Master/texmf-dist/asymptote/nopapersize.ps
new file mode 100644
index 00000000000..67e31404744
--- /dev/null
+++ b/Master/texmf-dist/asymptote/nopapersize.ps
@@ -0,0 +1,3 @@
+@ a4size 0in 0in
+
+@ letterSize 0in 0in
diff --git a/Master/texmf-dist/asymptote/obj.asy b/Master/texmf-dist/asymptote/obj.asy
new file mode 100644
index 00000000000..8dde9341c42
--- /dev/null
+++ b/Master/texmf-dist/asymptote/obj.asy
@@ -0,0 +1,113 @@
+// A module for reading simple obj files with groups.
+// Authors: Jens Schwaiger and John Bowman
+//
+// Here simple means that :
+//
+// 1) all vertex statements should come before the face statements;
+//
+// 2) face informations with respect to texture and/or normal vectors are
+// ignored;
+//
+// 3) face statements only contain positive numbers(no relative positions).
+//
+// The reading process only takes into account lines starting with "v" or
+// "f" or "g"(group).
+
+import three;
+
+struct obj {
+ surface s;
+ material[] surfacepen;
+ pen[] meshpen;
+
+ path3[][] read(string datafile, bool verbose=false) {
+ file in=input(datafile).word().line();
+ triple[] vert;
+ path3[][] g;
+ g[0]=new path3[] ;
+ string[] G;
+ void Vertex(real x,real y ,real z) {vert.push((x,y,z));}
+ void Face(int[] vertnr, int groupnr) {
+ guide3 gh;
+ for(int i=0; i < vertnr.length; ++i)
+ gh=gh--vert[vertnr[i]-1];
+ gh=gh--cycle;
+ g[groupnr].push(gh);
+ }
+ if(verbose) write("Reading data from "+datafile+".");
+ int groupnr;
+ while(true) {
+ string[] str=in;
+ if(str.length == 0) break;
+ str=sequence(new string(int i) {return split(str[i],"/")[0];},str.length);
+ if(str[0] == "g" && str.length > 1) {
+ int tst=find(G == str[1]);
+ if(tst == -1) {
+ G.push(str[1]);
+ groupnr=G.length-1;
+ g[groupnr]=new path3[] ;
+ }
+ if(tst > -1) groupnr=tst;
+ }
+ if(str[0] == "v") Vertex((real) str[1],(real) str[2],(real) str[3]);
+ if(str[0] == "f") {
+ int[] vertnr;
+ for(int i=1; i < str.length; ++i) vertnr[i-1]=(int) str[i];
+ Face(vertnr,groupnr);
+ }
+ if(eof(in)) break;
+ }
+ close(in);
+ if(verbose) {
+ write("Number of groups: ",G.length);
+ write("Groups and their names:");
+ write(G);
+ write("Reading done.");
+ write("Number of faces contained in the groups: ");
+ for(int j=0; j < G.length; ++j)
+ write(G[j],": ",(string) g[j].length);
+ }
+ return g;
+ }
+
+ void operator init(path3[][] g, material[] surfacepen, pen[] meshpen) {
+ for(int i=0; i < g.length; ++i) {
+ path3[] gi=g[i];
+ for(int j=0; j < gi.length; ++j) {
+ // Treat all faces as planar to avoid subdivision cracks.
+ surface sij=surface(gi[j],planar=true);
+ s.append(sij);
+ this.surfacepen.append(array(sij.s.length,surfacepen[i]));
+ this.meshpen.append(array(sij.s.length,meshpen[i]));
+ }
+ }
+ }
+
+ void operator init(string datafile, bool verbose=false,
+ material[] surfacepen, pen[] meshpen=nullpens) {
+ operator init(read(datafile,verbose),surfacepen,meshpen);
+ }
+
+ void operator init(string datafile, bool verbose=false,
+ material surfacepen, pen meshpen=nullpen) {
+ material[] surfacepen={surfacepen};
+ pen[] meshpen={meshpen};
+ surfacepen.cyclic=true;
+ meshpen.cyclic=true;
+ operator init(read(datafile,verbose),surfacepen,meshpen);
+ }
+}
+
+obj operator * (transform3 T, obj o)
+{
+ obj ot;
+ ot.s=T*o.s;
+ ot.surfacepen=copy(o.surfacepen);
+ ot.meshpen=copy(o.meshpen);
+ return ot;
+}
+
+void draw(picture pic=currentpicture, obj o, light light=currentlight)
+{
+ draw(pic,o.s,o.surfacepen,o.meshpen,light);
+}
diff --git a/Master/texmf-dist/asymptote/ode.asy b/Master/texmf-dist/asymptote/ode.asy
new file mode 100644
index 00000000000..0b88060e081
--- /dev/null
+++ b/Master/texmf-dist/asymptote/ode.asy
@@ -0,0 +1,457 @@
+real stepfactor=2; // Maximum dynamic step size adjustment factor.
+
+struct coefficients
+{
+ real[] steps;
+ real[] factors;
+ real[][] weights;
+ real[] highOrderWeights;
+ real[] lowOrderWeights;
+}
+
+struct RKTableau
+{
+ int order;
+ coefficients a;
+ void stepDependence(real h, real c, coefficients a) {}
+
+ real pgrow;
+ real pshrink;
+ bool exponential;
+
+ void operator init(int order, real[][] weights, real[] highOrderWeights,
+ real[] lowOrderWeights=new real[],
+ real[] steps=sequence(new real(int i) {
+ return sum(weights[i]);},weights.length),
+ void stepDependence(real, real, coefficients)=null) {
+ this.order=order;
+ a.steps=steps;
+ a.factors=array(a.steps.length+1,1);
+ a.weights=weights;
+ a.highOrderWeights=highOrderWeights;
+ a.lowOrderWeights=lowOrderWeights;
+ if(stepDependence != null) {
+ this.stepDependence=stepDependence;
+ exponential=true;
+ }
+ pgrow=(order > 0) ? 1/order : 0;
+ pshrink=(order > 1) ? 1/(order-1) : pgrow;
+ }
+}
+
+real[] Coeff={1,1/2,1/6,1/24,1/120,1/720,1/5040,1/40320,1/362880,1/3628800,
+ 1/39916800.0,1/479001600.0,1/6227020800.0,1/87178291200.0,
+ 1/1307674368000.0,1/20922789888000.0,1/355687428096000.0,
+ 1/6402373705728000.0,1/121645100408832000.0,
+ 1/2432902008176640000.0,1/51090942171709440000.0,
+ 1/1124000727777607680000.0};
+
+real phi1(real x) {return x != 0 ? expm1(x)/x : 1;}
+
+real phi2(real x)
+{
+ real x2=x*x;
+ if(fabs(x) > 1) return (exp(x)-x-1)/x2;
+ real x3=x2*x;
+ real x5=x2*x3;
+ if(fabs(x) < 0.1)
+ return Coeff[1]+x*Coeff[2]+x2*Coeff[3]+x3*Coeff[4]+x2*x2*Coeff[5]
+ +x5*Coeff[6]+x3*x3*Coeff[7]+x5*x2*Coeff[8]+x5*x3*Coeff[9];
+ else {
+ real x7=x5*x2;
+ real x8=x7*x;
+ return Coeff[1]+x*Coeff[2]+x2*Coeff[3]+x3*Coeff[4]+x2*x2*Coeff[5]
+ +x5*Coeff[6]+x3*x3*Coeff[7]+x7*Coeff[8]+x8*Coeff[9]
+ +x8*x*Coeff[10]+x5*x5*Coeff[11]+x8*x3*Coeff[12]+x7*x5*Coeff[13]+
+ x8*x5*Coeff[14]+x7*x7*Coeff[15]+x8*x7*Coeff[16]+x8*x8*Coeff[17];
+ }
+}
+
+real phi3(real x)
+{
+ real x2=x*x;
+ real x3=x2*x;
+ if(fabs(x) > 1.6) return (exp(x)-0.5*x2-x-1)/x3;
+ real x5=x2*x3;
+ if(fabs(x) < 0.1)
+ return Coeff[2]+x*Coeff[3]+x2*Coeff[4]+x3*Coeff[5]
+ +x2*x2*Coeff[6]+x5*Coeff[7]+x3*x3*Coeff[8]+x5*x2*Coeff[9]
+ +x5*x3*Coeff[10];
+ else {
+ real x7=x5*x2;
+ real x8=x7*x;
+ real x16=x8*x8;
+ return Coeff[2]+x*Coeff[3]+x2*Coeff[4]+x3*Coeff[5]
+ +x2*x2*Coeff[6]+x5*Coeff[7]+x3*x3*Coeff[8]+x5*x2*Coeff[9]
+ +x5*x3*Coeff[10]+x8*x*Coeff[11]
+ +x5*x5*Coeff[12]+x8*x3*Coeff[13]+x7*x5*Coeff[14]
+ +x8*x5*Coeff[15]+x7*x7*Coeff[16]+x8*x7*Coeff[17]+x16*Coeff[18]
+ +x16*x*Coeff[19]+x16*x2*Coeff[20];
+ }
+}
+
+void expfactors(real x, coefficients a)
+{
+ for(int i=0; i < a.steps.length; ++i)
+ a.factors[i]=exp(x*a.steps[i]);
+ a.factors[a.steps.length]=exp(x);
+}
+
+// First-Order Euler
+RKTableau Euler=RKTableau(1,new real[][],
+ new real[] {1});
+
+// First-Order Exponential Euler
+RKTableau E_Euler=RKTableau(1,new real[][], new real[] {1},
+ new void(real h, real c, coefficients a) {
+ real x=-c*h;
+ expfactors(x,a);
+ a.highOrderWeights[0]=phi1(x);
+ });
+
+// Second-Order Runge-Kutta
+RKTableau RK2=RKTableau(2,new real[][] {{1/2}},
+ new real[] {0,1}, // 2nd order
+ new real[] {1,0}); // 1st order
+
+// Second-Order Exponential Runge-Kutta
+RKTableau E_RK2=RKTableau(2,new real[][] {{1/2}},
+ new real[] {0,1}, // 2nd order
+ new real[] {1,0}, // 1st order
+ new void(real h, real c, coefficients a) {
+ real x=-c*h;
+ expfactors(x,a);
+ a.weights[0][0]=1/2*phi1(x/2);
+ real w=phi1(x);
+ a.highOrderWeights[0]=0;
+ a.highOrderWeights[1]=w;
+ a.lowOrderWeights[0]=w;
+ });
+
+// Second-Order Predictor-Corrector
+RKTableau PC=RKTableau(2,new real[][] {{1}},
+ new real[] {1/2,1/2}, // 2nd order
+ new real[] {1,0}); // 1st order
+
+// Second-Order Exponential Predictor-Corrector
+RKTableau E_PC=RKTableau(2,new real[][] {{1}},
+ new real[] {1/2,1/2}, // 2nd order
+ new real[] {1,0}, // 1st order
+ new void(real h, real c, coefficients a) {
+ real x=-c*h;
+ expfactors(x,a);
+ real w=phi1(x);
+ a.weights[0][0]=w;
+ a.highOrderWeights[0]=w/2;
+ a.highOrderWeights[1]=w/2;
+ a.lowOrderWeights[0]=w;
+ });
+
+// Third-Order Classical Runge-Kutta
+RKTableau RK3=RKTableau(3,new real[][] {{1/2},{-1,2}},
+ new real[] {1/6,2/3,1/6});
+
+// Third-Order Bogacki-Shampine Runge-Kutta
+RKTableau RK3BS=RKTableau(3,new real[][] {{1/2},{0,3/4}},
+ new real[] {2/9,1/3,4/9}, // 3rd order
+ new real[] {7/24,1/4,1/3,1/8}); // 2nd order
+
+// Third-Order Exponential Bogacki-Shampine Runge-Kutta
+RKTableau E_RK3BS=RKTableau(3,new real[][] {{1/2},{0,3/4}},
+ new real[] {2/9,1/3,4/9}, // 3rd order
+ new real[] {7/24,1/4,1/3,1/8}, // 2nd order
+ new void(real h, real c, coefficients a) {
+ real x=-c*h;
+ expfactors(x,a);
+ real w=phi1(x);
+ real w2=phi2(x);
+ a.weights[0][0]=1/2*phi1(x/2);
+ real a11=9/8*phi2(3/4*x)+3/8*phi2(x/2);
+ a.weights[1][0]=3/4*phi1(3/4*x)-a11;
+ a.weights[1][1]=a11;
+ real a21=1/3*w;
+ real a22=4/3*w2-2/9*w;
+ a.highOrderWeights[0]=w-a21-a22;
+ a.highOrderWeights[1]=a21;
+ a.highOrderWeights[2]=a22;
+ a.lowOrderWeights[0]=w-17/12*w2;
+ a.lowOrderWeights[1]=w2/2;
+ a.lowOrderWeights[2]=2/3*w2;
+ a.lowOrderWeights[3]=w2/4;
+ });
+
+// Fourth-Order Classical Runge-Kutta
+RKTableau RK4=RKTableau(4,new real[][] {{1/2},{0,1/2},{0,0,1}},
+ new real[] {1/6,1/3,1/3,1/6});
+
+// Fifth-Order Cash-Karp Runge-Kutta
+RKTableau RK5=RKTableau(5,new real[][] {{1/5},
+ {3/40,9/40},
+ {3/10,-9/10,6/5},
+ {-11/54,5/2,-70/27,35/27},
+ {1631/55296,175/512,575/13824,
+ 44275/110592,253/4096}},
+ new real[] {37/378,0,250/621,125/594,
+ 0,512/1771}, // 5th order
+ new real[] {2825/27648,0,18575/48384,13525/55296,
+ 277/14336,1/4}); // 4th order
+
+// Fifth-Order Fehlberg Runge-Kutta
+RKTableau RK5F=RKTableau(5,new real[][] {{1/4},
+ {3/32,9/32},
+ {1932/2197,-7200/2197,7296/2197},
+ {439/216,-8,3680/513,-845/4104},
+ {-8/27,2,-3544/2565,1859/4104,
+ -11/40}},
+ new real[] {16/135,0,6656/12825,28561/56430,-9/50,2/55}, // 5th order
+ new real[] {25/216,0,1408/2565,2197/4104,-1/5,0}); // 4th order
+
+// Fifth-Order Dormand-Prince Runge-Kutta
+RKTableau RK5DP=RKTableau(5,new real[][] {{1/5},
+ {3/40,9/40},
+ {44/45,-56/15,32/9},
+ {19372/6561,-25360/2187,64448/6561,
+ -212/729},
+ {9017/3168,-355/33,46732/5247,49/176,
+ -5103/18656}},
+ new real[] {35/384,0,500/1113,125/192,-2187/6784,
+ 11/84}, // 5th order
+ new real[] {5179/57600,0,7571/16695,393/640,
+ -92097/339200,187/2100,1/40}); // 4th order
+
+real error(real error, real initial, real lowOrder, real norm, real diff)
+{
+ if(initial != 0 && lowOrder != initial) {
+ static real epsilon=realMin/realEpsilon;
+ real denom=max(abs(norm),abs(initial))+epsilon;
+ return max(error,max(abs(diff)/denom));
+ }
+ return error;
+}
+
+void report(real old, real h, real t)
+{
+ write("Time step changed from "+(string) old+" to "+(string) h+" at t="+
+ (string) t+".");
+}
+
+real adjust(real h, real error, real tolmin, real tolmax, RKTableau tableau)
+{
+ if(error > tolmax)
+ h *= max((tolmin/error)^tableau.pshrink,1/stepfactor);
+ else if(error > 0 && error < tolmin)
+ h *= min((tolmin/error)^tableau.pgrow,stepfactor);
+ return h;
+}
+
+struct solution
+{
+ real[] t;
+ real[] y;
+}
+
+void write(solution S)
+{
+ for(int i=0; i < S.t.length; ++i)
+ write(S.t[i],S.y[i]);
+}
+
+// Integrate dy/dt+cy=f(t,y) from a to b using initial conditions y,
+// specifying either the step size h or the number of steps n.
+solution integrate(real y, real c=0, real g(real t, real y), real a, real b=a,
+ real h=0, int n=0, bool dynamic=false, real tolmin=0,
+ real tolmax=0, real dtmin=0, real dtmax=realMax,
+ RKTableau tableau, bool verbose=false)
+{
+ solution S;
+ S.t=new real[] {a};
+ S.y=new real[]{y};
+
+ if(h == 0) {
+ if(b == a) return S;
+ if(n == 0) abort("Either n or h must be specified");
+ else h=(b-a)/n;
+ }
+
+ real f(real t, real y)=(c == 0 || tableau.exponential) ? g :
+ new real(real t, real y) {return g(t,y)-c*y;};
+
+ tableau.stepDependence(h,c,tableau.a);
+
+ real t=a;
+ real f0;
+ if(tableau.a.lowOrderWeights.length == 0) dynamic=false;
+ bool fsal=dynamic &&
+ (tableau.a.lowOrderWeights.length > tableau.a.highOrderWeights.length);
+ if(fsal) f0=f(t,y);
+
+ real dt=h;
+ while(t < b) {
+ h=min(h,b-t);
+ if(t+h == t) break;
+ if(h != dt) {
+ if(verbose) report(dt,h,t);
+ tableau.stepDependence(h,c,tableau.a);
+ dt=h;
+ }
+
+ real[] predictions={fsal ? f0 : f(t,y)};
+ for(int i=0; i < tableau.a.steps.length; ++i)
+ predictions.push(f(t+h*tableau.a.steps[i],
+ tableau.a.factors[i]*y+h*dot(tableau.a.weights[i],
+ predictions)));
+
+ real highOrder=h*dot(tableau.a.highOrderWeights,predictions);
+ real y0=tableau.a.factors[tableau.a.steps.length]*y;
+ if(dynamic) {
+ real f1;
+ if(fsal) {
+ f1=f(t+h,y0+highOrder);
+ predictions.push(f1);
+ }
+ real lowOrder=h*dot(tableau.a.lowOrderWeights,predictions);
+ real error;
+ error=error(error,y,y0+lowOrder,y0+highOrder,highOrder-lowOrder);
+ h=adjust(h,error,tolmin,tolmax,tableau);
+ if(h >= dt) {
+ t += dt;
+ y=y0+highOrder;
+ S.t.push(t);
+ S.y.push(y);
+ f0=f1;
+ }
+ h=min(max(h,dtmin),dtmax);
+ } else {
+ t += h;
+ y=y0+highOrder;
+ S.y.push(y);
+ }
+ }
+ return S;
+}
+
+struct Solution
+{
+ real[] t;
+ real[][] y;
+}
+
+void write(Solution S)
+{
+ for(int i=0; i < S.t.length; ++i) {
+ write(S.t[i],tab);
+ for(real y : S.y[i])
+ write(y,tab);
+ write();
+ }
+}
+
+// Integrate a set of equations, dy/dt=f(t,y), from a to b using initial
+// conditions y, specifying either the step size h or the number of steps n.
+Solution integrate(real[] y, real[] f(real t, real[] y), real a, real b=a,
+ real h=0, int n=0, bool dynamic=false,
+ real tolmin=0, real tolmax=0, real dtmin=0,
+ real dtmax=realMax, RKTableau tableau, bool verbose=false)
+{
+ Solution S;
+ S.t=new real[] {a};
+ S.y=new real[][] {copy(y)};
+
+ if(h == 0) {
+ if(b == a) return S;
+ if(n == 0) abort("Either n or h must be specified");
+ else h=(b-a)/n;
+ }
+ real t=a;
+ real[] f0;
+ if(tableau.a.lowOrderWeights.length == 0) dynamic=false;
+ bool fsal=dynamic &&
+ (tableau.a.lowOrderWeights.length > tableau.a.highOrderWeights.length);
+ if(fsal) f0=f(t,y);
+
+ real dt=h;
+ while(t < b) {
+ h=min(h,b-t);
+ if(t+h == t) break;
+ if(h != dt) {
+ if(verbose) report(dt,h,t);
+ dt=h;
+ }
+
+ real[][] predictions={fsal ? f0 : f(t,y)};
+ for(int i=0; i < tableau.a.steps.length; ++i)
+ predictions.push(f(t+h*tableau.a.steps[i],
+ y+h*tableau.a.weights[i]*predictions));
+
+ real[] highOrder=h*tableau.a.highOrderWeights*predictions;
+ if(dynamic) {
+ real[] f1;
+ if(fsal) {
+ f1=f(t+h,y+highOrder);
+ predictions.push(f1);
+ }
+ real[] lowOrder=h*tableau.a.lowOrderWeights*predictions;
+ real error;
+ for(int i=0; i < y.length; ++i)
+ error=error(error,y[i],y[i]+lowOrder[i],y[i]+highOrder[i],
+ highOrder[i]-lowOrder[i]);
+ h=adjust(h,error,tolmin,tolmax,tableau);
+ if(h >= dt) {
+ t += dt;
+ y += highOrder;
+ S.t.push(t);
+ S.y.push(y);
+ f0=f1;
+ }
+ h=min(max(h,dtmin),dtmax);
+ } else {
+ t += h;
+ y += highOrder;
+ S.t.push(t);
+ S.y.push(y);
+ }
+ }
+ return S;
+}
+
+real[][] finiteDifferenceJacobian(real[] f(real[]), real[] t,
+ real[] h=sqrtEpsilon*abs(t))
+{
+ real[] ft=f(t);
+ real[][] J=new real[t.length][ft.length];
+ real[] ti=copy(t);
+ real tlast=ti[0];
+ ti[0] += h[0];
+ J[0]=(f(ti)-ft)/h[0];
+ for(int i=1; i < t.length; ++i) {
+ ti[i-1]=tlast;
+ tlast=ti[i];
+ ti[i] += h[i];
+ J[i]=(f(ti)-ft)/h[i];
+ }
+ return transpose(J);
+}
+
+// Solve simultaneous nonlinear system by Newton's method.
+real[] newton(int iterations=100, real[] f(real[]), real[][] jacobian(real[]),
+ real[] t)
+{
+ real[] t=copy(t);
+ for(int i=0; i < iterations; ++i)
+ t += solve(jacobian(t),-f(t));
+ return t;
+}
+
+real[] solveBVP(real[] f(real, real[]), real a, real b=a, real h=0, int n=0,
+ bool dynamic=false, real tolmin=0, real tolmax=0, real dtmin=0,
+ real dtmax=realMax, RKTableau tableau, bool verbose=false,
+ real[] initial(real[]), real[] discrepancy(real[]),
+ real[] guess, int iterations=100)
+{
+ real[] g(real[] t) {
+ real[][] y=integrate(initial(t),f,a,b,h,n,dynamic,tolmin,tolmax,dtmin,dtmax,
+ tableau,verbose).y;return discrepancy(y[y.length-1]);
+ }
+ real[][] jacobian(real[] t) {return finiteDifferenceJacobian(g,t);}
+ return initial(newton(iterations,g,jacobian,guess));
+}
diff --git a/Master/texmf-dist/asymptote/palette.asy b/Master/texmf-dist/asymptote/palette.asy
new file mode 100644
index 00000000000..b1d1985a323
--- /dev/null
+++ b/Master/texmf-dist/asymptote/palette.asy
@@ -0,0 +1,495 @@
+private import graph;
+
+private transform swap=(0,0,0,1,1,0);
+
+typedef bounds range(picture pic, real min, real max);
+
+range Range(bool automin=false, real min=-infinity,
+ bool automax=false, real max=infinity)
+{
+ return new bounds(picture pic, real dmin, real dmax) {
+ // autoscale routine finds reasonable limits
+ bounds mz=autoscale(pic.scale.z.T(dmin),
+ pic.scale.z.T(dmax),
+ pic.scale.z.scale);
+ // If automin/max, use autoscale result, else
+ // if min/max is finite, use specified value, else
+ // use minimum/maximum data value
+ real pmin=automin ? pic.scale.z.Tinv(mz.min) : (finite(min) ? min : dmin);
+ real pmax=automax ? pic.scale.z.Tinv(mz.max) : (finite(max) ? max : dmax);
+ return bounds(pmin,pmax);
+ };
+}
+
+range Automatic=Range(true,true);
+range Full=Range();
+
+void image(frame f, real[][] data, pair initial, pair final, pen[] palette,
+ bool transpose=(initial.x < final.x && initial.y < final.y),
+ transform t=identity(), bool copy=true, bool antialias=false)
+{
+ transform T=transpose ? swap : identity();
+ _image(f,copy ? copy(data) : data,T*initial,T*final,palette,t*T,copy=false,
+ antialias=antialias);
+}
+
+void image(frame f, pen[][] data, pair initial, pair final,
+ bool transpose=(initial.x < final.x && initial.y < final.y),
+ transform t=identity(), bool copy=true, bool antialias=false)
+{
+ transform T=transpose ? swap : identity();
+ _image(f,copy ? copy(data) : data,T*initial,T*final,t*T,copy=false,
+ antialias=antialias);
+}
+
+// Reduce color palette to approximate range of data relative to "display"
+// range => errors of 1/palette.length in resulting color space.
+pen[] adjust(picture pic, real min, real max, real rmin, real rmax,
+ pen[] palette)
+{
+ real dmin=pic.scale.z.T(min);
+ real dmax=pic.scale.z.T(max);
+ real delta=rmax-rmin;
+ if(delta > 0) {
+ real factor=palette.length/delta;
+ int minindex=floor(factor*(dmin-rmin));
+ if(minindex < 0) minindex=0;
+ int maxindex=ceil(factor*(dmax-rmin));
+ if(maxindex > palette.length) maxindex=palette.length;
+ if(minindex > 0 || maxindex < palette.length)
+ return palette[minindex:maxindex];
+ }
+ return palette;
+}
+
+private real[] sequencereal;
+
+bounds image(picture pic=currentpicture, real[][] f, range range=Full,
+ pair initial, pair final, pen[] palette,
+ bool transpose=(initial.x < final.x && initial.y < final.y),
+ bool copy=true, bool antialias=false)
+{
+ if(copy) f=copy(f);
+ if(copy) palette=copy(palette);
+
+ real m=min(f);
+ real M=max(f);
+ bounds bounds=range(pic,m,M);
+ real rmin=pic.scale.z.T(bounds.min);
+ real rmax=pic.scale.z.T(bounds.max);
+ palette=adjust(pic,m,M,rmin,rmax,palette);
+
+ // Crop data to allowed range and scale
+ if(range != Full || pic.scale.z.scale.T != identity ||
+ pic.scale.z.postscale.T != identity) {
+ scalefcn T=pic.scale.z.T;
+ real m=bounds.min;
+ real M=bounds.max;
+ for(int i=0; i < f.length; ++i)
+ f[i]=map(new real(real x) {return T(min(max(x,m),M));},f[i]);
+ }
+
+ initial=Scale(pic,initial);
+ final=Scale(pic,final);
+
+ transform T=transpose ? swap : identity();
+ pic.add(new void(frame F, transform t) {
+ _image(F,f,T*initial,T*final,palette,t*T,copy=false,antialias=antialias);
+ },true);
+ pic.addBox(initial,final);
+ return bounds; // Return bounds used for color space
+}
+
+bounds image(picture pic=currentpicture, real f(real, real),
+ range range=Full, pair initial, pair final,
+ int nx=ngraph, int ny=nx, pen[] palette, bool antialias=false)
+{
+ // Generate data, taking scaling into account
+ real xmin=pic.scale.x.T(initial.x);
+ real xmax=pic.scale.x.T(final.x);
+ real ymin=pic.scale.y.T(initial.y);
+ real ymax=pic.scale.y.T(final.y);
+ real[][] data=new real[ny][nx];
+ for(int j=0; j < ny; ++j) {
+ real y=pic.scale.y.Tinv(interp(ymin,ymax,(j+0.5)/nx));
+ scalefcn Tinv=pic.scale.x.Tinv;
+ // Take center point of each bin
+ data[j]=sequence(new real(int i) {
+ return f(Tinv(interp(xmin,xmax,(i+0.5)/ny)),y);
+ },nx);
+ }
+ return image(pic,data,range,initial,final,palette,transpose=false,
+ copy=false,antialias=antialias);
+}
+
+void image(picture pic=currentpicture, pen[][] data, pair initial, pair final,
+ bool transpose=(initial.x < final.x && initial.y < final.y),
+ bool copy=true, bool antialias=false)
+{
+ if(copy) data=copy(data);
+
+ initial=Scale(pic,initial);
+ final=Scale(pic,final);
+
+ transform T=transpose ? swap : identity();
+ pic.add(new void(frame F, transform t) {
+ _image(F,data,T*initial,T*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,
+ pair initial, pair final,
+ bool transpose=(initial.x < final.x && initial.y < final.y),
+ bool antialias=false)
+{
+ initial=Scale(pic,initial);
+ final=Scale(pic,final);
+
+ transform T=transpose ? swap : identity();
+ pic.add(new void(frame F, transform t) {
+ _image(F,f,width,height,T*initial,T*final,t*T,antialias=antialias);
+ },true);
+ pic.addBox(initial,final);
+}
+
+bounds image(picture pic=currentpicture, pair[] z, real[] f,
+ range range=Full, pen[] palette)
+{
+ if(z.length != f.length)
+ abort("z and f arrays have different lengths");
+
+ real m=min(f);
+ real M=max(f);
+ bounds bounds=range(pic,m,M);
+ real rmin=pic.scale.z.T(bounds.min);
+ real rmax=pic.scale.z.T(bounds.max);
+
+ palette=adjust(pic,m,M,rmin,rmax,palette);
+ rmin=max(rmin,m);
+ rmax=min(rmax,M);
+
+ // Crop data to allowed range and scale
+ if(range != Full || pic.scale.z.scale.T != identity ||
+ pic.scale.z.postscale.T != identity) {
+ scalefcn T=pic.scale.z.T;
+ real m=bounds.min;
+ real M=bounds.max;
+ f=map(new real(real x) {return T(min(max(x,m),M));},f);
+ }
+
+ int[] edges={0,0,1};
+ int N=palette.length-1;
+
+ int[][] trn=triangulate(z);
+ real step=rmax == rmin ? 0.0 : N/(rmax-rmin);
+ for(int i=0; i < trn.length; ++i) {
+ int[] trni=trn[i];
+ int i0=trni[0], i1=trni[1], i2=trni[2];
+ pen color(int i) {return palette[round((f[i]-rmin)*step)];}
+ gouraudshade(pic,z[i0]--z[i1]--z[i2]--cycle,
+ new pen[] {color(i0),color(i1),color(i2)},edges);
+ }
+ return bounds; // Return bounds used for color space
+}
+
+bounds image(picture pic=currentpicture, real[] x, real[] y, real[] f,
+ range range=Full, pen[] palette)
+{
+ int n=x.length;
+ if(n != y.length)
+ abort("x and y arrays have different lengths");
+
+ pair[] z=sequence(new pair(int i) {return (x[i],y[i]);},n);
+ return image(pic,z,f,range,palette);
+}
+
+// Construct a pen[] array from f using the specified palette.
+pen[] palette(real[] f, pen[] palette)
+{
+ real Min=min(f);
+ real Max=max(f);
+ if(palette.length == 0) return new pen[];
+ real step=Max == Min ? 0.0 : (palette.length-1)/(Max-Min);
+ return sequence(new pen(int i) {return palette[round((f[i]-Min)*step)];},
+ f.length);
+}
+
+// Construct a pen[][] array from f using the specified palette.
+pen[][] palette(real[][] f, pen[] palette)
+{
+ real Min=min(f);
+ real Max=max(f);
+ int n=f.length;
+ int m=n > 0 ? f[0].length : 0;
+ pen[][] p=new pen[n][m];
+ real step=(Max == Min) ? 0.0 : (palette.length-1)/(Max-Min);
+ for(int i=0; i < n; ++i) {
+ real[] fi=f[i];
+ p[i]=sequence(new pen(int j) {return palette[round((fi[j]-Min)*step)];},m);
+ }
+ return p;
+}
+
+typedef ticks paletteticks(int sign=-1);
+
+paletteticks PaletteTicks(Label format="", ticklabel ticklabel=null,
+ bool beginlabel=true, bool endlabel=true,
+ int N=0, int n=0, real Step=0, real step=0,
+ pen pTick=nullpen, pen ptick=nullpen)
+{
+ return new ticks(int sign=-1) {
+ format.align(sign > 0 ? RightSide : LeftSide);
+ return Ticks(sign,format,ticklabel,beginlabel,endlabel,N,n,Step,step,
+ true,true,extend=true,pTick,ptick);
+ };
+}
+
+paletteticks PaletteTicks=PaletteTicks();
+
+void palette(picture pic=currentpicture, Label L="", bounds bounds,
+ pair initial, pair final, axis axis=Right, pen[] palette,
+ pen p=currentpen, paletteticks ticks=PaletteTicks,
+ bool copy=true, bool antialias=false)
+{
+ real initialz=pic.scale.z.T(bounds.min);
+ real finalz=pic.scale.z.T(bounds.max);
+ bounds mz=autoscale(initialz,finalz,pic.scale.z.scale);
+
+ axisT axis;
+ axis(pic,axis);
+ real angle=degrees(axis.align.dir);
+
+ initial=Scale(pic,initial);
+ final=Scale(pic,final);
+
+ pair lambda=final-initial;
+ bool vertical=(floor((angle+45)/90) % 2 == 0);
+ pair perp,par;
+
+ if(vertical) {perp=E; par=N;} else {perp=N; par=E;}
+
+ path g=(final-dot(lambda,par)*par)--final;
+ path g2=initial--final-dot(lambda,perp)*perp;
+
+ if(sgn(dot(lambda,perp)*dot(axis.align.dir,perp)) == -1) {
+ path tmp=g;
+ g=g2;
+ g2=tmp;
+ }
+
+ if(copy) palette=copy(palette);
+ Label L=L.copy();
+ if(L.defaultposition) L.position(0.5);
+ L.align(axis.align);
+ L.p(p);
+ if(vertical && L.defaulttransform) {
+ frame f;
+ add(f,Label(L.s,(0,0),L.p));
+ if(length(max(f)-min(f)) > ylabelwidth*fontsize(L.p))
+ L.transform(rotate(90));
+ }
+ real[][] pdata={sequence(palette.length)};
+
+ transform T=vertical ? swap : identity();
+ pic.add(new void(frame f, transform t) {
+ _image(f,pdata,T*initial,T*final,palette,t*T,copy=false,
+ antialias=antialias);
+ },true);
+
+ ticklocate locate=ticklocate(initialz,finalz,pic.scale.z,mz.min,mz.max);
+ axis(pic,L,g,g2,p,ticks(sgn(axis.side.x*dot(lambda,par))),locate,mz.divisor,
+ true);
+
+ pic.add(new void(frame f, transform t) {
+ pair Z0=t*initial;
+ pair Z1=t*final;
+ draw(f,Z0--(Z0.x,Z1.y)--Z1--(Z1.x,Z0.y)--cycle,p);
+ },true);
+
+ pic.addBox(initial,final);
+}
+
+// A grayscale palette
+pen[] Grayscale(int NColors=256)
+{
+ real ninv=1.0/(NColors-1.0);
+ return sequence(new pen(int i) {return gray(i*ninv);},NColors);
+}
+
+// A color wheel palette
+pen[] Wheel(int NColors=32766)
+{
+ if(settings.gray) return Grayscale(NColors);
+
+ int nintervals=6;
+ int n=-quotient(NColors,-nintervals);
+
+ pen[] Palette;
+ if(n == 0) return Palette;
+
+ Palette=new pen[n*nintervals];
+ real ninv=1.0/n;
+
+ for(int i=0; i < n; ++i) {
+ real ininv=i*ninv;
+ real ininv1=1.0-ininv;
+ Palette[i]=rgb(1.0,0.0,ininv);
+ Palette[n+i]=rgb(ininv1,0.0,1.0);
+ Palette[2n+i]=rgb(0.0,ininv,1.0);
+ Palette[3n+i]=rgb(0.0,1.0,ininv1);
+ Palette[4n+i]=rgb(ininv,1.0,0.0);
+ Palette[5n+i]=rgb(1.0,ininv1,0.0);
+ }
+ return Palette;
+}
+
+// A rainbow palette
+pen[] Rainbow(int NColors=32766)
+{
+ if(settings.gray) return Grayscale(NColors);
+
+ int offset=1;
+ int nintervals=5;
+ int n=-quotient(NColors-1,-nintervals);
+
+ pen[] Palette;
+ if(n == 0) return Palette;
+
+ Palette=new pen[n*nintervals+offset];
+ real ninv=1.0/n;
+
+ for(int i=0; i < n; ++i) {
+ real ininv=i*ninv;
+ real ininv1=1.0-ininv;
+ Palette[i]=rgb(ininv1,0.0,1.0);
+ Palette[n+i]=rgb(0.0,ininv,1.0);
+ Palette[2n+i]=rgb(0.0,1.0,ininv1);
+ Palette[3n+i]=rgb(ininv,1.0,0.0);
+ Palette[4n+i]=rgb(1.0,ininv1,0.0);
+ }
+ Palette[4n+n]=rgb(1.0,0.0,0.0);
+
+ return Palette;
+}
+
+private pen[] BWRainbow(int NColors, bool two)
+{
+ if(settings.gray) return Grayscale(NColors);
+
+ int offset=1;
+ int nintervals=6;
+ int divisor=3;
+
+ if(two) nintervals += 6;
+
+ int num=NColors-offset;
+ int n=-quotient(num,-nintervals*divisor)*divisor;
+ NColors=n*nintervals+offset;
+
+ pen[] Palette;
+ if(n == 0) return Palette;
+
+ Palette=new pen[NColors];
+ real ninv=1.0/n;
+
+ int k=0;
+
+ if(two) {
+ for(int i=0; i < n; ++i) {
+ real ininv=i*ninv;
+ real ininv1=1.0-ininv;
+ Palette[i]=rgb(ininv1,0.0,1.0);
+ Palette[n+i]=rgb(0.0,ininv,1.0);
+ Palette[2n+i]=rgb(0.0,1.0,ininv1);
+ Palette[3n+i]=rgb(ininv,1.0,0.0);
+ Palette[4n+i]=rgb(1.0,ininv1,0.0);
+ Palette[5n+i]=rgb(1.0,0.0,ininv);
+ }
+ k += 6n;
+ }
+
+ if(two)
+ for(int i=0; i < n; ++i)
+ Palette[k+i]=rgb(1.0-i*ninv,0.0,1.0);
+ else {
+ int n3=-quotient(n,-3);
+ int n23=2*n3;
+ real third=n3*ninv;
+ real twothirds=n23*ninv;
+ for(int i=0; i < n3; ++i) {
+ real ininv=i*ninv;
+ Palette[k+i]=rgb(ininv,0.0,ininv);
+ Palette[k+n3+i]=rgb(third,0.0,third+ininv);
+ Palette[k+n23+i]=rgb(third-ininv,0.0,twothirds+ininv);
+ }
+ }
+ k += n;
+
+ for(int i=0; i < n; ++i) {
+ real ininv=i*ninv;
+ real ininv1=1.0-ininv;
+ Palette[k+i]=rgb(0.0,ininv,1.0);
+ Palette[k+n+i]=rgb(0.0,1.0,ininv1);
+ Palette[k+2n+i]=rgb(ininv,1.0,0.0);
+ Palette[k+3n+i]=rgb(1.0,ininv1,0.0);
+ Palette[k+4n+i]=rgb(1.0,ininv,ininv);
+ }
+ Palette[k+5n]=rgb(1.0,1.0,1.0);
+
+ return Palette;
+}
+
+// Quantize palette to exactly n values
+pen[] quantize(pen[] Palette, int n)
+{
+ if(Palette.length == 0) abort("cannot quantize empty palette");
+ if(n <= 1) abort("palette must contain at least two pens");
+ real step=(Palette.length-1)/(n-1);
+ return sequence(new pen(int i) {
+ return Palette[round(i*step)];
+ },n);
+}
+
+// A rainbow palette tapering off to black/white at the spectrum ends,
+pen[] BWRainbow(int NColors=32761)
+{
+ return BWRainbow(NColors,false);
+}
+
+// A double rainbow palette tapering off to black/white at the spectrum ends,
+// with a linearly scaled intensity.
+pen[] BWRainbow2(int NColors=32761)
+{
+ pen[] Palette=BWRainbow(NColors,true);
+ int n=Palette.length;
+ real ninv=1.0/n;
+ for(int i=0; i < n; ++i)
+ Palette[i]=i*ninv*Palette[i];
+ return Palette;
+}
+
+//A palette varying linearly over the specified array of pens, using
+// NColors in each interpolation interval.
+pen[] Gradient(int NColors=256 ... pen[] p)
+{
+ pen[] P;
+ if(p.length < 2) abort("at least 2 colors must be specified");
+ real step=NColors > 1 ? (1/(NColors-1)) : 1;
+ for(int i=0; i < p.length-1; ++i) {
+ pen begin=p[i];
+ pen end=p[i+1];
+ P.append(sequence(new pen(int j) {
+ return interp(begin,end,j*step);
+ },NColors));
+ }
+ return P;
+}
+
+pen[] cmyk(pen[] Palette)
+{
+ int n=Palette.length;
+ for(int i=0; i < n; ++i)
+ Palette[i]=cmyk(Palette[i]);
+ return Palette;
+}
diff --git a/Master/texmf-dist/asymptote/patterns.asy b/Master/texmf-dist/asymptote/patterns.asy
new file mode 100644
index 00000000000..56fa3bf4a24
--- /dev/null
+++ b/Master/texmf-dist/asymptote/patterns.asy
@@ -0,0 +1,102 @@
+// Create a tiling named name from picture pic
+// with optional left-bottom margin lb and right-top margin rt.
+frame tiling(string name, picture pic, pair lb=0, pair rt=0)
+{
+ frame tiling;
+ frame f=pic.fit(identity());
+ pair pmin=min(f)-lb;
+ pair pmax=max(f)+rt;
+ string s="%.6f";
+ postscript(tiling,"<< /PaintType 1 /PatternType 1 /TilingType 1
+/BBox ["+format(s,pmin.x,"C")+" "+format(s,pmin.y,"C")+" "+
+ format(s,pmax.x,"C")+" "+format(s,pmax.y,"C")+"]
+/XStep "+format(s,pmax.x-pmin.x,"C")+"
+/YStep "+format(s,pmax.y-pmin.y,"C")+"
+/PaintProc {pop");
+ add(tiling,f);
+ postscript(tiling,"} >>
+ matrix makepattern
+/"+name+" exch def");
+ return tiling;
+}
+
+// Add to frame preamble a tiling name constructed from picture pic
+// with optional left-bottom margin lb and right-top margin rt.
+void add(string name, picture pic, pair lb=0, pair rt=0)
+{
+ add(currentpatterns,tiling(name,pic,lb,rt));
+}
+
+picture tile(real Hx=5mm, real Hy=0, pen p=currentpen,
+ filltype filltype=NoFill)
+{
+ picture tiling;
+ if(Hy == 0) Hy=Hx;
+ path tile=box((0,0),(Hx,Hy));
+ tiling.add(new void (frame f, transform t) {
+ filltype.fill(f,t*tile,p);
+ });
+ clip(tiling,tile);
+ return tiling;
+}
+
+picture checker(real Hx=5mm, real Hy=0, pen p=currentpen)
+{
+ picture tiling;
+ if(Hy == 0) Hy=Hx;
+ path tile=box((0,0),(Hx,Hy));
+ fill(tiling,tile,p);
+ fill(tiling,shift(Hx,Hy)*tile,p);
+ clip(tiling,box((0,0),(2Hx,2Hy)));
+ return tiling;
+}
+
+picture brick(real Hx=5mm, real Hy=0, pen p=currentpen)
+{
+ picture tiling;
+ if(Hy == 0) Hy=Hx/2;
+ path tile=box((0,0),(Hx,Hy));
+ draw(tiling,tile,p);
+ draw(tiling,(Hx/2,Hy)--(Hx/2,2Hy),p);
+ draw(tiling,(0,2Hy)--(Hx,2Hy),p);
+ clip(tiling,box((0,0),(Hx,2Hy)));
+ return tiling;
+}
+
+real hatchepsilon=1e-4;
+picture hatch(real H=5mm, pair dir=NE, pen p=currentpen)
+{
+ picture tiling;
+ real theta=angle(dir);
+ real s=sin(theta);
+ real c=cos(theta);
+ if(abs(s) <= hatchepsilon) {
+ path g=(0,0)--(H,0);
+ draw(tiling,g,p);
+ draw(tiling,shift(0,H)*g,p);
+ clip(tiling,scale(H)*unitsquare);
+ } else if(abs(c) <= hatchepsilon) {
+ path g=(0,0)--(0,H);
+ draw(tiling,g,p);
+ draw(tiling,shift(H,0)*g,p);
+ clip(tiling,scale(H)*unitsquare);
+ } else {
+ real h=H/s;
+ real y=H/c;
+ path g=(0,0)--(h,y);
+ draw(tiling,g,p);
+ draw(tiling,shift(-h/2,y/2)*g,p);
+ draw(tiling,shift(h/2,-y/2)*g,p);
+ clip(tiling,box((0,0),(h,y)));
+ }
+ return tiling;
+}
+
+picture crosshatch(real H=5mm, pen p=currentpen)
+{
+ picture tiling;
+ add(tiling,hatch(H,p));
+ add(tiling,shift(H*sqrt(2))*rotate(90)*hatch(H,p));
+ return tiling;
+}
+
diff --git a/Master/texmf-dist/asymptote/plain.asy b/Master/texmf-dist/asymptote/plain.asy
new file mode 100644
index 00000000000..e06bc66543c
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain.asy
@@ -0,0 +1,284 @@
+/*****
+ * plain.asy
+ * Andy Hammerlindl and John Bowman 2004/08/19
+ *
+ * A package for general purpose drawing, with automatic sizing of pictures.
+ *
+ *****/
+
+access settings;
+
+if(settings.command != "") {
+ string s=settings.command;
+ settings.command="";
+ settings.multipleView=settings.batchView=settings.interactiveView;
+ _eval(s+";",false,true);
+ exit();
+}
+
+include plain_constants;
+
+access version;
+if(version.VERSION != VERSION) {
+ warning("version","using possibly incompatible version "+
+ version.VERSION+" of plain.asy"+'\n');
+ nowarn("version");
+}
+
+include plain_strings;
+include plain_pens;
+include plain_paths;
+include plain_filldraw;
+include plain_margins;
+include plain_picture;
+include plain_Label;
+include plain_shipout;
+include plain_arcs;
+include plain_boxes;
+include plain_markers;
+include plain_arrows;
+include plain_debugger;
+
+pair viewportmargin=(0,0); // Horizontal and vertical 3D viewport margins.
+
+typedef void exitfcn();
+
+bool needshipout() {
+ return !shipped && !currentpicture.empty();
+}
+
+void updatefunction()
+{
+ if(!currentpicture.uptodate) shipout();
+}
+
+void exitfunction()
+{
+ if(needshipout()) shipout();
+}
+
+atupdate(updatefunction);
+atexit(exitfunction);
+
+// A restore thunk is a function, that when called, restores the graphics state
+// to what it was when the restore thunk was created.
+typedef void restoreThunk();
+typedef restoreThunk saveFunction();
+saveFunction[] saveFunctions={};
+
+// When save is called, this will be redefined to do the corresponding restore.
+void restore()
+{
+ warning("nomatchingsave","restore called with no matching save");
+}
+
+void addSaveFunction(saveFunction s)
+{
+ saveFunctions.push(s);
+}
+
+restoreThunk buildRestoreThunk()
+{
+ // Call the save functions in reverse order, storing their restore thunks.
+ restoreThunk[] thunks={};
+ for (int i=saveFunctions.length-1; i >= 0; --i)
+ thunks.push(saveFunctions[i]());
+
+ return new void() {
+ // Call the restore thunks in an order matching the saves.
+ for (int i=thunks.length-1; i >= 0; --i)
+ thunks[i]();
+ };
+}
+
+// Add the default save function.
+addSaveFunction(new restoreThunk () {
+ pen defaultpen=defaultpen();
+ pen p=currentpen;
+ picture pic=currentpicture.copy();
+ restoreThunk r=restore;
+ return new void() {
+ defaultpen(defaultpen);
+ currentpen=p;
+ currentpicture=pic;
+ currentpicture.uptodate=false;
+ restore=r;
+ };
+ });
+
+// Save the current state, so that restore will put things back in that state.
+restoreThunk save()
+{
+ return restore=buildRestoreThunk();
+}
+
+void restoredefaults()
+{
+ warning("nomatchingsavedefaults",
+ "restoredefaults called with no matching savedefaults");
+}
+
+restoreThunk buildRestoreDefaults()
+{
+ pen defaultpen=defaultpen();
+ exitfcn atupdate=atupdate();
+ exitfcn atexit=atexit();
+ restoreThunk r=restoredefaults;
+ return new void() {
+ defaultpen(defaultpen);
+ atupdate(atupdate);
+ atexit(atexit);
+ restoredefaults=r;
+ };
+}
+
+// Save the current state, so that restore will put things back in that state.
+restoreThunk savedefaults()
+{
+ return restoredefaults=buildRestoreDefaults();
+}
+
+void initdefaults()
+{
+ savedefaults();
+ resetdefaultpen();
+ atupdate(null);
+ atexit(null);
+}
+
+// Return the sequence n,...m
+int[] sequence(int n, int m)
+{
+ return sequence(new int(int x){return x;},m-n+1)+n;
+}
+
+int[] reverse(int n) {return sequence(new int(int x){return n-1-x;},n);}
+bool[] reverse(bool[] a) {return a[reverse(a.length)];}
+int[] reverse(int[] a) {return a[reverse(a.length)];}
+real[] reverse(real[] a) {return a[reverse(a.length)];}
+pair[] reverse(pair[] a) {return a[reverse(a.length)];}
+triple[] reverse(triple[] a) {return a[reverse(a.length)];}
+string[] reverse(string[] a) {return a[reverse(a.length)];}
+
+// Return a uniform partition dividing [a,b] into n subintervals.
+real[] uniform(real a, real b, int n)
+{
+ if(n <= 0) return new real[];
+ return a+(b-a)/n*sequence(n+1);
+}
+
+void eval(string s, bool embedded=false)
+{
+ if(!embedded) initdefaults();
+ _eval(s+";",embedded);
+ if(!embedded) restoredefaults();
+}
+
+void eval(code s, bool embedded=false)
+{
+ if(!embedded) initdefaults();
+ _eval(s,embedded);
+ if(!embedded) restoredefaults();
+}
+
+// Evaluate user command line option.
+void usersetting()
+{
+ eval(settings.user,true);
+}
+
+string stripsuffix(string f, string suffix=".asy")
+{
+ int n=rfind(f,suffix);
+ if(n != -1) f=erase(f,n,-1);
+ return f;
+}
+
+// Conditionally process each file name in array s in a new environment.
+void asy(string format, bool overwrite=false ... string[] s)
+{
+ for(string f : s) {
+ f=stripsuffix(f);
+ string suffix="."+format;
+ string fsuffix=f+suffix;
+ if(overwrite || error(input(fsuffix,check=false))) {
+ string outformat=settings.outformat;
+ bool interactiveView=settings.interactiveView;
+ bool batchView=settings.batchView;
+ settings.outformat=format;
+ settings.interactiveView=false;
+ settings.batchView=false;
+ string outname=outname();
+ delete(outname+"_"+".aux");
+ eval("import \""+f+"\" as dummy");
+ rename(stripsuffix(outname)+suffix,fsuffix);
+ settings.outformat=outformat;
+ settings.interactiveView=interactiveView;
+ settings.batchView=batchView;
+ }
+ }
+}
+
+void beep()
+{
+ write('\7',flush);
+}
+
+struct processtime {
+ real user;
+ real system;
+}
+
+struct cputime {
+ processtime parent;
+ processtime child;
+ processtime change;
+}
+
+cputime cputime()
+{
+ static processtime last;
+ real [] a=_cputime();
+ cputime cputime;
+ cputime.parent.user=a[0];
+ cputime.parent.system=a[1];
+ cputime.child.user=a[2];
+ cputime.child.system=a[3];
+ real user=a[0]+a[2];
+ real system=a[1]+a[3];
+ cputime.change.user=user-last.user;
+ cputime.change.system=system-last.system;
+ last.user=user;
+ last.system=system;
+ return cputime;
+}
+
+string cputimeformat="%#.2f";
+
+void write(file file, string s="", cputime c, string format=cputimeformat,
+ suffix suffix=none)
+{
+ write(file,s,
+ format(format,c.change.user)+"u "+
+ format(format,c.change.system)+"s "+
+ format(format,c.parent.user+c.child.user)+"U "+
+ format(format,c.parent.system+c.child.system)+"S ",suffix);
+}
+
+void write(string s="", cputime c, string format=cputimeformat,
+ suffix suffix=endl)
+{
+ write(stdout,s,c,format,suffix);
+}
+
+if(settings.autoimport != "") {
+ string s=settings.autoimport;
+ settings.autoimport="";
+ eval("import \""+s+"\" as dummy",true);
+ shipped=false;
+ atupdate(updatefunction);
+ atexit(exitfunction);
+ settings.autoimport=s;
+}
+
+cputime();
diff --git a/Master/texmf-dist/asymptote/plain_Label.asy b/Master/texmf-dist/asymptote/plain_Label.asy
new file mode 100644
index 00000000000..a559ae88298
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_Label.asy
@@ -0,0 +1,669 @@
+real angle(transform t)
+{
+ pair z=(2t.xx*t.yy,t.yx*t.yy-t.xx*t.xy);
+ if(t.xx < 0) z=-z;
+ return degrees(z,warn=false);
+}
+
+transform rotation(transform t)
+{
+ return rotate(angle(t));
+}
+
+transform scaleless(transform t)
+{
+ real a=t.xx, b=t.xy, c=t.yx, d=t.yy;
+ real arg=(a-d)^2+4b*c;
+ pair delta=arg >= 0 ? sqrt(arg) : I*sqrt(-arg);
+ real trace=a+d;
+ pair l1=0.5(trace+delta);
+ pair l2=0.5(trace-delta);
+
+ if(abs(delta) < sqrtEpsilon*max(abs(l1),abs(l2))) {
+ real s=abs(0.5trace);
+ return (s != 0) ? scale(1/s)*t : t;
+ }
+
+ if(abs(l1-d) < abs(l2-d)) {pair temp=l1; l1=l2; l2=temp;}
+
+ pair dot(pair[] u, pair[] v) {return conj(u[0])*v[0]+conj(u[1])*v[1];}
+
+ pair[] unit(pair[] u) {
+ real norm2=abs(u[0])^2+abs(u[1])^2;
+ return norm2 != 0 ? u/sqrt(norm2) : u;
+ }
+
+ pair[] u={l1-d,b};
+ pair[] v={c,l2-a};
+ u=unit(u);
+ pair d=dot(u,u);
+ if(d != 0) v -= dot(u,v)/d*u;
+ v=unit(v);
+
+ pair[][] U={{u[0],v[0]},{u[1],v[1]}};
+ pair[][] A={{a,b},{c,d}};
+
+ pair[][] operator *(pair[][] a, pair[][] b) {
+ pair[][] c=new pair[2][2];
+ for(int i=0; i < 2; ++i) {
+ for(int j=0; j < 2; ++j) {
+ c[i][j]=a[i][0]*b[0][j]+a[i][1]*b[1][j];
+ }
+ }
+ return c;
+ }
+
+ pair[][] conj(pair[][] a) {
+ pair[][] c=new pair[2][2];
+ for(int i=0; i < 2; ++i) {
+ for(int j=0; j < 2; ++j) {
+ c[i][j]=conj(a[j][i]);
+ }
+ }
+ return c;
+ }
+
+ A=conj(U)*A*U;
+
+ real D=abs(A[0][0]);
+ if(D != 0) {
+ A[0][0] /= D;
+ A[0][1] /= D;
+ }
+
+ D=abs(A[1][1]);
+ if(D != 0) {
+ A[1][0] /= D;
+ A[1][1] /= D;
+ }
+
+ A=U*A*conj(U);
+
+ return (0,0,A[0][0].x,A[0][1].x,A[1][0].x,A[1][1].x);
+}
+
+struct align {
+ pair dir;
+ triple dir3;
+ bool relative=false;
+ bool default=true;
+ bool is3D=false;
+ void init(pair dir=0, bool relative=false, bool default=false) {
+ this.dir=dir;
+ this.relative=relative;
+ this.default=default;
+ is3D=false;
+ }
+ void init(triple dir=(0,0,0), bool relative=false, bool default=false) {
+ this.dir3=dir;
+ this.relative=relative;
+ this.default=default;
+ is3D=true;
+ }
+ align copy() {
+ align align=new align;
+ align.init(dir,relative,default);
+ align.dir3=dir3;
+ align.is3D=is3D;
+ return align;
+ }
+ void align(align align) {
+ if(!align.default) {
+ bool is3D=align.is3D;
+ init(align.dir,align.relative);
+ dir3=align.dir3;
+ this.is3D=is3D;
+ }
+ }
+ void align(align align, align default) {
+ align(align);
+ if(this.default) {
+ init(default.dir,default.relative,default.default);
+ dir3=default.dir3;
+ is3D=default.is3D;
+ }
+ }
+ void write(file file=stdout, suffix suffix=endl) {
+ if(!default) {
+ if(relative) {
+ write(file,"Relative(");
+ if(is3D)
+ write(file,dir3);
+ else
+ write(file,dir);
+ write(file,")",suffix);
+ } else {
+ if(is3D)
+ write(file,dir3,suffix);
+ else
+ write(file,dir,suffix);
+ }
+ }
+ }
+ bool Center() {
+ return relative && (is3D ? dir3 == (0,0,0) : dir == 0);
+ }
+}
+
+struct side {
+ pair align;
+}
+
+side Relative(explicit pair align)
+{
+ side s;
+ s.align=align;
+ return s;
+}
+
+restricted side NoSide;
+restricted side LeftSide=Relative(W);
+restricted side Center=Relative((0,0));
+restricted side RightSide=Relative(E);
+
+side operator * (real x, side s)
+{
+ side S;
+ S.align=x*s.align;
+ return S;
+}
+
+align operator cast(pair dir) {align A; A.init(dir,false); return A;}
+align operator cast(triple dir) {align A; A.init(dir,false); return A;}
+align operator cast(side side) {align A; A.init(side.align,true); return A;}
+restricted align NoAlign;
+
+void write(file file=stdout, align align, suffix suffix=endl)
+{
+ align.write(file,suffix);
+}
+
+struct position {
+ pair position;
+ bool relative;
+}
+
+position Relative(real position)
+{
+ position p;
+ p.position=position;
+ p.relative=true;
+ return p;
+}
+
+restricted position BeginPoint=Relative(0);
+restricted position MidPoint=Relative(0.5);
+restricted position EndPoint=Relative(1);
+
+position operator cast(pair x) {position P; P.position=x; return P;}
+position operator cast(real x) {return (pair) x;}
+position operator cast(int x) {return (pair) x;}
+
+pair operator cast(position P) {return P.position;}
+
+typedef transform embed(transform);
+transform Shift(transform t) {return identity();}
+transform Rotate(transform t) {return rotation(t);}
+transform Slant(transform t) {return scaleless(t);}
+transform Scale(transform t) {return t;}
+
+embed Rotate(pair z) {
+ return new transform(transform t) {return rotate(degrees(shiftless(t)*z,
+ warn=false));};
+}
+
+struct Label {
+ string s,size;
+ position position;
+ bool defaultposition=true;
+ align align;
+ pen p=nullpen;
+ transform T;
+ transform3 T3=identity(4);
+ bool defaulttransform=true;
+ bool defaulttransform3=true;
+ embed embed=Rotate; // Shift, Rotate, Slant, or Scale with embedded picture
+ filltype filltype=NoFill;
+
+ void init(string s="", string size="", position position=0,
+ bool defaultposition=true, align align=NoAlign, pen p=nullpen,
+ transform T=identity(), transform3 T3=identity4,
+ bool defaulttransform=true, bool defaulttransform3=true,
+ embed embed=Rotate, filltype filltype=NoFill) {
+ this.s=s;
+ this.size=size;
+ this.position=position;
+ this.defaultposition=defaultposition;
+ this.align=align.copy();
+ this.p=p;
+ this.T=T;
+ this.T3=copy(T3);
+ this.defaulttransform=defaulttransform;
+ this.defaulttransform3=defaulttransform3;
+ this.embed=embed;
+ this.filltype=filltype;
+ }
+
+ void initalign(string s="", string size="", align align, pen p=nullpen,
+ embed embed=Rotate, filltype filltype=NoFill) {
+ init(s,size,align,p,embed,filltype);
+ }
+
+ void transform(transform T) {
+ this.T=T;
+ defaulttransform=false;
+ }
+
+ void transform3(transform3 T) {
+ this.T3=copy(T);
+ defaulttransform3=false;
+ }
+
+ Label copy(transform3 T3=this.T3) {
+ Label L=new Label;
+ L.init(s,size,position,defaultposition,align,p,T,T3,defaulttransform,
+ defaulttransform3,embed,filltype);
+ return L;
+ }
+
+ void position(position pos) {
+ this.position=pos;
+ defaultposition=false;
+ }
+
+ void align(align a) {
+ align.align(a);
+ }
+ void align(align a, align default) {
+ align.align(a,default);
+ }
+
+ void p(pen p0) {
+ if(this.p == nullpen) this.p=p0;
+ }
+
+ void filltype(filltype filltype0) {
+ if(this.filltype == NoFill) this.filltype=filltype0;
+ }
+
+ 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);
+ }
+
+ void out(frame f, transform t=identity(), pair position=position.position,
+ pair align=align.dir) {
+ if(filltype == NoFill)
+ label(f,t,position,align);
+ else {
+ frame d;
+ label(d,t,position,align);
+ add(f,d,filltype);
+ }
+ }
+
+ void label(picture pic=currentpicture, pair position, pair align) {
+ if(s == "") return;
+ pic.add(new void (frame f, transform t) {
+ out(f,t,position,align);
+ },true);
+ frame f;
+ // Create a picture with label at the origin to extract its bbox truesize.
+ label(f,(0,0),align);
+ pic.addBox(position,position,min(f),max(f));
+ }
+
+ void out(picture pic=currentpicture) {
+ label(pic,position.position,align.dir);
+ }
+
+ void out(picture pic=currentpicture, path g) {
+ bool relative=position.relative;
+ real position=position.position.x;
+ pair Align=align.dir;
+ bool alignrelative=align.relative;
+ if(defaultposition) {relative=true; position=0.5;}
+ if(relative) position=reltime(g,position);
+ if(align.default) {
+ alignrelative=true;
+ Align=position <= sqrtEpsilon ? S :
+ position >= length(g)-sqrtEpsilon ? N : E;
+ }
+
+ pic.add(new void (frame f, transform t) {
+ out(f,t,point(g,position),
+ alignrelative ? -Align*dir(t*g,position)*I : Align);
+ },!alignrelative);
+
+ frame f;
+ pair align=alignrelative ? -Align*dir(g,position)*I : Align;
+ label(f,(0,0),align);
+ pair position=point(g,position);
+ pic.addBox(position,position,min(f),max(f));
+ }
+
+ void write(file file=stdout, suffix suffix=endl) {
+ write(file,"\""+s+"\"");
+ if(!defaultposition) write(file,", position=",position.position);
+ if(!align.default) write(file,", align=");
+ write(file,align);
+ if(p != nullpen) write(file,", pen=",p);
+ if(!defaulttransform)
+ write(file,", transform=",T);
+ if(!defaulttransform3)
+ write(file,T3);
+ write(file,"",suffix);
+ }
+
+ real relative() {
+ return defaultposition ? 0.5 : position.position.x;
+ };
+
+ real relative(path g) {
+ return position.relative ? reltime(g,relative()) : relative();
+ };
+}
+
+Label Label;
+
+void add(frame f, transform t=identity(), Label L)
+{
+ L.out(f,t);
+}
+
+void add(picture pic=currentpicture, Label L)
+{
+ L.out(pic);
+}
+
+Label operator * (transform t, Label L)
+{
+ Label tL=L.copy();
+ tL.align.dir=L.align.dir;
+ tL.transform(t*L.T);
+ return tL;
+}
+
+Label operator * (transform3 t, Label L)
+{
+ Label tL=L.copy(t*L.T3);
+ tL.align.dir=L.align.dir;
+ tL.defaulttransform3=false;
+ return tL;
+}
+
+Label Label(string s, string size="", explicit position position,
+ align align=NoAlign, pen p=nullpen, embed embed=Rotate,
+ filltype filltype=NoFill)
+{
+ Label L;
+ L.init(s,size,position,false,align,p,embed,filltype);
+ return L;
+}
+
+Label Label(string s, string size="", pair position, align align=NoAlign,
+ pen p=nullpen, embed embed=Rotate, filltype filltype=NoFill)
+{
+ return Label(s,size,(position) position,align,p,embed,filltype);
+}
+
+Label Label(explicit pair position, align align=NoAlign, pen p=nullpen,
+ embed embed=Rotate, filltype filltype=NoFill)
+{
+ return Label((string) position,position,align,p,embed,filltype);
+}
+
+Label Label(string s="", string size="", align align=NoAlign, pen p=nullpen,
+ embed embed=Rotate, filltype filltype=NoFill)
+{
+ Label L;
+ L.initalign(s,size,align,p,embed,filltype);
+ return L;
+}
+
+Label Label(Label L, align align=NoAlign, pen p=nullpen, embed embed=L.embed,
+ filltype filltype=NoFill)
+{
+ Label L=L.copy();
+ L.align(align);
+ L.p(p);
+ L.embed=embed;
+ L.filltype(filltype);
+ return L;
+}
+
+Label Label(Label L, explicit position position, align align=NoAlign,
+ pen p=nullpen, embed embed=L.embed, filltype filltype=NoFill)
+{
+ Label L=Label(L,align,p,embed,filltype);
+ L.position(position);
+ return L;
+}
+
+Label Label(Label L, pair position, align align=NoAlign,
+ pen p=nullpen, embed embed=L.embed, filltype filltype=NoFill)
+{
+ return Label(L,(position) position,align,p,embed,filltype);
+}
+
+void write(file file=stdout, Label L, suffix suffix=endl)
+{
+ L.write(file,suffix);
+}
+
+void label(frame f, Label L, pair position, align align=NoAlign,
+ pen p=currentpen, filltype filltype=NoFill)
+{
+ add(f,Label(L,position,align,p,filltype));
+}
+
+void label(frame f, Label L, align align=NoAlign,
+ pen p=currentpen, filltype filltype=NoFill)
+{
+ add(f,Label(L,L.position,align,p,filltype));
+}
+
+void label(picture pic=currentpicture, Label L, pair position,
+ align align=NoAlign, pen p=currentpen, filltype filltype=NoFill)
+{
+ Label L=Label(L,position,align,p,filltype);
+ add(pic,L);
+}
+
+void label(picture pic=currentpicture, Label L, align align=NoAlign,
+ pen p=currentpen, filltype filltype=NoFill)
+{
+ label(pic,L,L.position,align,p,filltype);
+}
+
+void label(picture pic=currentpicture, Label L, explicit path g,
+ align align=NoAlign, pen p=currentpen, filltype filltype=NoFill)
+{
+ Label L=Label(L,align,p,filltype);
+ L.out(pic,g);
+}
+
+void label(picture pic=currentpicture, Label L, explicit guide g,
+ align align=NoAlign, pen p=currentpen, filltype filltype=NoFill)
+{
+ label(pic,L,(path) g,align,p,filltype);
+}
+
+Label operator cast(string s) {return Label(s);}
+
+// A structure that a string, Label, or frame can be cast to.
+struct object {
+ frame f;
+ Label L=Label;
+ path g; // Bounding path
+
+ void operator init(frame f) {
+ this.f=f;
+ g=box(min(f),max(f));
+ }
+
+ void operator init(Label L) {
+ this.L=L.copy();
+ if(L != Label) L.out(f);
+ g=box(min(f),max(f));
+ }
+}
+
+object operator cast(frame f) {
+ return object(f);
+}
+
+object operator cast(Label L)
+{
+ return object(L);
+}
+
+object operator cast(string s)
+{
+ return object(s);
+}
+
+Label operator cast(object F)
+{
+ return F.L;
+}
+
+frame operator cast(object F)
+{
+ return F.f;
+}
+
+object operator * (transform t, explicit object F)
+{
+ object f;
+ f.f=t*F.f;
+ f.L=t*F.L;
+ f.g=t*F.g;
+ return f;
+}
+
+// Returns a copy of object F aligned in the direction align
+object align(object F, pair align)
+{
+ return shift(F.f,align)*F;
+}
+
+void add(picture dest=currentpicture, object F, pair position=0,
+ bool group=true, filltype filltype=NoFill, bool above=true)
+{
+ add(dest,F.f,position,group,filltype,above);
+}
+
+// Pack a list of objects into a frame.
+frame pack(pair align=2S ... object inset[])
+{
+ frame F;
+ int n=inset.length;
+ pair z;
+ for (int i=0; i < n; ++i) {
+ add(F,inset[i].f,z);
+ z += align+realmult(unit(align),size(inset[i].f));
+ }
+ return F;
+}
+
+path[] texpath(Label L, bool tex=settings.tex != "none", bool bbox=false)
+{
+ struct stringfont
+ {
+ string s;
+ real fontsize;
+ string font;
+
+ void operator init(Label L)
+ {
+ s=L.s;
+ fontsize=fontsize(L.p);
+ font=font(L.p);
+ }
+
+ pen pen() {return fontsize(fontsize)+fontcommand(font);}
+ }
+
+ bool lexorder(stringfont a, stringfont b) {
+ return a.s < b.s || (a.s == b.s && (a.fontsize < b.fontsize ||
+ (a.fontsize == b.fontsize &&
+ a.font < b.font)));
+ }
+
+ static stringfont[] stringcache;
+ static path[][] pathcache;
+
+ static stringfont[] stringlist;
+ static bool adjust[];
+
+ path[] G;
+
+ stringfont s=stringfont(L);
+ pen p=s.pen();
+
+ int i=search(stringcache,s,lexorder);
+ if(i == -1 || lexorder(stringcache[i],s)) {
+ int k=search(stringlist,s,lexorder);
+ if(k == -1 || lexorder(stringlist[k],s)) {
+ ++k;
+ stringlist.insert(k,s);
+ // PDF tex engines lose track of the baseline.
+ adjust.insert(k,tex && basealign(L.p) == 1 && pdf());
+ }
+ }
+
+ path[] transform(path[] g, Label L) {
+ if(g.length == 0) return g;
+ pair m=min(g);
+ pair M=max(g);
+ pair dir=rectify(inverse(L.T)*-L.align.dir);
+ if(tex && basealign(L.p) == 1)
+ dir -= (0,(1-dir.y)*m.y/(M.y-m.y));
+ pair a=m+realmult(dir,M-m);
+
+ return shift(L.position+L.align.dir*labelmargin(L.p))*L.T*shift(-a)*g;
+ }
+
+ if(tex && bbox) {
+ frame f;
+ label(f,L);
+ return transform(box(min(f),max(f)),L);
+ }
+
+ if(stringlist.length > 0) {
+ path[][] g;
+ int n=stringlist.length;
+ string[] s=new string[n];
+ pen[] p=new pen[n];
+ for(int i=0; i < n; ++i) {
+ stringfont S=stringlist[i];
+ s[i]=adjust[i] ? "."+S.s : S.s;
+ p[i]=adjust[i] ? S.pen()+basealign : S.pen();
+ }
+
+ g=tex ? _texpath(s,p) : textpath(s,p);
+
+ if(tex)
+ for(int i=0; i < n; ++i)
+ if(adjust[i]) {
+ real y=min(g[i][0]).y;
+ g[i].delete(0);
+ g[i]=shift(0,-y)*g[i];
+ }
+
+
+ for(int i=0; i < stringlist.length; ++i) {
+ stringfont s=stringlist[i];
+ int j=search(stringcache,s,lexorder)+1;
+ stringcache.insert(j,s);
+ pathcache.insert(j,g[i]);
+ }
+ stringlist.delete();
+ adjust.delete();
+ }
+
+ return transform(pathcache[search(stringcache,stringfont(L),lexorder)],L);
+}
diff --git a/Master/texmf-dist/asymptote/plain_arcs.asy b/Master/texmf-dist/asymptote/plain_arcs.asy
new file mode 100644
index 00000000000..140bc3cff19
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_arcs.asy
@@ -0,0 +1,44 @@
+bool CCW=true;
+bool CW=false;
+
+path circle(pair c, real r)
+{
+ return shift(c)*scale(r)*unitcircle;
+}
+
+path ellipse(pair c, real a, real b)
+{
+ return shift(c)*scale(a,b)*unitcircle;
+}
+
+// return an arc centered at c from pair z1 to z2 (assuming |z2-c|=|z1-c|),
+// drawing in the given direction.
+path arc(pair c, explicit pair z1, explicit pair z2, bool direction=CCW)
+{
+ z1 -= c;
+ real r=abs(z1);
+ z1=unit(z1);
+ z2=unit(z2-c);
+
+ real t1=intersect(unitcircle,(0,0)--2*z1)[0];
+ real t2=intersect(unitcircle,(0,0)--2*z2)[0];
+ static int n=length(unitcircle);
+ if(direction) {
+ if (t1 >= t2) t1 -= n;
+ } else if(t2 >= t1) t2 -= n;
+ return shift(c)*scale(r)*subpath(unitcircle,t1,t2);
+}
+
+// return an arc centered at c with radius r from angle1 to angle2 in degrees,
+// drawing in the given direction.
+path arc(pair c, real r, real angle1, real angle2, bool direction)
+{
+ return arc(c,c+r*dir(angle1),c+r*dir(angle2),direction);
+}
+
+// return an arc centered at c with radius r > 0 from angle1 to angle2 in
+// degrees, drawing counterclockwise if angle2 >= angle1 (otherwise clockwise).
+path arc(pair c, real r, real angle1, real angle2)
+{
+ return arc(c,r,angle1,angle2,angle2 >= angle1 ? CCW : CW);
+}
diff --git a/Master/texmf-dist/asymptote/plain_arrows.asy b/Master/texmf-dist/asymptote/plain_arrows.asy
new file mode 100644
index 00000000000..59b65aa4126
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_arrows.asy
@@ -0,0 +1,639 @@
+real arrowlength=0.75cm;
+real arrowfactor=15;
+real arrowangle=15;
+real arcarrowfactor=0.5*arrowfactor;
+real arcarrowangle=2*arrowangle;
+real arrowsizelimit=0.5;
+real arrow2sizelimit=1/3;
+real arrowdir=5;
+real arrowbarb=3;
+real arrowhookfactor=1.5;
+real arrowtexfactor=1;
+
+real barfactor=arrowfactor;
+
+real arrowsize(pen p=currentpen)
+{
+ return arrowfactor*linewidth(p);
+}
+
+real arcarrowsize(pen p=currentpen)
+{
+ return arcarrowfactor*linewidth(p);
+}
+
+real barsize(pen p=currentpen)
+{
+ return barfactor*linewidth(p);
+}
+
+struct arrowhead
+{
+ path head(path g, position position=EndPoint, pen p=currentpen,
+ real size=0, real angle=arrowangle);
+ real size(pen p)=arrowsize;
+ real arcsize(pen p)=arcarrowsize;
+ filltype defaultfilltype(pen) {return FillDraw;}
+}
+
+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};
+}
+
+path arrowbase(path r, pair y, real t, real size)
+{
+ pair perp=2*size*I*dir(r,t);
+ return size == 0 ? y : y+perp--y-perp;
+}
+
+arrowhead DefaultHead;
+DefaultHead.head=new path(path g, position position=EndPoint, pen p=currentpen,
+ real size=0, real angle=arrowangle) {
+ if(size == 0) size=DefaultHead.size(p);
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+ path r=subpath(g,position,0);
+ pair x=point(r,0);
+ real t=arctime(r,size);
+ pair y=point(r,t);
+ path base=arrowbase(r,y,t,size);
+ path left=rotate(-angle,x)*r;
+ path right=rotate(angle,x)*r;
+ 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 right=rotate(angle*factor,x)*r;
+ real[] T=arrowbasepoints(base,left,right);
+ return subpath(left,0,T[0])--subpath(right,T[1],0)&cycle;
+};
+
+arrowhead SimpleHead;
+SimpleHead.head=new path(path g, position position=EndPoint, pen p=currentpen,
+ real size=0, real angle=arrowangle) {
+ if(size == 0) size=SimpleHead.size(p);
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+ path r=subpath(g,position,0);
+ pair x=point(r,0);
+ real t=arctime(r,size);
+ pair y=point(r,t);
+ path base=arrowbase(r,y,t,size);
+ path left=rotate(-angle,x)*r;
+ path right=rotate(angle,x)*r;
+ 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 right=rotate(angle*factor,x)*r;
+ real[] T=arrowbasepoints(base,left,right);
+ return subpath(left,T[0],0)--subpath(right,0,T[1]);
+};
+
+arrowhead HookHead(real dir=arrowdir, real barb=arrowbarb)
+{
+ arrowhead a;
+ a.head=new path(path g, position position=EndPoint, pen p=currentpen,
+ real size=0, real angle=arrowangle)
+ {
+ if(size == 0) size=a.size(p);
+ angle=min(angle*arrowhookfactor,45);
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+ path r=subpath(g,position,0);
+ pair x=point(r,0);
+ real t=arctime(r,size);
+ pair y=point(r,t);
+ path base=arrowbase(r,y,t,size);
+ path left=rotate(-angle,x)*r;
+ path right=rotate(angle,x)*r;
+ 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 right=rotate(angle*factor,x)*r;
+ real[] T=arrowbasepoints(base,left,right);
+ left=subpath(left,0,T[0]);
+ right=subpath(right,T[1],0);
+ pair pl0=point(left,0), pl1=relpoint(left,1);
+ pair pr0=relpoint(right,0), pr1=relpoint(right,1);
+ pair M=(pl1+pr0)/2;
+ pair v=barb*unit(M-pl0);
+ pl1=pl1+v; pr0=pr0+v;
+ left=pl0{dir(-dir+degrees(M-pl0,false))}..pl1--M;
+ right=M--pr0..pr1{dir(dir+degrees(pr1-M,false))};
+ return left--right&cycle;
+ };
+ return a;
+}
+arrowhead HookHead=HookHead();
+
+arrowhead TeXHead;
+TeXHead.size=new real(pen p)
+{
+ static real hcoef=2.1; // 84/40=abs(base-hint)/base_height
+ return hcoef*arrowtexfactor*linewidth(p);
+};
+TeXHead.arcsize=TeXHead.size;
+
+TeXHead.head=new path(path g, position position=EndPoint, pen p=currentpen,
+ real size=0, real angle=arrowangle) {
+ static real wcoef=1/84; // 1/abs(base-hint)
+ static path texhead=scale(wcoef)*
+ ((0,20) .. controls (-75,75) and (-108,158) ..
+ (-108,166) .. controls (-108,175) and (-100,178) ..
+ (-93,178) .. controls (-82,178) and (-80,173) ..
+ (-77,168) .. controls (-62,134) and (-30,61) ..
+ (70,14) .. controls (82,8) and (84,7) ..
+ (84,0) .. controls (84,-7) and (82,-8) ..
+ (70,-14) .. controls (-30,-61) and (-62,-134) ..
+ (-77,-168) .. controls (-80,-173) and (-82,-178) ..
+ (-93,-178) .. controls (-100,-178) and (-108,-175)..
+ (-108,-166).. controls (-108,-158) and (-75,-75) ..
+ (0,-20)--cycle);
+ if(size == 0) size=TeXHead.size(p);
+ path gp=scale(size)*texhead;
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+ path r=subpath(g,position,0);
+ pair y=point(r,arctime(r,size));
+ return shift(y)*rotate(degrees(-dir(r,arctime(r,0.5*size))))*gp;
+};
+TeXHead.defaultfilltype=new filltype(pen p) {return Fill(p);};
+
+private real position(position position, real size, path g, bool center)
+{
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) {
+ position *= arclength(g);
+ if(center) position += 0.5*size;
+ position=arctime(g,position);
+ } else if(center)
+ position=arctime(g,arclength(subpath(g,0,position))+0.5*size);
+ return position;
+}
+
+void drawarrow(frame f, arrowhead arrowhead=DefaultHead,
+ path g, pen p=currentpen, real size=0,
+ real angle=arrowangle,
+ filltype filltype=null,
+ position position=EndPoint, bool forwards=true,
+ margin margin=NoMargin, bool center=false)
+{
+ if(size == 0) size=arrowhead.size(p);
+ if(filltype == null) filltype=arrowhead.defaultfilltype(p);
+ size=min(arrowsizelimit*arclength(g),size);
+ real position=position(position,size,g,center);
+
+ g=margin(g,p).g;
+ int L=length(g);
+ if(!forwards) {
+ g=reverse(g);
+ position=L-position;
+ }
+ path r=subpath(g,position,0);
+ size=min(arrowsizelimit*arclength(r),size);
+ path head=arrowhead.head(g,position,p,size,angle);
+ bool endpoint=position > L-sqrtEpsilon;
+ if(cyclic(head) && (filltype == NoFill || endpoint)) {
+ if(position > 0)
+ draw(f,subpath(r,arctime(r,size),length(r)),p);
+ if(!endpoint)
+ draw(f,subpath(g,position,L),p);
+ } else draw(f,g,p);
+ filltype.fill(f,head,p+solid);
+}
+
+void drawarrow2(frame f, arrowhead arrowhead=DefaultHead,
+ path g, pen p=currentpen, real size=0,
+ real angle=arrowangle, filltype filltype=null,
+ margin margin=NoMargin)
+{
+ if(size == 0) size=arrowhead.size(p);
+ if(filltype == null) filltype=arrowhead.defaultfilltype(p);
+ g=margin(g,p).g;
+ size=min(arrow2sizelimit*arclength(g),size);
+
+ path r=reverse(g);
+ int L=length(g);
+ path head=arrowhead.head(g,L,p,size,angle);
+ path tail=arrowhead.head(r,L,p,size,angle);
+ if(cyclic(head))
+ draw(f,subpath(r,arctime(r,size),L-arctime(g,size)),p);
+ else draw(f,g,p);
+ filltype.fill(f,head,p+solid);
+ filltype.fill(f,tail,p+solid);
+}
+
+// Add to picture an estimate of the bounding box contribution of arrowhead
+// using the local slope at endpoint and ignoring margin.
+void addArrow(picture pic, arrowhead arrowhead, path g, pen p, real size,
+ real angle, filltype filltype, real position)
+{
+ if(filltype == null) filltype=arrowhead.defaultfilltype(p);
+ pair z=point(g,position);
+ path g=z-(size+linewidth(p))*dir(g,position)--z;
+ frame f;
+ filltype.fill(f,arrowhead.head(g,position,p,size,angle),p);
+ pic.addBox(z,z,min(f)-z,max(f)-z);
+}
+
+picture arrow(arrowhead arrowhead=DefaultHead,
+ path g, pen p=currentpen, real size=0,
+ real angle=arrowangle, filltype filltype=null,
+ position position=EndPoint, bool forwards=true,
+ margin margin=NoMargin, bool center=false)
+{
+ if(size == 0) size=arrowhead.size(p);
+ picture pic;
+ pic.add(new void(frame f, transform t) {
+ drawarrow(f,arrowhead,t*g,p,size,angle,filltype,position,forwards,margin,
+ center);
+ });
+
+ pic.addPath(g,p);
+
+ real position=position(position,size,g,center);
+ path G;
+ if(!forwards) {
+ G=reverse(g);
+ position=length(g)-position;
+ } else G=g;
+ addArrow(pic,arrowhead,G,p,size,angle,filltype,position);
+
+ return pic;
+}
+
+picture arrow2(arrowhead arrowhead=DefaultHead,
+ path g, pen p=currentpen, real size=0,
+ real angle=arrowangle, filltype filltype=null,
+ margin margin=NoMargin)
+{
+ if(size == 0) size=arrowhead.size(p);
+ picture pic;
+ pic.add(new void(frame f, transform t) {
+ drawarrow2(f,arrowhead,t*g,p,size,angle,filltype,margin);
+ });
+
+ pic.addPath(g,p);
+
+ int L=length(g);
+ addArrow(pic,arrowhead,g,p,size,angle,filltype,L);
+ addArrow(pic,arrowhead,reverse(g),p,size,angle,filltype,L);
+
+ return pic;
+}
+
+void bar(picture pic, pair a, pair d, pen p=currentpen)
+{
+ picture opic;
+ Draw(opic,-0.5d--0.5d,p+solid);
+ add(pic,opic,a);
+}
+
+picture bar(pair a, pair d, pen p=currentpen)
+{
+ picture pic;
+ bar(pic,a,d,p);
+ return pic;
+}
+
+typedef bool arrowbar(picture, path, pen, margin);
+
+bool Blank(picture, path, pen, margin)
+{
+ return false;
+}
+
+bool None(picture, path, pen, margin)
+{
+ return true;
+}
+
+arrowbar BeginArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=BeginPoint)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,forwards=false,
+ margin));
+ return false;
+ };
+}
+
+arrowbar Arrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=EndPoint)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,margin));
+ return false;
+ };
+}
+
+arrowbar EndArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=EndPoint)=Arrow;
+
+arrowbar MidArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arrowangle, filltype filltype=null)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ add(pic,arrow(arrowhead,g,p,size,angle,filltype,MidPoint,margin,
+ center=true));
+ return false;
+ };
+}
+
+arrowbar Arrows(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ add(pic,arrow2(arrowhead,g,p,size,angle,filltype,margin));
+ return false;
+ };
+}
+
+arrowbar BeginArcArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null, position position=BeginPoint)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ real size=size == 0 ? arrowhead.arcsize(p) : size;
+ add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,
+ forwards=false,margin));
+ return false;
+ };
+}
+
+arrowbar ArcArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null, position position=EndPoint)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ real size=size == 0 ? arrowhead.arcsize(p) : size;
+ add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,margin));
+ return false;
+ };
+}
+
+arrowbar EndArcArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null,
+ position position=EndPoint)=ArcArrow;
+
+arrowbar MidArcArrow(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ real size=size == 0 ? arrowhead.arcsize(p) : size;
+ add(pic,arrow(arrowhead,g,p,size,angle,filltype,MidPoint,margin,
+ center=true));
+ return false;
+ };
+}
+
+arrowbar ArcArrows(arrowhead arrowhead=DefaultHead,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ real size=size == 0 ? arrowhead.arcsize(p) : size;
+ add(pic,arrow2(arrowhead,g,p,size,angle,filltype,margin));
+ return false;
+ };
+}
+
+arrowbar BeginBar(real size=0)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ real size=size == 0 ? barsize(p) : size;
+ bar(pic,point(g,0),size*dir(g,0)*I,p);
+ return true;
+ };
+}
+
+arrowbar Bar(real size=0)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ int L=length(g);
+ real size=size == 0 ? barsize(p) : size;
+ bar(pic,point(g,L),size*dir(g,L)*I,p);
+ return true;
+ };
+}
+
+arrowbar EndBar(real size=0)=Bar;
+
+arrowbar Bars(real size=0)
+{
+ return new bool(picture pic, path g, pen p, margin margin) {
+ real size=size == 0 ? barsize(p) : size;
+ BeginBar(size)(pic,g,p,margin);
+ EndBar(size)(pic,g,p,margin);
+ return true;
+ };
+}
+
+arrowbar BeginArrow=BeginArrow(),
+MidArrow=MidArrow(),
+Arrow=Arrow(),
+EndArrow=Arrow(),
+Arrows=Arrows(),
+BeginArcArrow=BeginArcArrow(),
+MidArcArrow=MidArcArrow(),
+ArcArrow=ArcArrow(),
+EndArcArrow=ArcArrow(),
+ArcArrows=ArcArrows(),
+BeginBar=BeginBar(),
+Bar=Bar(),
+EndBar=Bar(),
+Bars=Bars();
+
+void draw(frame f, path g, pen p=currentpen, arrowbar arrow)
+{
+ picture pic;
+ if(arrow(pic,g,p,NoMargin))
+ draw(f,g,p);
+ add(f,pic.fit());
+}
+
+void draw(picture pic=currentpicture, Label L=null, path g,
+ align align=NoAlign, pen p=currentpen, arrowbar arrow=None,
+ arrowbar bar=None, margin margin=NoMargin, Label legend=null,
+ marker marker=nomarker)
+{
+ // These if statements are ordered in such a way that the most common case
+ // (with just a path and a pen) executes the least bytecode.
+ if (marker == nomarker)
+ {
+ if (arrow == None && bar == None)
+ {
+ if (margin == NoMargin && size(nib(p)) == 0)
+ {
+ pic.addExactAbove(
+ new void(frame f, transform t, transform T, pair, pair) {
+ _draw(f,t*T*g,p);
+ });
+ pic.addPath(g,p);
+
+ // Jumping over else clauses takes time, so test if we can return
+ // here.
+ if (L == null && legend == null)
+ return;
+ }
+ else // With margin or polygonal pen.
+ {
+ _draw(pic, g, p, margin);
+ }
+ }
+ else /* arrow or bar */
+ {
+ // Note we are using & instead of && as both arrow and bar need to be
+ // called.
+ if (arrow(pic, g, p, margin) & bar(pic, g, p, margin))
+ _draw(pic, g, p, margin);
+ }
+
+ if(L != null && L.s != "") {
+ L=L.copy();
+ L.align(align);
+ L.p(p);
+ L.out(pic,g);
+ }
+
+ if(legend != null && legend.s != "") {
+ legend.p(p);
+ pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above));
+ }
+ }
+ else /* marker != nomarker */
+ {
+ if(marker != nomarker && !marker.above) marker.mark(pic,g);
+
+ // Note we are using & instead of && as both arrow and bar need to be
+ // called.
+ if ((arrow == None || arrow(pic, g, p, margin)) &
+ (bar == None || bar(pic, g, p, margin)))
+ {
+ _draw(pic, g, p, margin);
+ }
+
+ if(L != null && L.s != "") {
+ L=L.copy();
+ L.align(align);
+ L.p(p);
+ L.out(pic,g);
+ }
+
+ if(legend != null && legend.s != "") {
+ legend.p(p);
+ pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above));
+ }
+
+ if(marker != nomarker && marker.above) marker.mark(pic,g);
+ }
+}
+
+// Draw a fixed-size line about the user-coordinate 'origin'.
+void draw(pair origin, picture pic=currentpicture, Label L=null, path g,
+ align align=NoAlign, pen p=currentpen, arrowbar arrow=None,
+ arrowbar bar=None, margin margin=NoMargin, Label legend=null,
+ marker marker=nomarker)
+{
+ picture opic;
+ draw(opic,L,g,align,p,arrow,bar,margin,legend,marker);
+ add(pic,opic,origin);
+}
+
+void draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen,
+ Label legend=null, marker marker=nomarker)
+{
+ // This could be optimized to size and draw the entire array as a batch.
+ for(int i=0; i < g.length-1; ++i)
+ draw(pic,g[i],p,marker);
+ if(g.length > 0) draw(pic,g[g.length-1],p,legend,marker);
+}
+
+void draw(picture pic=currentpicture, guide[] g, pen p=currentpen,
+ Label legend=null, marker marker=nomarker)
+{
+ draw(pic,(path[]) g,p,legend,marker);
+}
+
+void draw(pair origin, picture pic=currentpicture, explicit path[] g,
+ pen p=currentpen, Label legend=null, marker marker=nomarker)
+{
+ picture opic;
+ draw(opic,g,p,legend,marker);
+ add(pic,opic,origin);
+}
+
+void draw(pair origin, picture pic=currentpicture, guide[] g, pen p=currentpen,
+ Label legend=null, marker marker=nomarker)
+{
+ draw(origin,pic,(path[]) g,p,legend,marker);
+}
+
+// Align an arrow pointing to b from the direction dir. The arrow is
+// 'length' PostScript units long.
+void arrow(picture pic=currentpicture, Label L=null, pair b, pair dir,
+ real length=arrowlength, align align=NoAlign,
+ pen p=currentpen, arrowbar arrow=Arrow, margin margin=EndMargin)
+{
+ if(L != null && L.s != "") {
+ L=L.copy();
+ if(L.defaultposition) L.position(0);
+ L.align(L.align,dir);
+ L.p(p);
+ }
+ marginT margin=margin(b--b,p); // Extract margin.begin and margin.end
+ pair a=(margin.begin+length+margin.end)*unit(dir);
+ draw(b,pic,L,a--(0,0),align,p,arrow,margin);
+}
+
+// Fit an array of pictures simultaneously using the sizing of picture all.
+frame[] fit2(picture[] pictures, picture all)
+{
+ frame[] out;
+ if(!all.empty2()) {
+ transform t=all.calculateTransform();
+ pair m=all.min(t);
+ pair M=all.max(t);
+ for(picture pic : pictures) {
+ frame f=pic.fit(t);
+ draw(f,m,nullpen);
+ draw(f,M,nullpen);
+ out.push(f);
+ }
+ }
+ return out;
+}
+
+// Fit an array of pictures simultaneously using the size of the first picture.
+// TODO: Remove unused arguments.
+frame[] fit(string prefix="", picture[] pictures, string format="",
+ bool view=true, string options="", string script="",
+ projection P=currentprojection)
+{
+ if(pictures.length == 0)
+ return new frame[];
+
+ picture all;
+ size(all,pictures[0]);
+ for(picture pic : pictures)
+ add(all,pic);
+
+ return fit2(pictures,all);
+}
diff --git a/Master/texmf-dist/asymptote/plain_bounds.asy b/Master/texmf-dist/asymptote/plain_bounds.asy
new file mode 100644
index 00000000000..871d82f01b5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_bounds.asy
@@ -0,0 +1,787 @@
+include plain_scaling;
+
+// After an transformation, produce new coordinate bounds. For paths that
+// have been added, this is only an approximation since it takes the bounds of
+// their transformed bounding box.
+private void addTransformedCoords(coords2 dest, transform t,
+ coords2 point, coords2 min, coords2 max)
+{
+ dest.push(t, point, point);
+
+ // Add in all 4 corner coords, to properly size rectangular pictures.
+ dest.push(t,min,min);
+ dest.push(t,min,max);
+ dest.push(t,max,min);
+ dest.push(t,max,max);
+}
+
+// Adds another sizing restriction to the coordinates, but only if it is
+// maximal, that is, if under some scaling, this coordinate could be the
+// largest.
+private void addIfMaximal(coord[] coords, real user, real truesize) {
+ // TODO: Test promoting coordinates for efficiency.
+
+ for (coord c : coords)
+ if (user <= c.user && truesize <= c.truesize)
+ // Not maximal.
+ return;
+
+ // The coordinate is not dominated by any existing extreme, so it is
+ // maximal and will be added, but first remove any coords it now dominates.
+ int i = 0;
+ while (i < coords.length) {
+ coord c = coords[i];
+ if (c.user <= user && c.truesize <= truesize)
+ coords.delete(i);
+ else
+ ++i;
+ }
+
+ // Add the coordinate to the extremes.
+ coords.push(coord.build(user, truesize));
+}
+
+private void addIfMaximal(coord[] dest, coord[] src)
+{
+ // This may be inefficient, as it rebuilds the coord struct when adding it.
+ for (coord c : src)
+ addIfMaximal(dest, c.user, c.truesize);
+}
+
+// Same as addIfMaximal, but testing for minimal coords.
+private void addIfMinimal(coord[] coords, real user, real truesize) {
+ for (coord c : coords)
+ if (user >= c.user && truesize >= c.truesize)
+ return;
+
+ int i = 0;
+ while (i < coords.length) {
+ coord c = coords[i];
+ if (c.user >= user && c.truesize >= truesize)
+ coords.delete(i);
+ else
+ ++i;
+ }
+
+ coords.push(coord.build(user, truesize));
+}
+
+private void addIfMinimal(coord[] dest, coord[] src)
+{
+ for (coord c : src)
+ addIfMinimal(dest, c.user, c.truesize);
+}
+
+// This stores a list of sizing bounds for picture data. If the object is
+// frozen, then it cannot be modified further, and therefore can be safely
+// passed by reference and stored in the sizing data for multiple pictures.
+private struct freezableBounds {
+ restricted bool frozen = false;
+ void freeze() {
+ frozen = true;
+ }
+
+ // Optional links to further (frozen) sizing data.
+ private freezableBounds[] links;
+
+ // Links to (frozen) sizing data that is transformed when added here.
+ private static struct transformedBounds {
+ transform t;
+ freezableBounds link;
+ };
+ private transformedBounds[] tlinks;
+
+ // The sizing data. It cannot be modified once this object is frozen.
+ private coords2 point, min, max;
+
+ // A bound represented by a path. Using the path instead of the bounding
+ // box means it will be accurate after a transformation by coordinates.
+ private path[] pathBounds;
+
+ // A bound represented by a path and a pen.
+ // As often many paths use the same pen, we store an array of paths.
+ private static struct pathpen {
+ path[] g; pen p;
+
+ void operator init(path g, pen p) {
+ this.g.push(g);
+ this.p = p;
+ }
+ }
+ private static pathpen operator *(transform t, pathpen pp) {
+ // Should the pen be transformed?
+ pathpen newpp;
+ for (path g : pp.g)
+ newpp.g.push(t*g);
+ newpp.p = pp.p;
+ return newpp;
+ }
+
+ // WARNING: Due to crazy optimizations, if this array is changed between an
+ // empty and non-empty state, the assignment of a method to
+ // addPath(path,pen) must also change.
+ private pathpen[] pathpenBounds;
+
+ // Once frozen, the sizing is immutable, and therefore we can compute and
+ // store the extremal coordinates.
+ public static struct extremes {
+ coord[] left, bottom, right, top;
+
+ void operator init(coord[] left, coord[] bottom,
+ coord[] right, coord[] top) {
+ this.left = left;
+ this.bottom = bottom;
+ this.right = right;
+ this.top = top;
+ }
+
+ }
+ private static void addMaxToExtremes(extremes e, pair user, pair truesize) {
+ addIfMaximal(e.right, user.x, truesize.x);
+ addIfMaximal(e.top, user.y, truesize.y);
+ }
+ private static void addMinToExtremes(extremes e, pair user, pair truesize) {
+ addIfMinimal(e.left, user.x, truesize.x);
+ addIfMinimal(e.bottom, user.y, truesize.y);
+ }
+ private static void addMaxToExtremes(extremes e, coords2 coords) {
+ addIfMaximal(e.right, coords.x);
+ addIfMaximal(e.top, coords.y);
+ }
+ private static void addMinToExtremes(extremes e, coords2 coords) {
+ addIfMinimal(e.left, coords.x);
+ addIfMinimal(e.bottom, coords.y);
+ }
+
+ private extremes cachedExtremes = null;
+
+ // Once frozen, getMutable returns a new object based on this one, which can
+ // be modified.
+ freezableBounds getMutable() {
+ assert(frozen);
+ var f = new freezableBounds;
+ f.links.push(this);
+ return f;
+ }
+
+ freezableBounds transformed(transform t) {
+ // Freeze these bounds, as we are storing a reference to them.
+ freeze();
+
+ var tlink = new transformedBounds;
+ tlink.t = t;
+ tlink.link = this;
+
+ var b = new freezableBounds;
+ b.tlinks.push(tlink);
+
+ return b;
+ }
+
+ void append(freezableBounds b) {
+ // Check that we can modify the object.
+ assert(!frozen);
+
+ //TODO: If b is "small", ie. a single tlink or cliplink, just copy the
+ //link.
+
+ // As we only reference b, we must freeze it to ensure it does not change.
+ b.freeze();
+ links.push(b);
+ }
+
+ void addPoint(pair user, pair truesize) {
+ assert(!frozen);
+ point.push(user, truesize);
+ }
+
+ void addBox(pair userMin, pair userMax, pair trueMin, pair trueMax) {
+ assert(!frozen);
+ this.min.push(userMin, trueMin);
+ this.max.push(userMax, trueMax);
+ }
+
+ void addPath(path g) {
+ // This, and other asserts have been removed to speed things up slightly.
+ //assert(!frozen);
+ this.pathBounds.push(g);
+ }
+
+ void addPath(path[] g) {
+ //assert(!frozen);
+ this.pathBounds.append(g);
+ }
+
+ // To squeeze out a bit more performance, this method is either assigned
+ // addPathToNonEmptyArray or addPathToEmptyArray depending on the state of
+ // the pathpenBounds array.
+ void addPath(path g, pen p);
+
+ private void addPathToNonEmptyArray(path g, pen p) {
+ //assert(!frozen);
+ //assert(!pathpenBounds.empty());
+ var pp = pathpenBounds[0];
+
+ // Test if the pens are equal or have the same bounds.
+ if (pp.p == p || (min(pp.p) == min(p) && max(pp.p) == max(p))) {
+ // If this path has the same pen as the last one, just add it to the
+ // array corresponding to that pen.
+ pp.g.push(g);
+ }
+ else {
+ // A different pen. Start a new bound and put it on the front. Put
+ // the old bound at the end of the array.
+ pathpenBounds[0] = pathpen(g,p);
+ pathpenBounds.push(pp);
+ }
+ }
+ void addPathToEmptyArray(path g, pen p) {
+ //assert(!frozen);
+ //assert(pathpenBounds.empty());
+
+ pathpenBounds.push(pathpen(g,p));
+ addPath = addPathToNonEmptyArray;
+ }
+
+ // Initial setting for addPath.
+ addPath = addPathToEmptyArray;
+
+ // Transform the sizing info by t then add the result to the coords
+ // structure.
+ private void accumulateCoords(transform t, coords2 coords) {
+ for (var link : links)
+ link.accumulateCoords(t, coords);
+
+ for (var tlink : tlinks)
+ tlink.link.accumulateCoords(t*tlink.t, coords);
+
+ addTransformedCoords(coords, t, this.point, this.min, this.max);
+
+ for (var g : pathBounds) {
+ g = t*g;
+ coords.push(min(g), (0,0));
+ coords.push(max(g), (0,0));
+ }
+
+ for (var pp: pathpenBounds) {
+ pair pm = min(pp.p), pM = max(pp.p);
+ for (var g : pp.g) {
+ g = t*g;
+ coords.push(min(g), pm);
+ coords.push(max(g), pM);
+ }
+ }
+ }
+
+ // Add all of the sizing info to the given coords structure.
+ private void accumulateCoords(coords2 coords) {
+ for (var link : links)
+ link.accumulateCoords(coords);
+
+ for (var tlink : tlinks)
+ tlink.link.accumulateCoords(tlink.t, coords);
+
+ coords.append(this.point);
+ coords.append(this.min);
+ coords.append(this.max);
+
+ for (var g : pathBounds) {
+ coords.push(min(g), (0,0));
+ coords.push(max(g), (0,0));
+ }
+
+ for (var pp: pathpenBounds) {
+ pair pm = min(pp.p), pM = max(pp.p);
+ for (var g : pp.g) {
+ coords.push(min(g), pm);
+ coords.push(max(g), pM);
+ }
+ }
+ }
+
+ // Returns all of the coords that this sizing data represents.
+ private coords2 allCoords() {
+ coords2 coords;
+ accumulateCoords(coords);
+ return coords;
+ }
+
+ private void addLocalsToExtremes(transform t, extremes e) {
+ coords2 coords;
+ addTransformedCoords(coords, t, this.point, this.min, this.max);
+ addMinToExtremes(e, coords);
+ addMaxToExtremes(e, coords);
+
+ if (pathBounds.length > 0) {
+ addMinToExtremes(e, minAfterTransform(t, pathBounds), (0,0));
+ addMaxToExtremes(e, maxAfterTransform(t, pathBounds), (0,0));
+ }
+
+ for (var pp : pathpenBounds) {
+ if (pp.g.length > 0) {
+ addMinToExtremes(e, minAfterTransform(t, pp.g), min(pp.p));
+ addMaxToExtremes(e, maxAfterTransform(t, pp.g), max(pp.p));
+ }
+ }
+ }
+
+ private void addToExtremes(transform t, extremes e) {
+ for (var link : links)
+ link.addToExtremes(t, e);
+
+ for (var tlink : tlinks)
+ tlink.link.addToExtremes(t*tlink.t, e);
+
+ addLocalsToExtremes(t, e);
+ }
+
+ private void addLocalsToExtremes(extremes e) {
+ addMinToExtremes(e, point);
+ addMaxToExtremes(e, point);
+ addMinToExtremes(e, min);
+ addMaxToExtremes(e, max);
+
+ if (pathBounds.length > 0) {
+ addMinToExtremes(e, min(pathBounds), (0,0));
+ addMaxToExtremes(e, max(pathBounds), (0,0));
+ }
+
+ for(var pp : pathpenBounds) {
+ pair m=min(pp.p);
+ pair M=max(pp.p);
+ for(path gg : pp.g) {
+ if (size(gg) > 0) {
+ addMinToExtremes(e,min(gg),m);
+ addMaxToExtremes(e,max(gg),M);
+ }
+ }
+ }
+ }
+
+ private void addToExtremes(extremes e) {
+ for (var link : links)
+ link.addToExtremes(e);
+
+ for (var tlink : tlinks)
+ tlink.link.addToExtremes(tlink.t, e);
+
+ addLocalsToExtremes(e);
+ }
+
+ private static void write(extremes e) {
+ static void write(coord[] coords) {
+ for (coord c : coords)
+ write(" " + (string)c.user + " u + " + (string)c.truesize);
+ }
+ write("left:");
+ write(e.left);
+ write("bottom:");
+ write(e.bottom);
+ write("right:");
+ write(e.right);
+ write("top:");
+ write(e.top);
+ }
+
+ // Returns the extremal coordinates of the sizing data.
+ public extremes extremes() {
+ if (cachedExtremes == null) {
+ freeze();
+
+ extremes e;
+ addToExtremes(e);
+ cachedExtremes = e;
+ }
+
+ return cachedExtremes;
+ }
+
+ // Helper functions for computing the usersize bounds. usermin and usermax
+ // would be easily computable from extremes, except that the picture
+ // interface actually allows calls that manually change the usermin and
+ // usermax values. Therefore, we have to compute these values separately.
+ private static struct userbounds {
+ bool areSet=false;
+ pair min;
+ pair max;
+ }
+ private static struct boundsAccumulator {
+ pair[] mins;
+ pair[] maxs;
+
+ void push(pair m, pair M) {
+ mins.push(m);
+ maxs.push(M);
+ }
+
+ void push(userbounds b) {
+ if (b.areSet)
+ push(b.min, b.max);
+ }
+
+ void push(transform t, userbounds b) {
+ if (b.areSet) {
+ pair[] box = { t*(b.min.x,b.max.y), t*b.max,
+ t*b.min, t*(b.max.x,b.min.y) };
+ for (var z : box)
+ push(z,z);
+ }
+ }
+
+ void pushUserCoords(coords2 min, coords2 max) {
+ int n = min.x.length;
+ assert(min.y.length == n);
+ assert(max.x.length == n);
+ assert(max.y.length == n);
+
+ for (int i = 0; i < n; ++i)
+ push((min.x[i].user, min.y[i].user),
+ (max.x[i].user, max.y[i].user));
+ }
+
+ userbounds collapse() {
+ userbounds b;
+ if (mins.length > 0) {
+ b.areSet = true;
+ b.min = minbound(mins);
+ b.max = maxbound(maxs);
+ }
+ else {
+ b.areSet = false;
+ }
+ return b;
+ }
+ }
+
+ // The user bounds already calculated for this data.
+ private userbounds storedUserBounds = null;
+
+ private void accumulateUserBounds(boundsAccumulator acc)
+ {
+ if (storedUserBounds != null) {
+ assert(frozen);
+ acc.push(storedUserBounds);
+ } else {
+ acc.pushUserCoords(point, point);
+ acc.pushUserCoords(min, max);
+ if (pathBounds.length > 0)
+ acc.push(min(pathBounds), max(pathBounds));
+ for (var pp : pathpenBounds)
+ acc.push(min(pp.g), max(pp.g));
+ for (var link : links)
+ link.accumulateUserBounds(acc);
+
+ // Transforms are handled as they were in the old system.
+ for (var tlink : tlinks) {
+ boundsAccumulator tacc;
+ tlink.link.accumulateUserBounds(tacc);
+ acc.push(tlink.t, tacc.collapse());
+ }
+ }
+ }
+
+ private void computeUserBounds() {
+ freeze();
+ boundsAccumulator acc;
+ accumulateUserBounds(acc);
+ storedUserBounds = acc.collapse();
+ }
+
+ private userbounds userBounds() {
+ if (storedUserBounds == null)
+ computeUserBounds();
+
+ assert(storedUserBounds != null);
+ return storedUserBounds;
+ }
+
+ // userMin/userMax returns the minimal/maximal userspace coordinate of the
+ // sizing data. As coordinates for objects such as labels can have
+ // significant truesize dimensions, this userMin/userMax values may not
+ // correspond closely to the end of the screen, and are of limited use.
+ // userSetx and userSety determine if there is sizing data in order to even
+ // have userMin/userMax defined.
+ public bool userBoundsAreSet() {
+ return userBounds().areSet;
+ }
+
+ public pair userMin() {
+ return userBounds().min;
+ }
+ public pair userMax() {
+ return userBounds().max;
+ }
+
+ // To override the true userMin and userMax bounds, first compute the
+ // userBounds as they should be at this point, then change the values.
+ public void alterUserBound(string which, real val) {
+ // We are changing the bounds data, so it cannot be frozen yet. After the
+ // user bounds are set, however, the sizing data cannot change, so it will
+ // be frozen.
+ assert(!frozen);
+ computeUserBounds();
+ assert(frozen);
+
+ var b = storedUserBounds;
+ if (which == "minx")
+ b.min = (val, b.min.y);
+ else if (which == "miny")
+ b.min = (b.min.x, val);
+ else if (which == "maxx")
+ b.max = (val, b.max.y);
+ else {
+ assert(which == "maxy");
+ b.max = (b.max.x, val);
+ }
+ }
+
+ // A temporary measure. Stuffs all of the data from the links and paths
+ // into the coords.
+ private void flatten() {
+ assert(!frozen);
+
+ // First, compute the user bounds, taking into account any manual
+ // alterations.
+ computeUserBounds();
+
+ // Calculate all coordinates.
+ coords2 coords = allCoords();
+
+ // Erase all the old data.
+ point.erase();
+ min.erase();
+ max.erase();
+ pathBounds.delete();
+ pathpenBounds.delete();
+ addPath = addPathToEmptyArray;
+ links.delete();
+ tlinks.delete();
+
+ // Put all of the coordinates into point.
+ point = coords;
+ }
+
+ void xclip(real Min, real Max) {
+ assert(!frozen);
+ flatten();
+ point.xclip(Min,Max);
+ min.xclip(Min,Max);
+ max.xclip(Min,Max);
+
+ // Cap the userBounds.
+ userbounds b = storedUserBounds;
+ b.min = (max(Min, b.min.x), b.min.y);
+ b.max = (min(Max, b.max.x), b.max.y);
+ }
+
+ void yclip(real Min, real Max) {
+ assert(!frozen);
+ flatten();
+ point.yclip(Min,Max);
+ min.yclip(Min,Max);
+ max.yclip(Min,Max);
+
+ // Cap the userBounds.
+ userbounds b = storedUserBounds;
+ b.min = (b.min.x, max(Min, b.min.y));
+ b.max = (b.max.x, min(Max, b.max.y));
+ }
+
+ // Calculate the min for the final frame, given the coordinate transform.
+ pair min(transform t) {
+ extremes e = extremes();
+ if (e.left.length == 0)
+ return 0;
+
+ pair a=t*(1,1)-t*(0,0), b=t*(0,0);
+ scaling xs=scaling.build(a.x,b.x);
+ scaling ys=scaling.build(a.y,b.y);
+
+ return (min(infinity, xs, e.left), min(infinity, ys, e.bottom));
+ }
+
+ // Calculate the max for the final frame, given the coordinate transform.
+ pair max(transform t) {
+ extremes e = extremes();
+ if (e.right.length == 0)
+ return 0;
+
+ pair a=t*(1,1)-t*(0,0), b=t*(0,0);
+ scaling xs=scaling.build(a.x,b.x);
+ scaling ys=scaling.build(a.y,b.y);
+
+ return (max(-infinity, xs, e.right), max(-infinity, ys, e.top));
+ }
+
+ // Returns the transform for turning user-space pairs into true-space pairs.
+ transform scaling(real xsize, real ysize,
+ real xunitsize, real yunitsize,
+ bool keepAspect, bool warn) {
+ if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0)
+ return identity();
+
+ // Get the extremal coordinates.
+ extremes e = extremes();
+
+ real sx;
+ if(xunitsize == 0) {
+ if(xsize != 0) sx=calculateScaling("x",e.left,e.right,xsize,warn);
+ } else sx=xunitsize;
+
+ /* Possible alternative code :
+ real sx = xunitsize != 0 ? xunitsize :
+ xsize != 0 ? calculateScaling("x", Coords.x, xsize, warn) :
+ 0; */
+
+ real sy;
+ if(yunitsize == 0) {
+ if(ysize != 0) sy=calculateScaling("y",e.bottom,e.top,ysize,warn);
+ } else sy=yunitsize;
+
+ if(sx == 0) {
+ sx=sy;
+ if(sx == 0)
+ return identity();
+ } else if(sy == 0) sy=sx;
+
+
+ if(keepAspect && (xunitsize == 0 || yunitsize == 0))
+ return scale(min(sx,sy));
+ else
+ return scale(sx,sy);
+ }
+}
+
+struct bounds {
+ private var base = new freezableBounds;
+
+ // We should probably put this back into picture.
+ bool exact = true;
+
+ // Called just before modifying the sizing data. It ensures base is
+ // non-frozen.
+ // Note that this is manually inlined for speed reasons in a couple often
+ // called methods below.
+ private void makeMutable() {
+ if (base.frozen)
+ base = base.getMutable();
+ //assert(!base.frozen); // Disabled for speed reasons.
+ }
+
+ void erase() {
+ // Just discard the old bounds.
+ base = new freezableBounds;
+
+ // We don't reset the 'exact' field, for backward compatibility.
+ }
+
+ bounds copy() {
+ // Freeze the underlying bounds and make a shallow copy.
+ base.freeze();
+
+ var b = new bounds;
+ b.base = this.base;
+ b.exact = this.exact;
+ return b;
+ }
+
+ bounds transformed(transform t) {
+ var b = new bounds;
+ b.base = base.transformed(t);
+ b.exact = this.exact;
+ return b;
+ }
+
+ void append(bounds b) {
+ makeMutable();
+ base.append(b.base);
+ }
+
+ void append(transform t, bounds b) {
+ // makeMutable will be called by append.
+ if (t == identity())
+ append(b);
+ else
+ append(b.transformed(t));
+ }
+
+ void addPoint(pair user, pair truesize) {
+ makeMutable();
+ base.addPoint(user, truesize);
+ }
+
+ void addBox(pair userMin, pair userMax, pair trueMin, pair trueMax) {
+ makeMutable();
+ base.addBox(userMin, userMax, trueMin, trueMax);
+ }
+
+ void addPath(path g) {
+ //makeMutable(); // Manually inlined here for speed reasons.
+ if (base.frozen)
+ base = base.getMutable();
+ base.addPath(g);
+ }
+
+ void addPath(path[] g) {
+ //makeMutable(); // Manually inlined here for speed reasons.
+ if (base.frozen)
+ base = base.getMutable();
+ base.addPath(g);
+ }
+
+ void addPath(path g, pen p) {
+ //makeMutable(); // Manually inlined here for speed reasons.
+ if (base.frozen)
+ base = base.getMutable();
+ base.addPath(g, p);
+ }
+
+ public bool userBoundsAreSet() {
+ return base.userBoundsAreSet();
+ }
+ public pair userMin() {
+ return base.userMin();
+ }
+ public pair userMax() {
+ return base.userMax();
+ }
+ public void alterUserBound(string which, real val) {
+ makeMutable();
+ base.alterUserBound(which, val);
+ }
+
+ void xclip(real Min, real Max) {
+ makeMutable();
+ base.xclip(Min,Max);
+ }
+
+ void yclip(real Min, real Max) {
+ makeMutable();
+ base.yclip(Min,Max);
+ }
+
+ void clip(pair Min, pair Max) {
+ // TODO: If the user bounds have been manually altered, they may be
+ // incorrect after the clip.
+ xclip(Min.x,Max.x);
+ yclip(Min.y,Max.y);
+ }
+
+ pair min(transform t) {
+ return base.min(t);
+ }
+
+ pair max(transform t) {
+ return base.max(t);
+ }
+
+ transform scaling(real xsize, real ysize,
+ real xunitsize, real yunitsize,
+ bool keepAspect, bool warn) {
+ return base.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn);
+ }
+}
+
+bounds operator *(transform t, bounds b) {
+ return b.transformed(t);
+}
diff --git a/Master/texmf-dist/asymptote/plain_boxes.asy b/Master/texmf-dist/asymptote/plain_boxes.asy
new file mode 100644
index 00000000000..0c6401311d8
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_boxes.asy
@@ -0,0 +1,137 @@
+// Draw and/or fill a box on frame dest using the dimensions of frame src.
+path box(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ pair z=(xmargin,ymargin);
+ int sign=filltype == NoFill ? 1 : -1;
+ pair h=0.5*sign*(max(p)-min(p));
+ path g=box(min(src)-h-z,max(src)+h+z);
+ frame F;
+ if(above == false) {
+ filltype.fill(F,g,p);
+ prepend(dest,F);
+ } else filltype.fill(dest,g,p);
+ return g;
+}
+
+path roundbox(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ pair m=min(src);
+ pair M=max(src);
+ pair bound=M-m;
+ int sign=filltype == NoFill ? 1 : -1;
+ real a=bound.x+2*xmargin;
+ real b=bound.y+2*ymargin;
+ real ds=0;
+ real dw=min(a,b)*0.3;
+ path g=shift(m-(xmargin,ymargin))*((0,dw)--(0,b-dw){up}..{right}
+ (dw,b)--(a-dw,b){right}..{down}
+ (a,b-dw)--(a,dw){down}..{left}
+ (a-dw,0)--(dw,0){left}..{up}cycle);
+
+ frame F;
+ if(above == false) {
+ filltype.fill(F,g,p);
+ prepend(dest,F);
+ } else filltype.fill(dest,g,p);
+ return g;
+}
+
+path ellipse(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ pair m=min(src);
+ pair M=max(src);
+ pair D=M-m;
+ static real factor=0.5*sqrt(2);
+ int sign=filltype == NoFill ? 1 : -1;
+ pair h=0.5*sign*(max(p)-min(p));
+ path g=ellipse(0.5*(M+m),factor*D.x+h.x+xmargin,factor*D.y+h.y+ymargin);
+ frame F;
+ if(above == false) {
+ filltype.fill(F,g,p);
+ prepend(dest,F);
+ } else filltype.fill(dest,g,p);
+ return g;
+}
+
+path box(frame f, Label L, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ add(f,L);
+ return box(f,xmargin,ymargin,p,filltype,above);
+}
+
+path roundbox(frame f, Label L, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ add(f,L);
+ return roundbox(f,xmargin,ymargin,p,filltype,above);
+}
+
+path ellipse(frame f, Label L, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ add(f,L);
+ return ellipse(f,xmargin,ymargin,p,filltype,above);
+}
+
+typedef path envelope(frame dest, frame src=dest, real xmargin=0,
+ real ymargin=xmargin, pen p=currentpen,
+ filltype filltype=NoFill, bool above=true);
+
+object object(Label L, envelope e, real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill, bool above=true)
+{
+ object F;
+ F.L=L.copy();
+ Label L0=L.copy();
+ L0.position(0);
+ L0.p(p);
+ add(F.f,L0);
+ F.g=e(F.f,xmargin,ymargin,p,filltype);
+ return F;
+}
+
+object draw(picture pic=currentpicture, Label L, envelope e,
+ real xmargin=0, real ymargin=xmargin, pen p=currentpen,
+ filltype filltype=NoFill, bool above=true)
+{
+ object F=object(L,e,xmargin,ymargin,p,filltype,above);
+ pic.add(new void (frame f, transform t) {
+ frame d;
+ add(d,t,F.L);
+ e(f,d,xmargin,ymargin,p,filltype,above);
+ add(f,d);
+ },true);
+ pic.addBox(L.position,L.position,min(F.f),max(F.f));
+ return F;
+}
+
+object draw(picture pic=currentpicture, Label L, envelope e, pair position,
+ real xmargin=0, real ymargin=xmargin, pen p=currentpen,
+ filltype filltype=NoFill, bool above=true)
+{
+ return draw(pic,Label(L,position),e,xmargin,ymargin,p,filltype,above);
+}
+
+pair point(object F, pair dir, transform t=identity())
+{
+ pair m=min(F.g);
+ pair M=max(F.g);
+ pair c=0.5*(m+M);
+ pair z=t*F.L.position;
+ real[] T=intersect(F.g,c--2*(m+realmult(rectify(dir),M-m))-c);
+ if(T.length == 0) return z;
+ return z+point(F.g,T[0]);
+}
+
+frame bbox(picture pic=currentpicture,
+ real xmargin=0, real ymargin=xmargin,
+ pen p=currentpen, filltype filltype=NoFill)
+{
+ frame f=pic.fit(max(pic.xsize-2*xmargin,0),max(pic.ysize-2*ymargin,0));
+ box(f,xmargin,ymargin,p,filltype,above=false);
+ return f;
+}
diff --git a/Master/texmf-dist/asymptote/plain_constants.asy b/Master/texmf-dist/asymptote/plain_constants.asy
new file mode 100644
index 00000000000..bf6988d4c42
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_constants.asy
@@ -0,0 +1,166 @@
+restricted int undefined=(intMax % 2 == 1) ? intMax : intMax-1;
+
+restricted real inches=72;
+restricted real inch=inches;
+restricted real cm=inches/2.54;
+restricted real mm=0.1cm;
+restricted real bp=1; // A PostScript point.
+restricted real pt=72.0/72.27; // A TeX pt; smaller than a PostScript bp.
+restricted pair I=(0,1);
+
+restricted pair right=(1,0);
+restricted pair left=(-1,0);
+restricted pair up=(0,1);
+restricted pair down=(0,-1);
+
+restricted pair E=(1,0);
+restricted pair N=(0,1);
+restricted pair W=(-1,0);
+restricted pair S=(0,-1);
+
+restricted pair NE=unit(N+E);
+restricted pair NW=unit(N+W);
+restricted pair SW=unit(S+W);
+restricted pair SE=unit(S+E);
+
+restricted pair ENE=unit(E+NE);
+restricted pair NNE=unit(N+NE);
+restricted pair NNW=unit(N+NW);
+restricted pair WNW=unit(W+NW);
+restricted pair WSW=unit(W+SW);
+restricted pair SSW=unit(S+SW);
+restricted pair SSE=unit(S+SE);
+restricted pair ESE=unit(E+SE);
+
+restricted real sqrtEpsilon=sqrt(realEpsilon);
+restricted pair Align=sqrtEpsilon*NE;
+restricted int mantissaBits=ceil(-log(realEpsilon)/log(2))+1;
+
+int min(... int[] a) {return min(a);}
+int max(... int[] a) {return max(a);}
+
+real min(... real[] a) {return min(a);}
+real max(... real[] a) {return max(a);}
+
+bool finite(real x)
+{
+ return abs(x) < infinity;
+}
+
+bool finite(pair z)
+{
+ return abs(z.x) < infinity && abs(z.y) < infinity;
+}
+
+bool finite(triple v)
+{
+ return abs(v.x) < infinity && abs(v.y) < infinity && abs(v.z) < infinity;
+}
+
+restricted file stdin=input();
+restricted file stdout=output();
+
+void none(file file) {}
+void endl(file file) {write(file,'\n',flush);}
+void newl(file file) {write(file,'\n');}
+void DOSendl(file file) {write(file,'\r\n',flush);}
+void DOSnewl(file file) {write(file,'\r\n');}
+void tab(file file) {write(file,'\t');}
+void comma(file file) {write(file,',');}
+typedef void suffix(file);
+
+// Used by interactive write to warn that the outputted type is the resolution
+// of an overloaded name.
+void overloadedMessage(file file) {
+ write(file,' <overloaded>');
+ endl(file);
+}
+
+void write(suffix suffix=endl) {suffix(stdout);}
+void write(file file, suffix suffix=none) {suffix(file);}
+
+path box(pair a, pair b)
+{
+ return a--(b.x,a.y)--b--(a.x,b.y)--cycle;
+}
+
+restricted path unitsquare=box((0,0),(1,1));
+
+restricted path unitcircle=E..N..W..S..cycle;
+restricted real circleprecision=0.0006;
+
+restricted transform invert=reflect((0,0),(1,0));
+
+restricted pen defaultpen;
+
+// A type that takes on one of the values true, false, or default.
+struct bool3 {
+ bool value;
+ bool set;
+}
+
+void write(file file, string s="", bool3 b, suffix suffix=none)
+{
+ if(b.set) write(b.value,suffix);
+ else write("default",suffix);
+}
+
+void write(string s="", bool3 b, suffix suffix=endl)
+{
+ write(stdout,s,b,suffix);
+}
+
+restricted bool3 default;
+
+bool operator cast(bool3 b)
+{
+ return b.set && b.value;
+}
+
+bool3 operator cast(bool b)
+{
+ bool3 B;
+ B.value=b;
+ B.set=true;
+ return B;
+}
+
+bool operator == (bool3 a, bool3 b)
+{
+ return a.set == b.set && (!a.set || (a.value == b.value));
+}
+
+bool operator != (bool3 a, bool3 b)
+{
+ return a.set != b.set || (a.set && (a.value != b.value));
+}
+
+bool operator == (bool3 a, bool b)
+{
+ return a.set && a.value == b;
+}
+
+bool operator != (bool3 a, bool b)
+{
+ return !a.set || a.value != b;
+}
+
+bool operator == (bool a, bool3 b)
+{
+ return b.set && b.value == a;
+}
+
+bool operator != (bool a, bool3 b)
+{
+ return !b.set || b.value != a;
+}
+
+bool[] operator cast(bool3[] b)
+{
+ return sequence(new bool(int i) {return b[i];},b.length);
+}
+
+bool3[] operator cast(bool[] b)
+{
+ return sequence(new bool3(int i) {return b[i];},b.length);
+}
diff --git a/Master/texmf-dist/asymptote/plain_debugger.asy b/Master/texmf-dist/asymptote/plain_debugger.asy
new file mode 100644
index 00000000000..8568ee0bd8d
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_debugger.asy
@@ -0,0 +1,86 @@
+int debuggerlines=5;
+
+int sourceline(string file, string text)
+{
+ string file=locatefile(file);
+ string[] source=input(file);
+ for(int line=0; line < source.length; ++line)
+ if(find(source[line],text) >= 0) return line+1;
+ write("no matching line in "+file+": \""+text+"\"");
+ return 0;
+}
+
+void stop(string file, string text, code s=quote{})
+{
+ int line=sourceline(file,text);
+ if(line > 0) stop(file,line,s);
+}
+
+void clear(string file, string text)
+{
+ int line=sourceline(file,text);
+ if(line > 0) clear(file,line);
+}
+
+// Enable debugging.
+bool debugging=true;
+
+// Variables used by conditional expressions:
+// e.g. stop("test",2,quote{ignore=(++count <= 10);});
+
+bool ignore;
+int count=0;
+
+string debugger(string file, int line, int column, code s=quote{})
+{
+ int verbose=settings.verbose;
+ settings.verbose=0;
+ _eval(s,true);
+ if(ignore) {
+ ignore=false;
+ settings.verbose=verbose;
+ return "c";
+ }
+ static string s;
+ if(debugging) {
+ static string lastfile;
+ static string[] source;
+ bool help=false;
+ while(true) {
+ if(file != lastfile && file != "-") {source=input(file); lastfile=file;}
+ write();
+ for(int i=max(line-debuggerlines,0); i < min(line,source.length); ++i)
+ write(source[i]);
+ for(int i=0; i < column-1; ++i)
+ write(" ",none);
+ write("^"+(verbose == 5 ? " trace" : ""));
+
+ if(help) {
+ write("c:continue f:file h:help i:inst n:next r:return s:step t:trace q:quit x:exit");
+ help=false;
+ }
+
+ string Prompt=file+": "+(string) line+"."+(string) column;
+ Prompt += "? [%s] ";
+ s=getstring(name="debug",default="h",prompt=Prompt,store=false);
+ if(s == "h" || s == "?") {help=true; continue;}
+ if(s == "c" || s == "s" || s == "n" || s == "i" || s == "f" || s == "r")
+ break;
+ if(s == "q") {debugging=false; abort();} // quit
+ if(s == "x") {debugging=false; return "";} // exit
+ if(s == "t") { // trace
+ if(verbose == 0) {
+ verbose=5;
+ } else {
+ verbose=0;
+ }
+ continue;
+ }
+ _eval(s+";",true);
+ }
+ }
+ settings.verbose=verbose;
+ return s;
+}
+
+atbreakpoint(debugger);
diff --git a/Master/texmf-dist/asymptote/plain_filldraw.asy b/Master/texmf-dist/asymptote/plain_filldraw.asy
new file mode 100644
index 00000000000..a5eea35ef25
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_filldraw.asy
@@ -0,0 +1,248 @@
+// Draw path g on frame f with user-constructed pen p.
+void makedraw(frame f, path g, pen p, int depth=mantissaBits)
+{
+ if(depth == 0) return;
+ --depth;
+
+ path n=nib(p);
+ for(int i=0; i < size(g); ++i)
+ fill(f,shift(point(g,i))*n,p);
+
+ static real epsilon=1000*realEpsilon;
+ int L=length(g);
+ real stop=L-epsilon;
+ int N=length(n);
+ pair first=point(n,0);
+ pair n0=first;
+
+ for(int i=0; i < N; ++i) {
+ pair n1=point(n,i+1);
+ pair dir=unit(n1-n0);
+ real t=dirtime(g,-dir);
+ if(straight(g,(int) t)) t=ceil(t);
+ if(t > epsilon && t < stop) {
+ makedraw(f,subpath(g,0,t),p,depth);
+ makedraw(f,subpath(g,t,L),p,depth);
+ return;
+ }
+ real t=dirtime(g,dir);
+ if(straight(g,(int) t)) t=ceil(t);
+ if(t > epsilon && t < stop) {
+ makedraw(f,subpath(g,0,t),p,depth);
+ makedraw(f,subpath(g,t,L),p,depth);
+ return;
+ }
+ n0=n1;
+ }
+
+ n0=first;
+ for(int i=0; i < N; ++i) {
+ pair n1=point(n,i+1);
+ fill(f,shift(n0)*g--shift(n1)*reverse(g)--cycle,p);
+ n0=n1;
+ }
+}
+
+void draw(frame f, path g, pen p=currentpen)
+{
+ if(size(nib(p)) == 0) _draw(f,g,p);
+ else {
+ begingroup(f);
+ makedraw(f,g,p);
+ endgroup(f);
+ }
+}
+
+void draw(frame f, explicit path[] g, pen p=currentpen)
+{
+ for(int i=0; i < g.length; ++i) draw(f,g[i],p);
+}
+
+void draw(frame f, guide[] g, pen p=currentpen)
+{
+ for(int i=0; i < g.length; ++i) draw(f,g[i],p);
+}
+
+void filldraw(frame f, path[] g, pen fillpen=currentpen,
+ pen drawpen=currentpen)
+{
+ begingroup(f);
+ fill(f,g,fillpen);
+ draw(f,g,drawpen);
+ endgroup(f);
+}
+
+path[] complement(frame f, path[] g)
+{
+ static pair margin=(0.5,0.5);
+ return box(minbound(min(f),min(g))-margin,maxbound(max(f),max(g))+margin)^^g;
+}
+
+void unfill(frame f, path[] g, bool copy=true)
+{
+ clip(f,complement(f,g),evenodd,copy);
+}
+
+void filloutside(frame f, path[] g, pen p=currentpen, bool copy=true)
+{
+ fill(f,complement(f,g),p+evenodd,copy);
+}
+
+struct filltype
+{
+ typedef void fill2(frame f, path[] g, pen fillpen);
+ fill2 fill2;
+ pen fillpen;
+ pen drawpen;
+
+ int type;
+ static int Fill=1;
+ static int FillDraw=2;
+ static int Draw=3;
+ static int NoFill=4;
+ static int UnFill=5;
+
+ void operator init(int type=0, pen fillpen=nullpen, pen drawpen=nullpen,
+ fill2 fill2) {
+ this.type=type;
+ this.fillpen=fillpen;
+ this.drawpen=drawpen;
+ this.fill2=fill2;
+ }
+ void fill(frame f, path[] g, pen p) {fill2(f,g,p);}
+}
+
+path[] margin(path[] g, real xmargin, real ymargin)
+{
+ if(xmargin != 0 || ymargin != 0) {
+ pair M=max(g);
+ pair m=min(g);
+ real width=M.x-m.x;
+ real height=M.y-m.y;
+ real xfactor=width > 0 ? (width+2xmargin)/width : 1;
+ real yfactor=height > 0 ? (height+2ymargin)/height : 1;
+ g=scale(xfactor,yfactor)*g;
+ g=shift(0.5*(M+m)-0.5*(max(g)+min(g)))*g;
+ }
+ return g;
+}
+
+filltype Fill(real xmargin=0, real ymargin=xmargin, pen p=nullpen)
+{
+ return filltype(filltype.Fill,p,new void(frame f, path[] g, pen fillpen) {
+ if(p != nullpen) fillpen=p;
+ if(fillpen == nullpen) fillpen=currentpen;
+ fill(f,margin(g,xmargin,ymargin),fillpen);
+ });
+}
+
+filltype FillDraw(real xmargin=0, real ymargin=xmargin,
+ pen fillpen=nullpen, pen drawpen=nullpen)
+{
+ return filltype(filltype.FillDraw,fillpen,drawpen,
+ new void(frame f, path[] g, pen Drawpen) {
+ if(drawpen != nullpen) Drawpen=drawpen;
+ pen Fillpen=fillpen == nullpen ? Drawpen : fillpen;
+ if(Fillpen == nullpen) Fillpen=currentpen;
+ if(Drawpen == nullpen) Drawpen=Fillpen;
+ if(cyclic(g[0]))
+ filldraw(f,margin(g,xmargin,ymargin),Fillpen,Drawpen);
+ else
+ draw(f,margin(g,xmargin,ymargin),Drawpen);
+ });
+}
+
+filltype Draw(real xmargin=0, real ymargin=xmargin, pen p=nullpen)
+{
+ return filltype(filltype.Draw,drawpen=p,
+ new void(frame f, path[] g, pen drawpen) {
+ pen drawpen=p == nullpen ? drawpen : p;
+ if(drawpen == nullpen) drawpen=currentpen;
+ draw(f,margin(g,xmargin,ymargin),drawpen);
+ });
+}
+
+filltype NoFill=filltype(filltype.NoFill,new void(frame f, path[] g, pen p) {
+ draw(f,g,p);
+ });
+
+
+filltype UnFill(real xmargin=0, real ymargin=xmargin)
+{
+ return filltype(filltype.UnFill,new void(frame f, path[] g, pen) {
+ unfill(f,margin(g,xmargin,ymargin));
+ });
+}
+
+filltype FillDraw=FillDraw(), Fill=Fill(), Draw=Draw(), UnFill=UnFill();
+
+// Fill varying radially from penc at the center of the bounding box to
+// penr at the edge.
+filltype RadialShade(pen penc, pen penr)
+{
+ return filltype(new void(frame f, path[] g, pen) {
+ pair c=(min(g)+max(g))/2;
+ radialshade(f,g,penc,c,0,penr,c,abs(max(g)-min(g))/2);
+ });
+}
+
+filltype RadialShadeDraw(real xmargin=0, real ymargin=xmargin,
+ pen penc, pen penr, pen drawpen=nullpen)
+{
+ return filltype(new void(frame f, path[] g, pen Drawpen) {
+ if(drawpen != nullpen) Drawpen=drawpen;
+ if(Drawpen == nullpen) Drawpen=penc;
+ pair c=(min(g)+max(g))/2;
+ if(cyclic(g[0]))
+ radialshade(f,margin(g,xmargin,ymargin),penc,c,0,penr,c,
+ abs(max(g)-min(g))/2);
+ draw(f,margin(g,xmargin,ymargin),Drawpen);
+ });
+}
+
+// Fill the region in frame dest underneath frame src and return the
+// boundary of src.
+path fill(frame dest, frame src, filltype filltype=NoFill,
+ real xmargin=0, real ymargin=xmargin)
+{
+ pair z=(xmargin,ymargin);
+ path g=box(min(src)-z,max(src)+z);
+ filltype.fill(dest,g,nullpen);
+ return g;
+}
+
+// Add frame dest to frame src with optional grouping and background fill.
+void add(frame dest, frame src, bool group, filltype filltype=NoFill,
+ bool above=true)
+{
+ if(above) {
+ if(filltype != NoFill) fill(dest,src,filltype);
+ if(group) begingroup(dest);
+ add(dest,src);
+ if(group) endgroup(dest);
+ } else {
+ if(group) {
+ frame f;
+ endgroup(f);
+ prepend(dest,f);
+ }
+ prepend(dest,src);
+ if(group) {
+ frame f;
+ begingroup(f);
+ prepend(dest,f);
+ }
+ if(filltype != NoFill) {
+ frame f;
+ fill(f,src,filltype);
+ prepend(dest,f);
+ }
+ }
+}
+
+void add(frame dest, frame src, filltype filltype,
+ bool above=filltype.type != filltype.UnFill)
+{
+ if(filltype != NoFill) fill(dest,src,filltype);
+ (above ? add : prepend)(dest,src);
+}
diff --git a/Master/texmf-dist/asymptote/plain_margins.asy b/Master/texmf-dist/asymptote/plain_margins.asy
new file mode 100644
index 00000000000..c4522754481
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_margins.asy
@@ -0,0 +1,99 @@
+struct marginT {
+ path g;
+ real begin,end;
+};
+
+typedef marginT margin(path, pen);
+
+path trim(path g, real begin, real end) {
+ real a=arctime(g,begin);
+ real b=arctime(g,arclength(g)-end);
+ return a <= b ? subpath(g,a,b) : point(g,a);
+}
+
+margin operator +(margin ma, margin mb)
+{
+ return new marginT(path g, pen p) {
+ marginT margin;
+ real ba=ma(g,p).begin < 0 ? 0 : ma(g,p).begin;
+ real bb=mb(g,p).begin < 0 ? 0 : mb(g,p).begin;
+ real ea=ma(g,p).end < 0 ? 0 : ma(g,p).end;
+ real eb=mb(g,p).end < 0 ? 0 : mb(g,p).end;
+ margin.begin=ba+bb;
+ margin.end=ea+eb;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin NoMargin()
+{
+ return new marginT(path g, pen) {
+ marginT margin;
+ margin.begin=margin.end=0;
+ margin.g=g;
+ return margin;
+ };
+}
+
+margin Margin(real begin, real end)
+{
+ return new marginT(path g, pen p) {
+ marginT margin;
+ real factor=labelmargin(p);
+ margin.begin=begin*factor;
+ margin.end=end*factor;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin PenMargin(real begin, real end)
+{
+ return new marginT(path g, pen p) {
+ marginT margin;
+ real factor=linewidth(p);
+ margin.begin=(begin+0.5)*factor;
+ margin.end=(end+0.5)*factor;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin DotMargin(real begin, real end)
+{
+ return new marginT(path g, pen p) {
+ marginT margin;
+ real margindot(real x) {return x > 0 ? dotfactor*x : x;}
+ real factor=linewidth(p);
+ margin.begin=(margindot(begin)+0.5)*factor;
+ margin.end=(margindot(end)+0.5)*factor;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin TrueMargin(real begin, real end)
+{
+ return new marginT(path g, pen p) {
+ marginT margin;
+ margin.begin=begin;
+ margin.end=end;
+ margin.g=trim(g,begin,end);
+ return margin;
+ };
+}
+
+margin NoMargin=NoMargin(),
+ BeginMargin=Margin(1,0),
+ Margin=Margin(0,1),
+ EndMargin=Margin,
+ Margins=Margin(1,1),
+ BeginPenMargin=PenMargin(0.5,-0.5),
+ PenMargin=PenMargin(-0.5,0.5),
+ EndPenMargin=PenMargin,
+ PenMargins=PenMargin(0.5,0.5),
+ BeginDotMargin=DotMargin(0.5,-0.5),
+ DotMargin=DotMargin(-0.5,0.5),
+ EndDotMargin=DotMargin,
+ DotMargins=DotMargin(0.5,0.5);
diff --git a/Master/texmf-dist/asymptote/plain_markers.asy b/Master/texmf-dist/asymptote/plain_markers.asy
new file mode 100644
index 00000000000..42b4e8a036c
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_markers.asy
@@ -0,0 +1,398 @@
+real legendlinelength=50;
+real legendhskip=1.2;
+real legendvskip=legendhskip;
+real legendmargin=10;
+real legendmaxrelativewidth=1;
+
+// Return a unit polygon with n sides.
+path polygon(int n)
+{
+ guide g;
+ for(int i=0; i < n; ++i) g=g--expi(2pi*(i+0.5)/n-0.5*pi);
+ return g--cycle;
+}
+
+// Return a unit n-point cyclic cross, with optional inner radius r and
+// end rounding.
+path cross(int n, bool round=true, real r=0)
+{
+ assert(n > 1);
+ real r=min(r,1);
+ real theta=pi/n;
+ real s=sin(theta);
+ real c=cos(theta);
+ pair z=(c,s);
+ transform mirror=reflect(0,z);
+ pair p1=(r,0);
+ path elementary;
+ if(round) {
+ pair e1=p1+z*max(1-r*(s+c),0);
+ elementary=p1--e1..(c,s)..mirror*e1--mirror*p1;
+ } else {
+ pair p2=p1+z*(max(sqrt(1-(r*s)^2)-r*c),0);
+ elementary=p1--p2--mirror*p2--mirror*p1;
+ }
+
+ guide g;
+ real step=360/n;
+ for(int i=0; i < n; ++i)
+ g=g--rotate(i*step-90)*elementary;
+
+ return g--cycle;
+}
+
+path[] plus=(-1,0)--(1,0)^^(0,-1)--(0,1);
+
+typedef void markroutine(picture pic=currentpicture, frame f, path g);
+
+// On picture pic, add frame f about every node of path g.
+void marknodes(picture pic=currentpicture, frame f, path g) {
+ for(int i=0; i < size(g); ++i)
+ add(pic,f,point(g,i));
+}
+
+// On picture pic, add n copies of frame f to path g, evenly spaced in
+// arclength.
+// If rotated=true, the frame will be rotated by the angle of the tangent
+// to the path at the points where the frame will be added.
+// If centered is true, center the frames within n evenly spaced arclength
+// intervals.
+markroutine markuniform(bool centered=false, int n, bool rotated=false) {
+ return new void(picture pic=currentpicture, frame f, path g) {
+ if(n <= 0) return;
+ void add(real x) {
+ real t=reltime(g,x);
+ add(pic,rotated ? rotate(degrees(dir(g,t)))*f : f,point(g,t));
+ }
+ if(centered) {
+ real width=1/n;
+ for(int i=0; i < n; ++i) add((i+0.5)*width);
+ } else {
+ if(n == 1) add(0.5);
+ else {
+ real width=1/(n-1);
+ for(int i=0; i < n; ++i)
+ add(i*width);
+ }
+ }
+ };
+}
+
+// On picture pic, add frame f at points z(t) for n evenly spaced values of
+// t in [a,b].
+markroutine markuniform(pair z(real t), real a, real b, int n)
+{
+ return new void(picture pic=currentpicture, frame f, path) {
+ real width=b-a;
+ for(int i=0; i <= n; ++i) {
+ add(pic,f,z(a+i/n*width));
+ }
+ };
+}
+
+struct marker {
+ frame f;
+ bool above=true;
+ markroutine markroutine=marknodes;
+ void mark(picture pic=currentpicture, path g) {
+ markroutine(pic,f,g);
+ };
+}
+
+marker marker(frame f=newframe, markroutine markroutine=marknodes,
+ bool above=true)
+{
+ marker m=new marker;
+ m.f=f;
+ m.above=above;
+ m.markroutine=markroutine;
+ return m;
+}
+
+marker marker(path[] g, markroutine markroutine=marknodes, pen p=currentpen,
+ filltype filltype=NoFill, bool above=true)
+{
+ frame f;
+ filltype.fill(f,g,p);
+ return marker(f,markroutine,above);
+}
+
+// On picture pic, add path g with opacity thinning about every node.
+marker markthin(path g, pen p=currentpen,
+ real thin(real fraction)=new real(real x) {return x^2;},
+ filltype filltype=NoFill) {
+ marker M=new marker;
+ M.above=true;
+ filltype.fill(M.f,g,p);
+ real factor=1/abs(size(M.f));
+ M.markroutine=new void(picture pic=currentpicture, frame, path G) {
+ transform t=pic.calculateTransform();
+ int n=size(G);
+ for(int i=0; i < n; ++i) {
+ pair z=point(G,i);
+ frame f;
+ real fraction=1;
+ if(i > 0) fraction=min(fraction,abs(t*(z-point(G,i-1)))*factor);
+ if(i < n-1) fraction=min(fraction,abs(t*(point(G,i+1)-z))*factor);
+ filltype.fill(f,g,p+opacity(thin(fraction)));
+ add(pic,f,point(G,i));
+ }
+ };
+ return M;
+}
+
+marker nomarker;
+
+real circlescale=0.85;
+
+path[] MarkPath={scale(circlescale)*unitcircle,
+ polygon(3),polygon(4),polygon(5),invert*polygon(3),
+ cross(4),cross(6)};
+
+marker[] Mark=sequence(new marker(int i) {return marker(MarkPath[i]);},
+ MarkPath.length);
+
+marker[] MarkFill=sequence(new marker(int i) {return marker(MarkPath[i],Fill);},
+ MarkPath.length-2);
+
+marker Mark(int n)
+{
+ n=n % (Mark.length+MarkFill.length);
+ if(n < Mark.length) return Mark[n];
+ else return MarkFill[n-Mark.length];
+}
+
+picture legenditem(Legend legenditem, real linelength)
+{
+ picture pic;
+ pair z1=(0,0);
+ pair z2=z1+(linelength,0);
+ if(!legenditem.above && !empty(legenditem.mark))
+ marknodes(pic,legenditem.mark,interp(z1,z2,0.5));
+ if(linelength > 0)
+ Draw(pic,z1--z2,legenditem.p);
+ if(legenditem.above && !empty(legenditem.mark))
+ marknodes(pic,legenditem.mark,interp(z1,z2,0.5));
+ if(legenditem.plabel != invisible)
+ label(pic,legenditem.label,z2,E,legenditem.plabel);
+ else
+ label(pic,legenditem.label,z2,E,currentpen);
+ return pic;
+}
+
+picture legend(Legend[] Legend, int perline=1, real linelength,
+ real hskip, real vskip, real maxwidth=0, real maxheight=0,
+ bool hstretch=false, bool vstretch=false)
+{
+ if(maxwidth <= 0) hstretch=false;
+ if(maxheight <= 0) vstretch=false;
+ if(Legend.length <= 1) vstretch=hstretch=false;
+
+ picture inset;
+ size(inset,0,0,IgnoreAspect);
+
+ if(Legend.length == 0)
+ return inset;
+
+ // Check for legend entries with lines:
+ bool bLineEntriesAvailable=false;
+ for(int i=0; i < Legend.length; ++i) {
+ if(Legend[i].p != invisible) {
+ bLineEntriesAvailable=true;
+ break;
+ }
+ }
+
+ real markersize=0;
+ for(int i=0; i < Legend.length; ++i)
+ markersize=max(markersize,size(Legend[i].mark).x);
+
+ // If no legend has a line, set the line length to zero
+ if(!bLineEntriesAvailable)
+ linelength=0;
+
+ linelength=max(linelength,markersize*(linelength == 0 ? 1 : 2));
+
+ // Get the maximum dimensions per legend entry;
+ // calculate line length for a one-line legend
+ real heightPerEntry=0;
+ real widthPerEntry=0;
+ real totalwidth=0;
+ for(int i=0; i < Legend.length; ++i) {
+ picture pic=legenditem(Legend[i],linelength);
+ pair lambda=size(pic);
+ heightPerEntry=max(heightPerEntry,lambda.y);
+ widthPerEntry=max(widthPerEntry,lambda.x);
+ if(Legend[i].p != invisible)
+ totalwidth += lambda.x;
+ else {
+ // Legend entries without leading line need less space in one-line legends
+ picture pic=legenditem(Legend[i],0);
+ totalwidth += size(pic).x;
+ }
+ }
+ // Does everything fit into one line?
+ if(((perline < 1) || (perline >= Legend.length)) &&
+ (maxwidth >= totalwidth+(totalwidth/Legend.length)*
+ (Legend.length-1)*(hskip-1))) {
+ // One-line legend
+ real currPosX=0;
+ real itemDistance;
+ if(hstretch)
+ itemDistance=(maxwidth-totalwidth)/(Legend.length-1);
+ else
+ itemDistance=(totalwidth/Legend.length)*(hskip-1);
+ for(int i=0; i < Legend.length; ++i) {
+ picture pic=legenditem(Legend[i],
+ Legend[i].p == invisible ? 0 : linelength);
+ add(inset,pic,(currPosX,0));
+ currPosX += size(pic).x+itemDistance;
+ }
+ } else {
+ // multiline legend
+ if(maxwidth > 0) {
+ int maxperline=floor(maxwidth/(widthPerEntry*hskip));
+ if((perline < 1) || (perline > maxperline))
+ perline=maxperline;
+ }
+ if(perline < 1) // This means: maxwidth < widthPerEntry
+ perline=1;
+
+ if(perline <= 1) hstretch=false;
+ if(hstretch) hskip=(maxwidth/widthPerEntry-perline)/(perline-1)+1;
+ if(vstretch) {
+ int rows=ceil(Legend.length/perline);
+ vskip=(maxheight/heightPerEntry-rows)/(rows-1)+1;
+ }
+
+ if(hstretch && (perline == 1)) {
+ Draw(inset,(0,0)--(maxwidth,0),invisible());
+ for(int i=0; i < Legend.length; ++i)
+ add(inset,legenditem(Legend[i],linelength),
+ (0.5*(maxwidth-widthPerEntry),
+ -quotient(i,perline)*heightPerEntry*vskip));
+ } else
+ for(int i=0; i < Legend.length; ++i)
+ add(inset,legenditem(Legend[i],linelength),
+ ((i%perline)*widthPerEntry*hskip,
+ -quotient(i,perline)*heightPerEntry*vskip));
+ }
+
+ return inset;
+}
+
+frame legend(picture pic=currentpicture, int perline=1,
+ real xmargin=legendmargin, real ymargin=xmargin,
+ real linelength=legendlinelength,
+ real hskip=legendhskip, real vskip=legendvskip,
+ real maxwidth=perline == 0 ?
+ legendmaxrelativewidth*size(pic).x : 0, real maxheight=0,
+ bool hstretch=false, bool vstretch=false, pen p=currentpen)
+{
+ frame F;
+ if(pic.legend.length == 0) return F;
+ F=legend(pic.legend,perline,linelength,hskip,vskip,
+ max(maxwidth-2xmargin,0),
+ max(maxheight-2ymargin,0),
+ hstretch,vstretch).fit();
+ box(F,xmargin,ymargin,p);
+ return F;
+}
+
+pair[] pairs(real[] x, real[] y)
+{
+ if(x.length != y.length) abort("arrays have different lengths");
+ return sequence(new pair(int i) {return (x[i],y[i]);},x.length);
+}
+
+void dot(frame f, pair z, pen p=currentpen, filltype filltype=Fill)
+{
+ if(filltype == Fill)
+ draw(f,z,dotsize(p)+p);
+ else {
+ transform t=shift(z);
+ path g=t*scale(0.5*(dotsize(p)-linewidth(p)))*unitcircle;
+ begingroup(f);
+ filltype.fill(f,g,p);
+ draw(f,g,p);
+ endgroup(f);
+ }
+}
+
+void dot(picture pic=currentpicture, pair z, pen p=currentpen,
+ filltype filltype=Fill)
+{
+ pic.add(new void(frame f, transform t) {
+ dot(f,t*z,p,filltype);
+ },true);
+ pic.addPoint(z,dotsize(p)+p);
+}
+
+void dot(picture pic=currentpicture, Label L, pair z, align align=NoAlign,
+ string format=defaultformat, pen p=currentpen, filltype filltype=Fill)
+{
+ Label L=L.copy();
+ L.position(z);
+ if(L.s == "") {
+ if(format == "") format=defaultformat;
+ L.s="("+format(format,z.x)+","+format(format,z.y)+")";
+ }
+ L.align(align,E);
+ L.p(p);
+ dot(pic,z,p,filltype);
+ add(pic,L);
+}
+
+void dot(picture pic=currentpicture, Label[] L=new Label[], pair[] z,
+ align align=NoAlign, string format=defaultformat, pen p=currentpen,
+ filltype filltype=Fill)
+{
+ int stop=min(L.length,z.length);
+ for(int i=0; i < stop; ++i)
+ dot(pic,L[i],z[i],align,format,p,filltype);
+ for(int i=stop; i < z.length; ++i)
+ dot(pic,z[i],p,filltype);
+}
+
+void dot(picture pic=currentpicture, Label[] L=new Label[],
+ explicit path g, align align=RightSide, string format=defaultformat,
+ pen p=currentpen, filltype filltype=Fill)
+{
+ int n=size(g);
+ int stop=min(L.length,n);
+ for(int i=0; i < stop; ++i)
+ dot(pic,L[i],point(g,i),-sgn(align.dir.x)*I*dir(g,i),format,p,filltype);
+ for(int i=stop; i < n; ++i)
+ dot(pic,point(g,i),p,filltype);
+}
+
+void dot(picture pic=currentpicture, path[] g, pen p=currentpen,
+ filltype filltype=Fill)
+{
+ for(int i=0; i < g.length; ++i)
+ dot(pic,g[i],p,filltype);
+}
+
+void dot(picture pic=currentpicture, Label L, pen p=currentpen,
+ filltype filltype=Fill)
+{
+ dot(pic,L,L.position,p,filltype);
+}
+
+// A dot in a frame.
+frame dotframe(pen p=currentpen, filltype filltype=Fill)
+{
+ frame f;
+ dot(f,(0,0),p,filltype);
+ return f;
+}
+
+frame dotframe=dotframe();
+
+marker dot(pen p=currentpen, filltype filltype=Fill)
+{
+ return marker(dotframe(p,filltype));
+}
+
+marker dot=dot();
+
diff --git a/Master/texmf-dist/asymptote/plain_paths.asy b/Master/texmf-dist/asymptote/plain_paths.asy
new file mode 100644
index 00000000000..c768922ffd1
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_paths.asy
@@ -0,0 +1,386 @@
+path nullpath;
+
+typedef guide interpolate(... guide[]);
+
+// These numbers identify the side of a specifier in an operator spec or
+// operator curl expression:
+// a{out} .. {in}b
+restricted int JOIN_OUT=0;
+restricted int JOIN_IN=1;
+
+// Define a.. tension t ..b to be equivalent to
+// a.. tension t and t ..b
+// and likewise with controls.
+tensionSpecifier operator tension(real t, bool atLeast)
+{
+ return operator tension(t,t,atLeast);
+}
+
+guide operator controls(pair z)
+{
+ return operator controls(z,z);
+}
+
+guide[] operator cast(pair[] z)
+{
+ return sequence(new guide(int i) {return z[i];},z.length);
+}
+
+path[] operator cast(pair[] z)
+{
+ return sequence(new path(int i) {return z[i];},z.length);
+}
+
+path[] operator cast(guide[] g)
+{
+ return sequence(new path(int i) {return g[i];},g.length);
+}
+
+guide[] operator cast(path[] g)
+{
+ return sequence(new guide(int i) {return g[i];},g.length);
+}
+
+path[] operator cast(path p)
+{
+ return new path[] {p};
+}
+
+path[] operator cast(guide g)
+{
+ return new path[] {(path) g};
+}
+
+path[] operator ^^ (path p, path q)
+{
+ return new path[] {p,q};
+}
+
+path[] operator ^^ (path p, explicit path[] q)
+{
+ return concat(new path[] {p},q);
+}
+
+path[] operator ^^ (explicit path[] p, path q)
+{
+ return concat(p,new path[] {q});
+}
+
+path[] operator ^^ (explicit path[] p, explicit path[] q)
+{
+ return concat(p,q);
+}
+
+path[] operator * (transform t, explicit path[] p)
+{
+ return sequence(new path(int i) {return t*p[i];},p.length);
+}
+
+pair[] operator * (transform t, pair[] z)
+{
+ return sequence(new pair(int i) {return t*z[i];},z.length);
+}
+
+void write(file file, string s="", explicit path[] x, suffix suffix=none)
+{
+ write(file,s);
+ if(x.length > 0) write(file,x[0]);
+ for(int i=1; i < x.length; ++i) {
+ write(file,endl);
+ write(file," ^^");
+ write(file,x[i]);
+ }
+ write(file,suffix);
+}
+
+void write(string s="", explicit path[] x, suffix suffix=endl)
+{
+ write(stdout,s,x,suffix);
+}
+
+void write(file file, string s="", explicit guide[] x, suffix suffix=none)
+{
+ write(file,s);
+ if(x.length > 0) write(file,x[0]);
+ for(int i=1; i < x.length; ++i) {
+ write(file,endl);
+ write(file," ^^");
+ write(file,x[i]);
+ }
+ write(file,suffix);
+}
+
+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) {
+ if(a.length == 0) return nullpath;
+ guide g=a[0];
+ for(int i=1; i < a.length; ++i)
+ g=g..t..a[i];
+ return g;
+ };
+}
+
+interpolate operator ::=operator ..(operator tension(1,true));
+interpolate operator ---=operator ..(operator tension(infinity,true));
+
+// return an arbitrary intersection point of paths p and q
+pair intersectionpoint(path p, path q, real fuzz=-1)
+{
+ real[] t=intersect(p,q,fuzz);
+ if(t.length == 0) abort("paths do not intersect");
+ return point(p,t[0]);
+}
+
+// return an array containing all intersection points of the paths p and q
+pair[] intersectionpoints(path p, path q, real fuzz=-1)
+{
+ real[][] t=intersections(p,q,fuzz);
+ return sequence(new pair(int i) {return point(p,t[i][0]);},t.length);
+}
+
+pair[] intersectionpoints(explicit path[] p, explicit path[] q, real fuzz=-1)
+{
+ pair[] z;
+ for(int i=0; i < p.length; ++i)
+ for(int j=0; j < q.length; ++j)
+ z.append(intersectionpoints(p[i],q[j],fuzz));
+ return z;
+}
+
+struct slice {
+ path before,after;
+}
+
+slice cut(path p, path knife, int n)
+{
+ slice s;
+ real[][] T=intersections(p,knife);
+ if(T.length == 0) {s.before=p; s.after=nullpath; return s;}
+ T.cyclic=true;
+ real t=T[n][0];
+ s.before=subpath(p,0,t);
+ s.after=subpath(p,t,length(p));
+ return s;
+}
+
+slice firstcut(path p, path knife)
+{
+ return cut(p,knife,0);
+}
+
+slice lastcut(path p, path knife)
+{
+ return cut(p,knife,-1);
+}
+
+pair dir(path p)
+{
+ return dir(p,length(p));
+}
+
+pair dir(path p, path q)
+{
+ return unit(dir(p)+dir(q));
+}
+
+// return the point on path p at arclength L
+pair arcpoint(path p, real L)
+{
+ return point(p,arctime(p,L));
+}
+
+// return the direction on path p at arclength L
+pair arcdir(path p, real L)
+{
+ return dir(p,arctime(p,L));
+}
+
+// return the time on path p at the relative fraction l of its arclength
+real reltime(path p, real l)
+{
+ return arctime(p,l*arclength(p));
+}
+
+// return the point on path p at the relative fraction l of its arclength
+pair relpoint(path p, real l)
+{
+ return point(p,reltime(p,l));
+}
+
+// return the direction of path p at the relative fraction l of its arclength
+pair reldir(path p, real l)
+{
+ return dir(p,reltime(p,l));
+}
+
+// return the initial point of path p
+pair beginpoint(path p)
+{
+ return point(p,0);
+}
+
+// return the point on path p at half of its arclength
+pair midpoint(path p)
+{
+ return relpoint(p,0.5);
+}
+
+// return the final point of path p
+pair endpoint(path p)
+{
+ return point(p,length(p));
+}
+
+path operator &(path p, cycleToken tok)
+{
+ int n=length(p);
+ if(n < 0) return nullpath;
+ if(n == 0) return p--cycle;
+ if(cyclic(p)) return p;
+ return straight(p,n-1) ? subpath(p,0,n-1)--cycle :
+ subpath(p,0,n-1)..controls postcontrol(p,n-1) and precontrol(p,n)..cycle;
+}
+
+// return a cyclic path enclosing a region bounded by a list of two or more
+// consecutively intersecting paths
+path buildcycle(... path[] p)
+{
+ int n=p.length;
+ if(n < 2) return nullpath;
+ real[] ta=new real[n];
+ real[] tb=new real[n];
+ if(n == 2) {
+ real[][] t=intersections(p[0],p[1]);
+ if(t.length < 2)
+ return nullpath;
+ int k=t.length-1;
+ ta[0]=t[0][0]; tb[0]=t[k][0];
+ ta[1]=t[k][1]; tb[1]=t[0][1];
+ } else {
+ int j=n-1;
+ for(int i=0; i < n; ++i) {
+ real[][] t=intersections(p[i],p[j]);
+ if(t.length == 0)
+ return nullpath;
+ ta[i]=t[0][0]; tb[j]=t[0][1];
+ j=i;
+ }
+ }
+
+ pair c;
+ for(int i=0; i < n ; ++i)
+ c += point(p[i],ta[i]);
+ c /= n;
+
+ path G;
+ for(int i=0; i < n ; ++i) {
+ real Ta=ta[i];
+ real Tb=tb[i];
+ if(cyclic(p[i])) {
+ int L=length(p[i]);
+ real t=Tb-L;
+ if(abs(c-point(p[i],0.5(Ta+t))) <
+ abs(c-point(p[i],0.5(Ta+Tb)))) Tb=t;
+ while(Tb < Ta) Tb += L;
+ }
+ G=G&subpath(p[i],Ta,Tb);
+ }
+ return G&cycle;
+}
+
+// return 1 if p strictly contains q,
+// -1 if q strictly contains p,
+// 0 otherwise.
+int inside(path p, path q, pen fillrule=currentpen)
+{
+ if(intersect(p,q).length > 0) return 0;
+ if(cyclic(p) && inside(p,point(q,0),fillrule)) return 1;
+ if(cyclic(q) && inside(q,point(p,0),fillrule)) return -1;
+ return 0;
+}
+
+// Return an arbitrary point strictly inside a cyclic path p according to
+// the specified fill rule.
+pair inside(path p, pen fillrule=currentpen)
+{
+ if(!cyclic(p)) abort("path is not cyclic");
+ int n=length(p);
+ for(int i=0; i < n; ++i) {
+ pair z=point(p,i);
+ pair dir=dir(p,i);
+ if(dir == 0) continue;
+ real[] T=intersections(p,z,z+I*dir);
+ // Check midpoints of line segments formed between the
+ // corresponding intersection points and z.
+ for(int j=0; j < T.length; ++j) {
+ if(T[j] != i) {
+ pair w=point(p,T[j]);
+ pair m=0.5*(z+w);
+ if(interior(windingnumber(p,m),fillrule)) return m;
+ }
+ }
+ }
+ // cannot find an interior point: path is degenerate
+ return point(p,0);
+}
+
+// Return all intersection times of path g with the vertical line through (x,0).
+real[] times(path p, real x)
+{
+ return intersections(p,(x,0),(x,1));
+}
+
+// Return all intersection times of path g with the horizontal line through
+// (0,z.y).
+real[] times(path p, explicit pair z)
+{
+ return intersections(p,(0,z.y),(1,z.y));
+}
+
+path randompath(int n, bool cumulate=true, interpolate join=operator ..)
+{
+ guide g;
+ pair w;
+ for(int i=0; i <= n; ++i) {
+ pair z=(unitrand()-0.5,unitrand()-0.5);
+ if(cumulate) w += z;
+ else w=z;
+ g=join(g,w);
+ }
+ return g;
+}
+
+path[] strokepath(path g, pen p=currentpen)
+{
+ path[] G=_strokepath(g,p);
+ if(G.length == 0) return G;
+ pair center(path g) {return 0.5*(min(g)+max(g));}
+ pair center(path[] g) {return 0.5*(min(g)+max(g));}
+ return shift(center(g)-center(G))*G;
+}
diff --git a/Master/texmf-dist/asymptote/plain_pens.asy b/Master/texmf-dist/asymptote/plain_pens.asy
new file mode 100644
index 00000000000..fcf9d31e638
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_pens.asy
@@ -0,0 +1,361 @@
+real labelmargin=0.3;
+real dotfactor=6;
+
+pen solid=linetype(new real[]);
+pen dotted=linetype(new real[] {0,4});
+pen dashed=linetype(new real[] {8,8});
+pen longdashed=linetype(new real[] {24,8});
+pen dashdotted=linetype(new real[] {8,8,0,8});
+pen longdashdotted=linetype(new real[] {24,8,0,8});
+
+pen linetype(string pattern, real offset=0, bool scale=true, bool adjust=true)
+{
+ return linetype((real[]) split(pattern),offset,scale,adjust);
+}
+
+void defaultpen(real w) {defaultpen(linewidth(w));}
+pen operator +(pen p, real w) {return p+linewidth(w);}
+pen operator +(real w, pen p) {return linewidth(w)+p;}
+
+pen Dotted(pen p=currentpen) {return linetype(new real[] {0,3})+2*linewidth(p);}
+pen Dotted=Dotted();
+
+restricted pen squarecap=linecap(0);
+restricted pen roundcap=linecap(1);
+restricted pen extendcap=linecap(2);
+
+restricted pen miterjoin=linejoin(0);
+restricted pen roundjoin=linejoin(1);
+restricted pen beveljoin=linejoin(2);
+
+restricted pen zerowinding=fillrule(0);
+restricted pen evenodd=fillrule(1);
+
+bool interior(int windingnumber, pen fillrule)
+{
+ return windingnumber != undefined &&
+ (fillrule(fillrule) == 1 ? windingnumber % 2 == 1 : windingnumber != 0);
+}
+
+restricted pen nobasealign=basealign(0);
+restricted pen basealign=basealign(1);
+
+pen invisible=invisible();
+pen thin() {return settings.thin ? linewidth(0) : defaultpen;}
+pen thick(pen p=currentpen) {return linewidth(linewidth(p));}
+pen nullpen=linewidth(0)+invisible;
+
+pen black=gray(0);
+pen white=gray(1);
+pen gray=gray(0.5);
+
+pen red=rgb(1,0,0);
+pen green=rgb(0,1,0);
+pen blue=rgb(0,0,1);
+
+pen Cyan=cmyk(1,0,0,0);
+pen Magenta=cmyk(0,1,0,0);
+pen Yellow=cmyk(0,0,1,0);
+pen Black=cmyk(0,0,0,1);
+
+pen cyan=rgb(0,1,1);
+pen magenta=rgb(1,0,1);
+pen yellow=rgb(1,1,0);
+
+pen palered=rgb(1,0.75,0.75);
+pen palegreen=rgb(0.75,1,0.75);
+pen paleblue=rgb(0.75,0.75,1);
+pen palecyan=rgb(0.75,1,1);
+pen palemagenta=rgb(1,0.75,1);
+pen paleyellow=rgb(1,1,0.75);
+pen palegray=gray(0.95);
+
+pen lightred=rgb(1,0.5,0.5);
+pen lightgreen=rgb(0.5,1,0.5);
+pen lightblue=rgb(0.5,0.5,1);
+pen lightcyan=rgb(0.5,1,1);
+pen lightmagenta=rgb(1,0.5,1);
+pen lightyellow=rgb(1,1,0.5);
+pen lightgray=gray(0.9);
+
+pen mediumred=rgb(1,0.25,0.25);
+pen mediumgreen=rgb(0.25,1,0.25);
+pen mediumblue=rgb(0.25,0.25,1);
+pen mediumcyan=rgb(0.25,1,1);
+pen mediummagenta=rgb(1,0.25,1);
+pen mediumyellow=rgb(1,1,0.25);
+pen mediumgray=gray(0.75);
+
+pen heavyred=rgb(0.75,0,0);
+pen heavygreen=rgb(0,0.75,0);
+pen heavyblue=rgb(0,0,0.75);
+pen heavycyan=rgb(0,0.75,0.75);
+pen heavymagenta=rgb(0.75,0,0.75);
+pen lightolive=rgb(0.75,0.75,0);
+pen heavygray=gray(0.25);
+
+pen deepred=rgb(0.5,0,0);
+pen deepgreen=rgb(0,0.5,0);
+pen deepblue=rgb(0,0,0.5);
+pen deepcyan=rgb(0,0.5,0.5);
+pen deepmagenta=rgb(0.5,0,0.5);
+pen olive=rgb(0.5,0.5,0);
+pen deepgray=gray(0.1);
+
+pen darkred=rgb(0.25,0,0);
+pen darkgreen=rgb(0,0.25,0);
+pen darkblue=rgb(0,0,0.25);
+pen darkcyan=rgb(0,0.25,0.25);
+pen darkmagenta=rgb(0.25,0,0.25);
+pen darkolive=rgb(0.25,0.25,0);
+pen darkgray=gray(0.05);
+
+pen orange=rgb(1,0.5,0);
+pen fuchsia=rgb(1,0,0.5);
+
+pen chartreuse=rgb(0.5,1,0);
+pen springgreen=rgb(0,1,0.5);
+
+pen purple=rgb(0.5,0,1);
+pen royalblue=rgb(0,0.5,1);
+
+// Synonyms:
+
+pen salmon=lightred;
+pen brown=deepred;
+pen darkbrown=darkred;
+pen pink=palemagenta;
+pen palegrey=palegray;
+pen lightgrey=lightgray;
+pen mediumgrey=mediumgray;
+pen grey=gray;
+pen heavygrey=heavygray;
+pen deepgrey=deepgray;
+pen darkgrey=darkgray;
+
+// Options for handling label overwriting
+restricted int Allow=0;
+restricted int Suppress=1;
+restricted int SuppressQuiet=2;
+restricted int Move=3;
+restricted int MoveQuiet=4;
+
+pen[] colorPen={red,blue,green,magenta,cyan,orange,purple,brown,
+ deepblue,deepgreen,chartreuse,fuchsia,lightred,
+ lightblue,black,pink,yellow,gray};
+
+colorPen.cyclic=true;
+
+pen[] monoPen={solid,dashed,dotted,longdashed,dashdotted,
+ longdashdotted};
+monoPen.cyclic=true;
+
+pen Pen(int n)
+{
+ return (settings.gray || settings.bw) ? monoPen[n] : colorPen[n];
+}
+
+pen Pentype(int n)
+{
+ return (settings.gray || settings.bw) ? monoPen[n] : monoPen[n]+colorPen[n];
+}
+
+real dotsize(pen p=currentpen)
+{
+ return dotfactor*linewidth(p);
+}
+
+pen fontsize(real size)
+{
+ return fontsize(size,1.2*size);
+}
+
+real labelmargin(pen p=currentpen)
+{
+ return labelmargin*fontsize(p);
+}
+
+void write(file file=stdout, string s="", pen[] p)
+{
+ for(int i=0; i < p.length; ++i)
+ write(file,s,p[i],endl);
+}
+
+void usetypescript(string s, string encoding="")
+{
+ string s="\usetypescript["+s+"]";
+ if(encoding != "") s +="["+encoding+"]";
+ texpreamble(s);
+}
+
+pen font(string name, string options="")
+{
+ // Work around misalignment in ConTeXt switchtobodyfont if font is not found.
+ return fontcommand(settings.tex == "context" ?
+ "\switchtobodyfont["+name+
+ (options == "" ? "" : ","+options)+
+ "]\removeunwantedspaces" :
+ "\font\ASYfont="+name+"\ASYfont");
+}
+
+pen font(string name, real size, string options="")
+{
+ string s=(string) (size/pt)+"pt";
+ if(settings.tex == "context")
+ return fontsize(size)+font(name+","+s,options);
+ return fontsize(size)+font(name+" at "+s);
+}
+
+pen font(string encoding, string family, string series, string shape)
+{
+ return fontcommand("\usefont{"+encoding+"}{"+family+"}{"+series+"}{"+shape+
+ "}");
+}
+
+pen AvantGarde(string series="m", string shape="n")
+{
+ return font("OT1","pag",series,shape);
+}
+pen Bookman(string series="m", string shape="n")
+{
+ return font("OT1","pbk",series,shape);
+}
+pen Courier(string series="m", string shape="n")
+{
+ return font("OT1","pcr",series,shape);
+}
+pen Helvetica(string series="m", string shape="n")
+{
+ return font("OT1","phv",series,shape);
+}
+pen NewCenturySchoolBook(string series="m", string shape="n")
+{
+ return font("OT1","pnc",series,shape);
+}
+pen Palatino(string series="m", string shape="n")
+{
+ return font("OT1","ppl",series,shape);
+}
+pen TimesRoman(string series="m", string shape="n")
+{
+ return font("OT1","ptm",series,shape);
+}
+pen ZapfChancery(string series="m", string shape="n")
+{
+ return font("OT1","pzc",series,shape);
+}
+pen Symbol(string series="m", string shape="n")
+{
+ return font("OT1","psy",series,shape);
+}
+pen ZapfDingbats(string series="m", string shape="n")
+{
+ return font("OT1","pzd",series,shape);
+}
+
+pen squarepen=makepen(shift(-0.5,-0.5)*unitsquare);
+
+struct hsv {
+ real h;
+ real v;
+ real s;
+ void operator init(real h, real s, real v) {
+ this.h=h;
+ this.s=s;
+ this.v=v;
+ }
+ void operator init(pen p) {
+ real[] c=colors(rgb(p));
+ real r=c[0];
+ real g=c[1];
+ real b=c[2];
+ real M=max(r,g,b);
+ real m=min(r,g,b);
+ if(M == m) this.h=0;
+ else {
+ real denom=1/(M-m);
+ if(M == r) {
+ this.h=60*(g-b)*denom;
+ if(g < b) h += 360;
+ } else if(M == g) {
+ this.h=60*(b-r)*denom+120;
+ } else
+ this.h=60*(r-g)*denom+240;
+ }
+ this.s=M == 0 ? 0 : 1-m/M;
+ this.v=M;
+ }
+ // return an rgb pen corresponding to h in [0,360) and s and v in [0,1].
+ pen rgb() {
+ real H=(h % 360)/60;
+ int i=floor(H) % 6;
+ real f=H-i;
+ real[] V={v,v*(1-s),v*(1-(i % 2 == 0 ? 1-f : f)*s)};
+ int[] a={0,2,1,1,2,0};
+ int[] b={2,0,0,2,1,1};
+ int[] c={1,1,2,0,0,2};
+ return rgb(V[a[i]],V[b[i]],V[c[i]]);
+ }
+}
+
+pen operator cast(hsv hsv)
+{
+ return hsv.rgb();
+}
+
+hsv operator cast(pen p)
+{
+ return hsv(p);
+}
+
+real[] rgba(pen p)
+{
+ real[] a=colors(rgb(p));
+ a.push(opacity(p));
+ return a;
+}
+
+pen rgba(real[] a)
+{
+ return rgb(a[0],a[1],a[2])+opacity(a[3]);
+}
+
+// Return a pen corresponding to a given 6-character RGB hexidecimal string.
+pen rgb(string s)
+{
+ real value(string s, int i) {return hex(substr(s,2i,2))/255;}
+ return rgb(value(s,0),value(s,1),value(s,2));
+}
+
+pen[] operator +(pen[] a, pen b)
+{
+ return sequence(new pen(int i) {return a[i]+b;},a.length);
+}
+
+pen[] operator +(pen a, pen[] b)
+{
+ return sequence(new pen(int i) {return a+b[i];},b.length);
+}
+
+// Interpolate an array of pens in rgb space using by default their minimum
+// opacity.
+pen mean(pen[] p, real opacity(real[])=min)
+{
+ if(p.length == 0) return nullpen;
+ real[] a=rgba(p[0]);
+ real[] t=new real[p.length];
+ t[0]=a[3];
+ for(int i=1; i < p.length; ++i) {
+ real[] b=rgba(p[i]);
+ a += b;
+ t[i]=b[3];
+ }
+ a /= p.length;
+ return rgb(a[0],a[1],a[2])+opacity(opacity(t));
+}
+
+pen[] mean(pen[][] palette, real opacity(real[])=min)
+{
+ return sequence(new pen(int i) {return mean(palette[i],opacity);},
+ palette.length);
+}
diff --git a/Master/texmf-dist/asymptote/plain_picture.asy b/Master/texmf-dist/asymptote/plain_picture.asy
new file mode 100644
index 00000000000..5b06b7f737c
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_picture.asy
@@ -0,0 +1,1646 @@
+// Pre picture <<<1
+import plain_scaling;
+import plain_bounds;
+
+include plain_prethree;
+
+// This variable is required by asymptote.sty.
+pair viewportsize=0; // Horizontal and vertical viewport limits.
+
+restricted bool Aspect=true;
+restricted bool IgnoreAspect=false;
+
+struct coords3 {
+ coord[] x,y,z;
+ void erase() {
+ x.delete();
+ y.delete();
+ z.delete();
+ }
+ // Only a shallow copy of the individual elements of x and y
+ // is needed since, once entered, they are never modified.
+ coords3 copy() {
+ coords3 c=new coords3;
+ c.x=copy(x);
+ c.y=copy(y);
+ c.z=copy(z);
+ return c;
+ }
+ void append(coords3 c) {
+ x.append(c.x);
+ y.append(c.y);
+ z.append(c.z);
+ }
+ void push(triple user, triple truesize) {
+ x.push(coord.build(user.x,truesize.x));
+ y.push(coord.build(user.y,truesize.y));
+ z.push(coord.build(user.z,truesize.z));
+ }
+ void push(coord cx, coord cy, coord cz) {
+ x.push(cx);
+ y.push(cy);
+ z.push(cz);
+ }
+ void push(transform3 t, coords3 c1, coords3 c2, coords3 c3) {
+ for(int i=0; i < c1.x.length; ++i) {
+ coord cx=c1.x[i], cy=c2.y[i], cz=c3.z[i];
+ triple tinf=shiftless(t)*(0,0,0);
+ triple z=t*(cx.user,cy.user,cz.user);
+ triple w=(cx.truesize,cy.truesize,cz.truesize);
+ w=length(w)*unit(shiftless(t)*w);
+ coord Cx,Cy,Cz;
+ Cx.user=z.x;
+ Cy.user=z.y;
+ Cz.user=z.z;
+ Cx.truesize=w.x;
+ Cy.truesize=w.y;
+ Cz.truesize=w.z;
+ push(Cx,Cy,Cz);
+ }
+ }
+}
+
+// scaleT and Legend <<<
+typedef real scalefcn(real x);
+
+struct scaleT {
+ scalefcn T,Tinv;
+ bool logarithmic;
+ bool automin,automax;
+ void operator init(scalefcn T, scalefcn Tinv, bool logarithmic=false,
+ bool automin=false, bool automax=false) {
+ this.T=T;
+ this.Tinv=Tinv;
+ this.logarithmic=logarithmic;
+ this.automin=automin;
+ this.automax=automax;
+ }
+ scaleT copy() {
+ scaleT dest=scaleT(T,Tinv,logarithmic,automin,automax);
+ return dest;
+ }
+};
+
+scaleT operator init()
+{
+ scaleT S=scaleT(identity,identity);
+ return S;
+}
+
+typedef void boundRoutine();
+
+struct autoscaleT {
+ scaleT scale;
+ scaleT postscale;
+ real tickMin=-infinity, tickMax=infinity;
+ boundRoutine[] bound; // Optional routines to recompute the bounding box.
+ bool automin=false, automax=false;
+ bool automin() {return automin && scale.automin;}
+ bool automax() {return automax && scale.automax;}
+
+ real T(real x) {return postscale.T(scale.T(x));}
+ scalefcn T() {return scale.logarithmic ? postscale.T : T;}
+ real Tinv(real x) {return scale.Tinv(postscale.Tinv(x));}
+
+ autoscaleT copy() {
+ autoscaleT dest=new autoscaleT;
+ dest.scale=scale.copy();
+ dest.postscale=postscale.copy();
+ dest.tickMin=tickMin;
+ dest.tickMax=tickMax;
+ dest.bound=copy(bound);
+ dest.automin=(bool) automin;
+ dest.automax=(bool) automax;
+ return dest;
+ }
+}
+
+struct ScaleT {
+ bool set;
+ autoscaleT x;
+ autoscaleT y;
+ autoscaleT z;
+
+ ScaleT copy() {
+ ScaleT dest=new ScaleT;
+ dest.set=set;
+ dest.x=x.copy();
+ dest.y=y.copy();
+ dest.z=z.copy();
+ return dest;
+ }
+};
+
+struct Legend {
+ string label;
+ pen plabel;
+ pen p;
+ frame mark;
+ bool above;
+ void operator init(string label, pen plabel=currentpen, pen p=nullpen,
+ frame mark=newframe, bool above=true) {
+ this.label=label;
+ this.plabel=plabel;
+ this.p=(p == nullpen) ? plabel : p;
+ this.mark=mark;
+ this.above=above;
+ }
+}
+
+// >>>
+
+// Frame Alignment was here
+
+triple min3(pen p)
+{
+ return linewidth(p)*(-0.5,-0.5,-0.5);
+}
+
+triple max3(pen p)
+{
+ return linewidth(p)*(0.5,0.5,0.5);
+}
+
+// A function that draws an object to frame pic, given that the transform
+// from user coordinates to true-size coordinates is t.
+typedef void drawer(frame f, transform t);
+
+// A generalization of drawer that includes the final frame's bounds.
+// TODO: Add documentation as to what T is.
+typedef void drawerBound(frame f, transform t, transform T, pair lb, pair rt);
+
+// PairOrTriple <<<1
+// This struct is used to represent a userMin/userMax which serves as both a
+// pair and a triple depending on the context.
+struct pairOrTriple {
+ real x,y,z;
+ void init() { x = y = z = 0; }
+};
+void copyPairOrTriple(pairOrTriple dest, pairOrTriple src)
+{
+ dest.x = src.x;
+ dest.y = src.y;
+ dest.z = src.z;
+}
+pair operator cast (pairOrTriple a) {
+ return (a.x, a.y);
+};
+triple operator cast (pairOrTriple a) {
+ return (a.x, a.y, a.z);
+}
+void write(pairOrTriple a) {
+ write((triple) a);
+}
+
+struct picture { // <<<1
+ // Nodes <<<2
+ // Three-dimensional version of drawer and drawerBound:
+ typedef void drawer3(frame f, transform3 t, picture pic, projection P);
+ typedef void drawerBound3(frame f, transform3 t, transform3 T,
+ picture pic, projection P, triple lb, triple rt);
+
+ // The functions to do the deferred drawing.
+ drawerBound[] nodes;
+ drawerBound3[] nodes3;
+
+ bool uptodate=true;
+
+ struct bounds3 {
+ coords3 point,min,max;
+ bool exact=true; // An accurate picture bounds is provided by the user.
+ void erase() {
+ point.erase();
+ min.erase();
+ max.erase();
+ }
+ bounds3 copy() {
+ bounds3 b=new bounds3;
+ b.point=point.copy();
+ b.min=min.copy();
+ b.max=max.copy();
+ b.exact=exact;
+ return b;
+ }
+ }
+
+ bounds bounds;
+ bounds3 bounds3;
+
+ // Other Fields <<<2
+ // Transform to be applied to this picture.
+ transform T;
+ transform3 T3;
+
+ // The internal representation of the 3D user bounds.
+ private pairOrTriple umin, umax;
+ private bool usetx, usety, usetz;
+
+ ScaleT scale; // Needed by graph
+ Legend[] legend;
+
+ pair[] clipmax; // Used by beginclip/endclip
+ pair[] clipmin;
+
+ // The maximum sizes in the x, y, and z directions; zero means no restriction.
+ real xsize=0, ysize=0;
+
+ real xsize3=0, ysize3=0, zsize3=0;
+
+ // Fixed unitsizes in the x y, and z directions; zero means use
+ // xsize, ysize, and zsize.
+ real xunitsize=0, yunitsize=0, zunitsize=0;
+
+ // If true, the x and y directions must be scaled by the same amount.
+ bool keepAspect=true;
+
+ // A fixed scaling transform.
+ bool fixed;
+ transform fixedscaling;
+
+ // Init and erase <<<2
+ void init() {
+ umin.init();
+ umax.init();
+ usetx=usety=usetz=false;
+ T3=identity(4);
+ }
+ init();
+
+ // Erase the current picture, retaining any size specification.
+ void erase() {
+ nodes.delete();
+ nodes3.delete();
+ bounds.erase();
+ bounds3.erase();
+ T=identity();
+ scale=new ScaleT;
+ legend.delete();
+ init();
+ }
+
+ // Empty <<<2
+ bool empty2() {
+ return nodes.length == 0;
+ }
+
+ bool empty3() {
+ return nodes3.length == 0;
+ }
+
+ bool empty() {
+ return empty2() && empty3();
+ }
+
+ // User min/max <<<2
+ pair userMin2() {return bounds.userMin(); }
+ pair userMax2() {return bounds.userMax(); }
+
+ bool userSetx2() { return bounds.userBoundsAreSet(); }
+ bool userSety2() { return bounds.userBoundsAreSet(); }
+
+ triple userMin3() { return umin; }
+ triple userMax3() { return umax; }
+
+ bool userSetx3() { return usetx; }
+ bool userSety3() { return usety; }
+ bool userSetz3() { return usetz; }
+
+ private typedef real binop(real, real);
+
+ // Helper functions for finding the minimum/maximum of two data, one of
+ // which may not be defined.
+ private static real merge(real x1, bool set1, real x2, bool set2, binop m)
+ {
+ return set1 ? (set2 ? m(x1,x2) : x1) : x2;
+ }
+ private pairOrTriple userExtreme(pair u2(), triple u3(), binop m)
+ {
+ bool setx2 = userSetx2();
+ bool sety2 = userSety2();
+ bool setx3 = userSetx3();
+ bool sety3 = userSety3();
+
+ pair p;
+ if (setx2 || sety2)
+ p = u2();
+ triple t = u3();
+
+ pairOrTriple r;
+ r.x = merge(p.x, setx2, t.x, setx3, m);
+ r.y = merge(p.y, sety2, t.y, sety3, m);
+ r.z = t.z;
+
+ return r;
+ }
+
+ // The combination of 2D and 3D data.
+ pairOrTriple userMin() {
+ return userExtreme(userMin2, userMin3, min);
+ }
+ pairOrTriple userMax() {
+ return userExtreme(userMax2, userMax3, max);
+ }
+
+ bool userSetx() { return userSetx2() || userSetx3(); }
+ bool userSety() { return userSety2() || userSety3(); }
+ bool userSetz() = userSetz3;
+
+ // Functions for setting the user bounds.
+ void userMinx3(real x) {
+ umin.x=x;
+ usetx=true;
+ }
+
+ void userMiny3(real y) {
+ umin.y=y;
+ usety=true;
+ }
+
+ void userMinz3(real z) {
+ umin.z=z;
+ usetz=true;
+ }
+
+ void userMaxx3(real x) {
+ umax.x=x;
+ usetx=true;
+ }
+
+ void userMaxy3(real y) {
+ umax.y=y;
+ usety=true;
+ }
+
+ void userMaxz3(real z) {
+ umax.z=z;
+ usetz=true;
+ }
+
+ void userMinx2(real x) { bounds.alterUserBound("minx", x); }
+ void userMinx(real x) { userMinx2(x); userMinx3(x); }
+ void userMiny2(real y) { bounds.alterUserBound("miny", y); }
+ void userMiny(real y) { userMiny2(y); userMiny3(y); }
+ void userMaxx2(real x) { bounds.alterUserBound("maxx", x); }
+ void userMaxx(real x) { userMaxx2(x); userMaxx3(x); }
+ void userMaxy2(real y) { bounds.alterUserBound("maxy", y); }
+ void userMaxy(real y) { userMaxy2(y); userMaxy3(y); }
+ void userMinz(real z) = userMinz3;
+ void userMaxz(real z) = userMaxz3;
+
+ void userCorners3(triple c000, triple c001, triple c010, triple c011,
+ triple c100, triple c101, triple c110, triple c111) {
+ umin.x = min(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x);
+ umin.y = min(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y);
+ umin.z = min(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z);
+ umax.x = max(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x);
+ umax.y = max(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y);
+ umax.z = max(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z);
+ }
+
+ // Cache the current user-space bounding box x coodinates
+ void userBoxX3(real min, real max, binop m=min, binop M=max) {
+ if (usetx) {
+ umin.x=m(umin.x,min);
+ umax.x=M(umax.x,max);
+ } else {
+ umin.x=min;
+ umax.x=max;
+ usetx=true;
+ }
+ }
+
+ // Cache the current user-space bounding box y coodinates
+ void userBoxY3(real min, real max, binop m=min, binop M=max) {
+ if (usety) {
+ umin.y=m(umin.y,min);
+ umax.y=M(umax.y,max);
+ } else {
+ umin.y=min;
+ umax.y=max;
+ usety=true;
+ }
+ }
+
+ // Cache the current user-space bounding box z coodinates
+ void userBoxZ3(real min, real max, binop m=min, binop M=max) {
+ if (usetz) {
+ umin.z=m(umin.z,min);
+ umax.z=M(umax.z,max);
+ } else {
+ umin.z=min;
+ umax.z=max;
+ usetz=true;
+ }
+ }
+
+ // Cache the current user-space bounding box
+ void userBox3(triple min, triple max) {
+ userBoxX3(min.x,max.x);
+ userBoxY3(min.y,max.y);
+ userBoxZ3(min.z,max.z);
+ }
+
+ // Add drawer <<<2
+ void add(drawerBound d, bool exact=false, bool above=true) {
+ uptodate=false;
+ if(!exact) bounds.exact=false;
+ if(above)
+ nodes.push(d);
+ else
+ nodes.insert(0,d);
+ }
+
+ // Faster implementation of most common case.
+ void addExactAbove(drawerBound d) {
+ uptodate=false;
+ nodes.push(d);
+ }
+
+ void add(drawer d, bool exact=false, bool above=true) {
+ add(new void(frame f, transform t, transform T, pair, pair) {
+ d(f,t*T);
+ },exact,above);
+ }
+
+ void add(drawerBound3 d, bool exact=false, bool above=true) {
+ uptodate=false;
+ if(!exact) bounds.exact=false;
+ if(above)
+ nodes3.push(d);
+ else
+ nodes3.insert(0,d);
+ }
+
+ void add(drawer3 d, bool exact=false, bool above=true) {
+ add(new void(frame f, transform3 t, transform3 T, picture pic,
+ projection P, triple, triple) {
+ d(f,t*T,pic,P);
+ },exact,above);
+ }
+
+ // Clip <<<2
+ void clip(pair min, pair max, drawer d, bool exact=false) {
+ bounds.clip(min, max);
+ this.add(d,exact);
+ }
+
+ void clip(pair min, pair max, drawerBound d, bool exact=false) {
+ bounds.clip(min, max);
+ this.add(d,exact);
+ }
+
+ // Add sizing <<<2
+ // Add a point to the sizing.
+ void addPoint(pair user, pair truesize=0) {
+ bounds.addPoint(user,truesize);
+ //userBox(user,user);
+ }
+
+ // Add a point to the sizing, accounting also for the size of the pen.
+ void addPoint(pair user, pair truesize=0, pen p) {
+ addPoint(user,truesize+min(p));
+ addPoint(user,truesize+max(p));
+ }
+
+ void addPoint(triple user, triple truesize=(0,0,0)) {
+ bounds3.point.push(user,truesize);
+ userBox3(user,user);
+ }
+
+ void addPoint(triple user, triple truesize=(0,0,0), pen p) {
+ addPoint(user,truesize+min3(p));
+ addPoint(user,truesize+max3(p));
+ }
+
+ // Add a box to the sizing.
+ void addBox(pair userMin, pair userMax, pair trueMin=0, pair trueMax=0) {
+ bounds.addBox(userMin, userMax, trueMin, trueMax);
+ }
+
+ void addBox(triple userMin, triple userMax, triple trueMin=(0,0,0),
+ triple trueMax=(0,0,0)) {
+ bounds3.min.push(userMin,trueMin);
+ bounds3.max.push(userMax,trueMax);
+ userBox3(userMin,userMax);
+ }
+
+ // For speed reason, we unravel the addPath routines from bounds. This
+ // avoids an extra function call.
+ from bounds unravel addPath;
+
+ // Size commands <<<2
+ void size(real x, real y=x, bool keepAspect=this.keepAspect) {
+ if(!empty()) uptodate=false;
+ xsize=x;
+ ysize=y;
+ this.keepAspect=keepAspect;
+ }
+
+ void size3(real x, real y=x, real z=y, bool keepAspect=this.keepAspect) {
+ if(!empty3()) uptodate=false;
+ xsize3=x;
+ ysize3=y;
+ zsize3=z;
+ this.keepAspect=keepAspect;
+ }
+
+ void unitsize(real x, real y=x, real z=y) {
+ uptodate=false;
+ xunitsize=x;
+ yunitsize=y;
+ zunitsize=z;
+ }
+
+ // min/max of picture <<<2
+ // Calculate the min for the final frame, given the coordinate transform.
+ pair min(transform t) {
+ return bounds.min(t);
+ }
+
+ // Calculate the max for the final frame, given the coordinate transform.
+ pair max(transform t) {
+ return bounds.max(t);
+ }
+
+ // Calculate the min for the final frame, given the coordinate transform.
+ triple min(transform3 t) {
+ if(bounds3.min.x.length == 0 && bounds3.point.x.length == 0 &&
+ bounds3.max.x.length == 0) return (0,0,0);
+ triple a=t*(1,1,1)-t*(0,0,0), b=t*(0,0,0);
+ scaling xs=scaling.build(a.x,b.x);
+ scaling ys=scaling.build(a.y,b.y);
+ scaling zs=scaling.build(a.z,b.z);
+ return (min(min(min(infinity,xs,bounds3.point.x),xs,bounds3.min.x),
+ xs,bounds3.max.x),
+ min(min(min(infinity,ys,bounds3.point.y),ys,bounds3.min.y),
+ ys,bounds3.max.y),
+ min(min(min(infinity,zs,bounds3.point.z),zs,bounds3.min.z),
+ zs,bounds3.max.z));
+ }
+
+ // Calculate the max for the final frame, given the coordinate transform.
+ triple max(transform3 t) {
+ if(bounds3.min.x.length == 0 && bounds3.point.x.length == 0 &&
+ bounds3.max.x.length == 0) return (0,0,0);
+ triple a=t*(1,1,1)-t*(0,0,0), b=t*(0,0,0);
+ scaling xs=scaling.build(a.x,b.x);
+ scaling ys=scaling.build(a.y,b.y);
+ scaling zs=scaling.build(a.z,b.z);
+ return (max(max(max(-infinity,xs,bounds3.point.x),xs,bounds3.min.x),
+ xs,bounds3.max.x),
+ max(max(max(-infinity,ys,bounds3.point.y),ys,bounds3.min.y),
+ ys,bounds3.max.y),
+ max(max(max(-infinity,zs,bounds3.point.z),zs,bounds3.min.z),
+ zs,bounds3.max.z));
+ }
+
+ void append(coords3 point, coords3 min, coords3 max, transform3 t,
+ bounds3 bounds)
+ {
+ // Add the coord info to this picture.
+ if(t == identity4) {
+ point.append(bounds.point);
+ min.append(bounds.min);
+ max.append(bounds.max);
+ } else {
+ point.push(t,bounds.point,bounds.point,bounds.point);
+ // Add in all 8 corner points, to properly size cuboid pictures.
+ point.push(t,bounds.min,bounds.min,bounds.min);
+ point.push(t,bounds.min,bounds.min,bounds.max);
+ point.push(t,bounds.min,bounds.max,bounds.min);
+ point.push(t,bounds.min,bounds.max,bounds.max);
+ point.push(t,bounds.max,bounds.min,bounds.min);
+ point.push(t,bounds.max,bounds.min,bounds.max);
+ point.push(t,bounds.max,bounds.max,bounds.min);
+ point.push(t,bounds.max,bounds.max,bounds.max);
+ }
+ }
+
+ // Scaling and Fit <<<2
+ // Returns the transform for turning user-space pairs into true-space pairs.
+ transform scaling(real xsize, real ysize, bool keepAspect=true,
+ bool warn=true) {
+ bounds b = (T == identity()) ? this.bounds : T * this.bounds;
+
+ return b.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn);
+ }
+
+ transform scaling(bool warn=true) {
+ return scaling(xsize,ysize,keepAspect,warn);
+ }
+
+ // Returns the transform for turning user-space pairs into true-space triples.
+ transform3 scaling(real xsize, real ysize, real zsize, bool keepAspect=true,
+ bool warn=true) {
+ if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0
+ && zsize == 0 && zunitsize == 0)
+ return identity(4);
+
+ coords3 Coords;
+
+ append(Coords,Coords,Coords,T3,bounds3);
+
+ real sx;
+ if(xunitsize == 0) {
+ if(xsize != 0) sx=calculateScaling("x",Coords.x,xsize,warn);
+ } else sx=xunitsize;
+
+ real sy;
+ if(yunitsize == 0) {
+ if(ysize != 0) sy=calculateScaling("y",Coords.y,ysize,warn);
+ } else sy=yunitsize;
+
+ real sz;
+ if(zunitsize == 0) {
+ if(zsize != 0) sz=calculateScaling("z",Coords.z,zsize,warn);
+ } else sz=zunitsize;
+
+ if(sx == 0) {
+ sx=max(sy,sz);
+ if(sx == 0)
+ return identity(4);
+ }
+ if(sy == 0) sy=max(sz,sx);
+ if(sz == 0) sz=max(sx,sy);
+
+ if(keepAspect && (xunitsize == 0 || yunitsize == 0 || zunitsize == 0))
+ return scale3(min(sx,sy,sz));
+ else
+ return scale(sx,sy,sz);
+ }
+
+ transform3 scaling3(bool warn=true) {
+ return scaling(xsize3,ysize3,zsize3,keepAspect,warn);
+ }
+
+ frame fit(transform t, transform T0=T, pair m, pair M) {
+ frame f;
+ int n = nodes.length;
+ for(int i=0; i < n; ++i)
+ nodes[i](f,t,T0,m,M);
+ return f;
+ }
+
+ frame fit3(transform3 t, transform3 T0=T3, picture pic, projection P,
+ triple m, triple M) {
+ frame f;
+ for(int i=0; i < nodes3.length; ++i)
+ nodes3[i](f,t,T0,pic,P,m,M);
+ return f;
+ }
+
+ // Returns a rigid version of the picture using t to transform user coords
+ // into truesize coords.
+ frame fit(transform t) {
+ return fit(t,min(t),max(t));
+ }
+
+ frame fit3(transform3 t, picture pic, projection P) {
+ return fit3(t,pic,P,min(t),max(t));
+ }
+
+ // Add drawer wrappers <<<2
+ void add(void d(picture, transform), bool exact=false) {
+ add(new void(frame f, transform t) {
+ picture opic=new picture;
+ d(opic,t);
+ add(f,opic.fit(identity()));
+ },exact);
+ }
+
+ void add(void d(picture, transform3), bool exact=false, bool above=true) {
+ add(new void(frame f, transform3 t, picture pic2, projection P) {
+ picture opic=new picture;
+ d(opic,t);
+ add(f,opic.fit3(identity4,pic2,P));
+ },exact,above);
+ }
+
+ void add(void d(picture, transform3, transform3, triple, triple),
+ bool exact=false, bool above=true) {
+ add(new void(frame f, transform3 t, transform3 T, picture pic2,
+ projection P, triple lb, triple rt) {
+ picture opic=new picture;
+ d(opic,t,T,lb,rt);
+ add(f,opic.fit3(identity4,pic2,P));
+ },exact,above);
+ }
+
+ // More scaling <<<2
+ frame scaled() {
+ frame f=fit(fixedscaling);
+ pair d=size(f);
+ static real epsilon=100*realEpsilon;
+ if(d.x > xsize*(1+epsilon))
+ warning("xlimit","frame exceeds xlimit: "+(string) d.x+" > "+
+ (string) xsize);
+ if(d.y > ysize*(1+epsilon))
+ warning("ylimit","frame exceeds ylimit: "+(string) d.y+" > "+
+ (string) ysize);
+ return f;
+ }
+
+ // Calculate additional scaling required if only an approximate picture
+ // size estimate is available.
+ transform scale(frame f, real xsize=this.xsize, real ysize=this.ysize,
+ bool keepaspect=this.keepAspect) {
+ if(bounds.exact) return identity();
+ pair m=min(f);
+ pair M=max(f);
+ real width=M.x-m.x;
+ real height=M.y-m.y;
+ real xgrow=xsize == 0 || width == 0 ? 1 : xsize/width;
+ real ygrow=ysize == 0 || height == 0 ? 1 : ysize/height;
+ if(keepAspect) {
+ real[] grow;
+ if(xsize > 0) grow.push(xgrow);
+ if(ysize > 0) grow.push(ygrow);
+ return scale(grow.length == 0 ? 1 : min(grow));
+ } else return scale(xgrow,ygrow);
+
+ }
+
+ // Calculate additional scaling required if only an approximate picture
+ // size estimate is available.
+ transform3 scale3(frame f, real xsize3=this.xsize3,
+ real ysize3=this.ysize3, real zsize3=this.zsize3,
+ bool keepaspect=this.keepAspect) {
+ if(bounds3.exact) return identity(4);
+ triple m=min3(f);
+ triple M=max3(f);
+ real width=M.x-m.x;
+ real height=M.y-m.y;
+ real depth=M.z-m.z;
+ real xgrow=xsize3 == 0 || width == 0 ? 1 : xsize3/width;
+ real ygrow=ysize3 == 0 || height == 0 ? 1 : ysize3/height;
+ real zgrow=zsize3 == 0 || depth == 0 ? 1 : zsize3/depth;
+ if(keepAspect) {
+ real[] grow;
+ if(xsize3 > 0) grow.push(xgrow);
+ if(ysize3 > 0) grow.push(ygrow);
+ if(zsize3 > 0) grow.push(zgrow);
+ return scale3(grow.length == 0 ? 1 : min(grow));
+ } else return scale(xgrow,ygrow,zgrow);
+ }
+
+ // calculateTransform with scaling <<<2
+ // Return the transform that would be used to fit the picture to a frame
+ transform calculateTransform(real xsize, real ysize, bool keepAspect=true,
+ bool warn=true) {
+ transform t=scaling(xsize,ysize,keepAspect,warn);
+ return scale(fit(t),xsize,ysize,keepAspect)*t;
+ }
+
+ transform calculateTransform(bool warn=true) {
+ if(fixed) return fixedscaling;
+ return calculateTransform(xsize,ysize,keepAspect,warn);
+ }
+
+ transform3 calculateTransform3(real xsize=xsize3, real ysize=ysize3,
+ real zsize=zsize3,
+ bool keepAspect=true, bool warn=true,
+ projection P=currentprojection) {
+ transform3 t=scaling(xsize,ysize,zsize,keepAspect,warn);
+ return scale3(fit3(t,null,P),keepAspect)*t;
+ }
+
+ // min/max with xsize and ysize <<<2
+ // NOTE: These are probably very slow as implemented.
+ pair min(real xsize=this.xsize, real ysize=this.ysize,
+ bool keepAspect=this.keepAspect, bool warn=true) {
+ return min(calculateTransform(xsize,ysize,keepAspect,warn));
+ }
+
+ pair max(real xsize=this.xsize, real ysize=this.ysize,
+ bool keepAspect=this.keepAspect, bool warn=true) {
+ return max(calculateTransform(xsize,ysize,keepAspect,warn));
+ }
+
+ triple min3(real xsize=this.xsize3, real ysize=this.ysize3,
+ real zsize=this.zsize3, bool keepAspect=this.keepAspect,
+ bool warn=true, projection P) {
+ return min(calculateTransform3(xsize,ysize,zsize,keepAspect,warn,P));
+ }
+
+ triple max3(real xsize=this.xsize3, real ysize=this.ysize3,
+ real zsize=this.zsize3, bool keepAspect=this.keepAspect,
+ bool warn=true, projection P) {
+ return max(calculateTransform3(xsize,ysize,zsize,keepAspect,warn,P));
+ }
+
+ // More Fitting <<<2
+ // Returns the 2D picture fit to the requested size.
+ frame fit2(real xsize=this.xsize, real ysize=this.ysize,
+ bool keepAspect=this.keepAspect) {
+ if(fixed) return scaled();
+ if(empty2())
+ return newframe;
+
+ transform t=scaling(xsize,ysize,keepAspect);
+ frame f=fit(t);
+ transform s=scale(f,xsize,ysize,keepAspect);
+ if(s == identity()) return f;
+ return fit(s*t);
+ }
+
+ static frame fitter(string,picture,string,real,real,bool,bool,string,string,
+ light,projection);
+ frame fit(string prefix="", string format="",
+ real xsize=this.xsize, real ysize=this.ysize,
+ bool keepAspect=this.keepAspect, bool view=false,
+ string options="", string script="", light light=currentlight,
+ projection P=currentprojection) {
+ return fitter == null ? fit2(xsize,ysize,keepAspect) :
+ fitter(prefix,this,format,xsize,ysize,keepAspect,view,options,script,
+ light,P);
+ }
+
+ // Fit a 3D picture.
+ frame fit3(projection P=currentprojection) {
+ if(settings.render == 0) return fit(P);
+ if(fixed) return scaled();
+ if(empty3()) return newframe;
+ transform3 t=scaling(xsize3,ysize3,zsize3,keepAspect);
+ frame f=fit3(t,null,P);
+ transform3 s=scale3(f,xsize3,ysize3,zsize3,keepAspect);
+ if(s == identity4) return f;
+ return fit3(s*t,null,P);
+ }
+
+ // In case only an approximate picture size estimate is available, return the
+ // fitted frame slightly scaled (including labels and true size distances)
+ // so that it precisely meets the given size specification.
+ frame scale(real xsize=this.xsize, real ysize=this.ysize,
+ bool keepAspect=this.keepAspect) {
+ frame f=fit(xsize,ysize,keepAspect);
+ transform s=scale(f,xsize,ysize,keepAspect);
+ if(s == identity()) return f;
+ return s*f;
+ }
+
+ // Copying <<<2
+
+ // Copies enough information to yield the same userMin/userMax.
+ void userCopy2(picture pic) {
+ userMinx2(pic.userMin2().x);
+ userMiny2(pic.userMin2().y);
+ userMaxx2(pic.userMax2().x);
+ userMaxy2(pic.userMax2().y);
+ }
+
+ void userCopy3(picture pic) {
+ copyPairOrTriple(umin, pic.umin);
+ copyPairOrTriple(umax, pic.umax);
+ usetx=pic.usetx;
+ usety=pic.usety;
+ usetz=pic.usetz;
+ }
+
+ void userCopy(picture pic) {
+ userCopy2(pic);
+ userCopy3(pic);
+ }
+
+ // Copies the drawing information, but not the sizing information into a new
+ // picture. Fitting this picture will not scale as the original picture would.
+ picture drawcopy() {
+ picture dest=new picture;
+ dest.nodes = copy(nodes);
+ dest.nodes3=copy(nodes3);
+ dest.T=T;
+ dest.T3=T3;
+
+ // TODO: User bounds are sizing info, which probably shouldn't be part of
+ // a draw copy. Should we move this down to copy()?
+ dest.userCopy3(this);
+
+ dest.scale=scale.copy();
+ dest.legend=copy(legend);
+
+ return dest;
+ }
+
+ // A deep copy of this picture. Modifying the copied picture will not affect
+ // the original.
+ picture copy() {
+ picture dest=drawcopy();
+
+ dest.uptodate=uptodate;
+ dest.bounds=bounds.copy();
+ dest.bounds3=bounds3.copy();
+
+ dest.xsize=xsize; dest.ysize=ysize;
+ dest.xsize3=xsize; dest.ysize3=ysize3; dest.zsize3=zsize3;
+ dest.keepAspect=keepAspect;
+ dest.xunitsize=xunitsize; dest.yunitsize=yunitsize;
+ dest.zunitsize=zunitsize;
+ dest.fixed=fixed; dest.fixedscaling=fixedscaling;
+
+ return dest;
+ }
+
+ // Helper function for defining transformed pictures. Do not call it
+ // directly.
+ picture transformed(transform t) {
+ picture dest=drawcopy();
+
+ // Replace nodes with a single drawer that realizes the transform.
+ drawerBound[] oldnodes = dest.nodes;
+ void drawAll(frame f, transform tt, transform T, pair lb, pair rt) {
+ transform Tt = T*t;
+ for (var node : oldnodes)
+ node(f, tt, Tt, lb, rt);
+ }
+ dest.nodes = new drawerBound[] { drawAll };
+
+ dest.uptodate=uptodate;
+ dest.bounds=bounds.transformed(t);
+ dest.bounds3=bounds3.copy();
+
+ dest.bounds.exact=false;
+
+ dest.xsize=xsize; dest.ysize=ysize;
+ dest.xsize3=xsize; dest.ysize3=ysize3; dest.zsize3=zsize3;
+ dest.keepAspect=keepAspect;
+ dest.xunitsize=xunitsize; dest.yunitsize=yunitsize;
+ dest.zunitsize=zunitsize;
+ dest.fixed=fixed; dest.fixedscaling=fixedscaling;
+
+ return dest;
+ }
+
+ // Add Picture <<<2
+ // Add a picture to this picture, such that the user coordinates will be
+ // scaled identically when fitted
+ void add(picture src, bool group=true, filltype filltype=NoFill,
+ bool above=true) {
+ // Copy the picture. Only the drawing function closures are needed, so we
+ // only copy them. This needs to be a deep copy, as src could later have
+ // objects added to it that should not be included in this picture.
+
+ if(src == this) abort("cannot add picture to itself");
+
+ uptodate=false;
+
+ picture srcCopy=src.drawcopy();
+ // Draw by drawing the copied picture.
+ if(srcCopy.nodes.length > 0)
+ nodes.push(new void(frame f, transform t, transform T, pair m, pair M) {
+ add(f,srcCopy.fit(t,T*srcCopy.T,m,M),group,filltype,above);
+ });
+
+ if(srcCopy.nodes3.length > 0) {
+ nodes3.push(new void(frame f, transform3 t, transform3 T3, picture pic,
+ projection P, triple m, triple M) {
+ add(f,srcCopy.fit3(t,T3*srcCopy.T3,pic,P,m,M),group,above);
+ });
+ }
+
+ legend.append(src.legend);
+
+ if(src.usetx) userBoxX3(src.umin.x,src.umax.x);
+ if(src.usety) userBoxY3(src.umin.y,src.umax.y);
+ if(src.usetz) userBoxZ3(src.umin.z,src.umax.z);
+
+ bounds.append(srcCopy.T, src.bounds);
+ //append(bounds.point,bounds.min,bounds.max,srcCopy.T,src.bounds);
+ append(bounds3.point,bounds3.min,bounds3.max,srcCopy.T3,src.bounds3);
+
+ //if(!src.bounds.exact) bounds.exact=false;
+ if(!src.bounds3.exact) bounds3.exact=false;
+ }
+}
+
+// Post Struct <<<1
+picture operator * (transform t, picture orig)
+{
+ return orig.transformed(t);
+}
+
+picture operator * (transform3 t, picture orig)
+{
+ picture pic=orig.copy();
+ pic.T3=t*pic.T3;
+ triple umin=pic.userMin3(), umax=pic.userMax3();
+ pic.userCorners3(t*umin,
+ t*(umin.x,umin.y,umax.z),
+ t*(umin.x,umax.y,umin.z),
+ t*(umin.x,umax.y,umax.z),
+ t*(umax.x,umin.y,umin.z),
+ t*(umax.x,umin.y,umax.z),
+ t*(umax.x,umax.y,umin.z),
+ t*umax);
+ pic.bounds3.exact=false;
+ return pic;
+}
+
+picture currentpicture;
+
+void size(picture pic=currentpicture, real x, real y=x,
+ bool keepAspect=pic.keepAspect)
+{
+ pic.size(x,y,keepAspect);
+}
+
+void size3(picture pic=currentpicture, real x, real y=x, real z=y,
+ bool keepAspect=pic.keepAspect)
+{
+ pic.size3(x,y,z,keepAspect);
+}
+
+void unitsize(picture pic=currentpicture, real x, real y=x, real z=y)
+{
+ pic.unitsize(x,y,z);
+}
+
+void size(picture pic=currentpicture, real xsize, real ysize,
+ pair min, pair max)
+{
+ pair size=max-min;
+ pic.unitsize(size.x != 0 ? xsize/size.x : 0,
+ size.y != 0 ? ysize/size.y : 0);
+}
+
+void size(picture dest, picture src)
+{
+ dest.size(src.xsize,src.ysize,src.keepAspect);
+ dest.size3(src.xsize3,src.ysize3,src.zsize3,src.keepAspect);
+ dest.unitsize(src.xunitsize,src.yunitsize,src.zunitsize);
+}
+
+pair min(picture pic, bool user=false)
+{
+ transform t=pic.calculateTransform();
+ pair z=pic.min(t);
+ return user ? inverse(t)*z : z;
+}
+
+pair max(picture pic, bool user=false)
+{
+ transform t=pic.calculateTransform();
+ pair z=pic.max(t);
+ return user ? inverse(t)*z : z;
+}
+
+pair size(picture pic, bool user=false)
+{
+ transform t=pic.calculateTransform();
+ pair M=pic.max(t);
+ pair m=pic.min(t);
+ if(!user) return M-m;
+ t=inverse(t);
+ return t*M-t*m;
+}
+
+// Frame Alignment <<<
+pair rectify(pair dir)
+{
+ real scale=max(abs(dir.x),abs(dir.y));
+ if(scale != 0) dir *= 0.5/scale;
+ dir += (0.5,0.5);
+ return dir;
+}
+
+pair point(frame f, pair dir)
+{
+ pair m=min(f);
+ pair M=max(f);
+ return m+realmult(rectify(dir),M-m);
+}
+
+path[] align(path[] g, transform t=identity(), pair position,
+ pair align, pen p=currentpen)
+{
+ if(g.length == 0) return g;
+ pair m=min(g);
+ pair M=max(g);
+ pair dir=rectify(inverse(t)*-align);
+ if(basealign(p) == 1)
+ dir -= (0,m.y/(M.y-m.y));
+ pair a=m+realmult(dir,M-m);
+ return shift(position+align*labelmargin(p))*t*shift(-a)*g;
+}
+
+// Returns a transform for aligning frame f in the direction align
+transform shift(frame f, pair align)
+{
+ return shift(align-point(f,-align));
+}
+
+// Returns a copy of frame f aligned in the direction align
+frame align(frame f, pair align)
+{
+ return shift(f,align)*f;
+}
+// >>>
+
+pair point(picture pic=currentpicture, pair dir, bool user=true)
+{
+ pair umin = pic.userMin2();
+ pair umax = pic.userMax2();
+
+ pair z=umin+realmult(rectify(dir),umax-umin);
+ return user ? z : pic.calculateTransform()*z;
+}
+
+pair truepoint(picture pic=currentpicture, pair dir, bool user=true)
+{
+ transform t=pic.calculateTransform();
+ pair m=pic.min(t);
+ pair M=pic.max(t);
+ pair z=m+realmult(rectify(dir),M-m);
+ return user ? inverse(t)*z : z;
+}
+
+// Transform coordinate in [0,1]x[0,1] to current user coordinates.
+pair relative(picture pic=currentpicture, pair z)
+{
+ return pic.userMin2()+realmult(z,pic.userMax2()-pic.userMin2());
+}
+
+void add(picture pic=currentpicture, drawer d, bool exact=false)
+{
+ pic.add(d,exact);
+}
+
+typedef void drawer3(frame f, transform3 t, picture pic, projection P);
+void add(picture pic=currentpicture, drawer3 d, bool exact=false)
+{
+ pic.add(d,exact);
+}
+
+void add(picture pic=currentpicture, void d(picture,transform),
+ bool exact=false)
+{
+ pic.add(d,exact);
+}
+
+void add(picture pic=currentpicture, void d(picture,transform3),
+ bool exact=false)
+{
+ pic.add(d,exact);
+}
+
+void begingroup(picture pic=currentpicture)
+{
+ pic.add(new void(frame f, transform) {
+ begingroup(f);
+ },true);
+}
+
+void endgroup(picture pic=currentpicture)
+{
+ pic.add(new void(frame f, transform) {
+ endgroup(f);
+ },true);
+}
+
+void Draw(picture pic=currentpicture, path g, pen p=currentpen)
+{
+ pic.add(new void(frame f, transform t) {
+ draw(f,t*g,p);
+ },true);
+ pic.addPath(g,p);
+}
+
+// Default arguments have been removed to increase speed.
+void _draw(picture pic, path g, pen p, margin margin)
+{
+ if (size(nib(p)) == 0 && margin==NoMargin) {
+ // Inline the drawerBound wrapper for speed.
+ pic.addExactAbove(new void(frame f, transform t, transform T, pair, pair) {
+ _draw(f,t*T*g,p);
+ });
+ } else {
+ pic.add(new void(frame f, transform t) {
+ draw(f,margin(t*g,p).g,p);
+ },true);
+ }
+ pic.addPath(g,p);
+}
+
+void Draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen)
+{
+ // Could optimize this by adding one drawer.
+ for(int i=0; i < g.length; ++i) Draw(pic,g[i],p);
+}
+
+void fill(picture pic=currentpicture, path[] g, pen p=currentpen,
+ bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ pic.add(new void(frame f, transform t) {
+ fill(f,t*g,p,false);
+ },true);
+ pic.addPath(g);
+}
+
+void latticeshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[][] p, bool copy=true)
+{
+ if(copy) {
+ g=copy(g);
+ p=copy(p);
+ }
+ pic.add(new void(frame f, transform t) {
+ latticeshade(f,t*g,stroke,fillrule,p,t,false);
+ },true);
+ pic.addPath(g);
+}
+
+void axialshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen pena, pair a, pen penb, pair b, bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ pic.add(new void(frame f, transform t) {
+ axialshade(f,t*g,stroke,pena,t*a,penb,t*b,false);
+ },true);
+ pic.addPath(g);
+}
+
+void radialshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen pena, pair a, real ra, pen penb, pair b, real rb,
+ bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ pic.add(new void(frame f, transform t) {
+ pair A=t*a, B=t*b;
+ real RA=abs(t*(a+ra)-A);
+ real RB=abs(t*(b+rb)-B);
+ radialshade(f,t*g,stroke,pena,A,RA,penb,B,RB,false);
+ },true);
+ pic.addPath(g);
+}
+
+void gouraudshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[] p, pair[] z, int[] edges,
+ bool copy=true)
+{
+ if(copy) {
+ g=copy(g);
+ p=copy(p);
+ z=copy(z);
+ edges=copy(edges);
+ }
+ pic.add(new void(frame f, transform t) {
+ gouraudshade(f,t*g,stroke,fillrule,p,t*z,edges,false);
+ },true);
+ pic.addPath(g);
+}
+
+void gouraudshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[] p, int[] edges, bool copy=true)
+{
+ if(copy) {
+ g=copy(g);
+ p=copy(p);
+ edges=copy(edges);
+ }
+ pic.add(new void(frame f, transform t) {
+ gouraudshade(f,t*g,stroke,fillrule,p,edges,false);
+ },true);
+ pic.addPath(g);
+}
+
+void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[][] p, path[] b=g,
+ pair[][] z=new pair[][], bool copy=true)
+{
+ if(copy) {
+ g=copy(g);
+ p=copy(p);
+ b=copy(b);
+ z=copy(z);
+ }
+ pic.add(new void(frame f, transform t) {
+ pair[][] Z=new pair[z.length][];
+ for(int i=0; i < z.length; ++i)
+ Z[i]=t*z[i];
+ tensorshade(f,t*g,stroke,fillrule,p,t*b,Z,false);
+ },true);
+ pic.addPath(g);
+}
+
+void tensorshade(frame f, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[] p,
+ path b=g.length > 0 ? g[0] : nullpath)
+{
+ tensorshade(f,g,stroke,fillrule,new pen[][] {p},b);
+}
+
+void tensorshade(frame f, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[] p,
+ path b=g.length > 0 ? g[0] : nullpath, pair[] z)
+{
+ tensorshade(f,g,stroke,fillrule,new pen[][] {p},b,new pair[][] {z});
+}
+
+void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[] p,
+ path b=g.length > 0 ? g[0] : nullpath)
+{
+ tensorshade(pic,g,stroke,fillrule,new pen[][] {p},b);
+}
+
+void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, pen[] p,
+ path b=g.length > 0 ? g[0] : nullpath, pair[] z)
+{
+ tensorshade(pic,g,stroke,fillrule,new pen[][] {p},b,new pair[][] {z});
+}
+
+// Smoothly shade the regions between consecutive paths of a sequence using a
+// given array of pens:
+void draw(picture pic=currentpicture, path[] g, pen fillrule=currentpen,
+ pen[] p)
+{
+ path[] G;
+ pen[][] P;
+ string differentlengths="arrays have different lengths";
+ if(g.length != p.length) abort(differentlengths);
+ for(int i=0; i < g.length-1; ++i) {
+ path g0=g[i];
+ path g1=g[i+1];
+ if(length(g0) != length(g1)) abort(differentlengths);
+ for(int j=0; j < length(g0); ++j) {
+ G.push(subpath(g0,j,j+1)--reverse(subpath(g1,j,j+1))--cycle);
+ P.push(new pen[] {p[i],p[i],p[i+1],p[i+1]});
+ }
+ }
+ tensorshade(pic,G,fillrule,P);
+}
+
+void functionshade(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, string shader, bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ pic.add(new void(frame f, transform t) {
+ functionshade(f,t*g,stroke,fillrule,shader);
+ },true);
+ pic.addPath(g);
+}
+
+void filldraw(picture pic=currentpicture, path[] g, pen fillpen=currentpen,
+ pen drawpen=currentpen)
+{
+ begingroup(pic);
+ fill(pic,g,fillpen);
+ Draw(pic,g,drawpen);
+ endgroup(pic);
+}
+
+void clip(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ //pic.userClip(min(g),max(g));
+ pic.clip(min(g), max(g),
+ new void(frame f, transform t) {
+ clip(f,t*g,stroke,fillrule,false);
+ },
+ true);
+}
+
+void beginclip(picture pic=currentpicture, path[] g, bool stroke=false,
+ pen fillrule=currentpen, bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+
+ pic.clipmin.push(min(g));
+ pic.clipmax.push(max(g));
+
+ pic.add(new void(frame f, transform t) {
+ beginclip(f,t*g,stroke,fillrule,false);
+ },true);
+}
+
+void endclip(picture pic=currentpicture)
+{
+ pair min,max;
+ if (pic.clipmin.length > 0 && pic.clipmax.length > 0)
+ {
+ min = pic.clipmin.pop();
+ max = pic.clipmax.pop();
+ }
+ else
+ {
+ // We should probably abort here, since the PostScript output will be
+ // garbage.
+ warning("endclip", "endclip without beginclip");
+ min = pic.userMin2();
+ max = pic.userMax2();
+ }
+
+ pic.clip(min, max,
+ new void(frame f, transform) {
+ endclip(f);
+ },
+ true);
+}
+
+void unfill(picture pic=currentpicture, path[] g, bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ pic.add(new void(frame f, transform t) {
+ unfill(f,t*g,false);
+ },true);
+}
+
+void filloutside(picture pic=currentpicture, path[] g, pen p=currentpen,
+ bool copy=true)
+{
+ if(copy)
+ g=copy(g);
+ pic.add(new void(frame f, transform t) {
+ filloutside(f,t*g,p,false);
+ },true);
+ pic.addPath(g);
+}
+
+// Use a fixed scaling to map user coordinates in box(min,max) to the
+// desired picture size.
+transform fixedscaling(picture pic=currentpicture, pair min, pair max,
+ pen p=nullpen, bool warn=false)
+{
+ Draw(pic,min,p+invisible);
+ Draw(pic,max,p+invisible);
+ pic.fixed=true;
+ return pic.fixedscaling=pic.calculateTransform(pic.xsize,pic.ysize,
+ pic.keepAspect);
+}
+
+// Add frame src to frame dest about position with optional grouping.
+void add(frame dest, frame src, pair position, bool group=false,
+ filltype filltype=NoFill, bool above=true)
+{
+ add(dest,shift(position)*src,group,filltype,above);
+}
+
+// Add frame src to picture dest about position with optional grouping.
+void add(picture dest=currentpicture, frame src, pair position=0,
+ bool group=true, filltype filltype=NoFill, bool above=true)
+{
+ if(is3D(src)) {
+ dest.add(new void(frame f, transform3, picture, projection) {
+ add(f,src); // always add about 3D origin (ignore position)
+ },true);
+ dest.addBox((0,0,0),(0,0,0),min3(src),max3(src));
+ } else {
+ dest.add(new void(frame f, transform t) {
+ add(f,shift(t*position)*src,group,filltype,above);
+ },true);
+ dest.addBox(position,position,min(src),max(src));
+ }
+}
+
+// Like add(picture,frame,pair) but extend picture to accommodate frame.
+void attach(picture dest=currentpicture, frame src, pair position=0,
+ bool group=true, filltype filltype=NoFill, bool above=true)
+{
+ transform t=dest.calculateTransform();
+ add(dest,src,position,group,filltype,above);
+ pair s=size(dest.fit(t));
+ size(dest,dest.xsize != 0 ? s.x : 0,dest.ysize != 0 ? s.y : 0);
+}
+
+// Like add(picture,frame,pair) but align frame in direction align.
+void add(picture dest=currentpicture, frame src, pair position, pair align,
+ bool group=true, filltype filltype=NoFill, bool above=true)
+{
+ add(dest,align(src,align),position,group,filltype,above);
+}
+
+// Like add(frame,frame,pair) but align frame in direction align.
+void add(frame dest, frame src, pair position, pair align,
+ bool group=true, filltype filltype=NoFill, bool above=true)
+{
+ add(dest,align(src,align),position,group,filltype,above);
+}
+
+// Like add(picture,frame,pair,pair) but extend picture to accommodate frame;
+void attach(picture dest=currentpicture, frame src, pair position,
+ pair align, bool group=true, filltype filltype=NoFill,
+ bool above=true)
+{
+ attach(dest,align(src,align),position,group,filltype,above);
+}
+
+// Add a picture to another such that user coordinates in both will be scaled
+// identically in the shipout.
+void add(picture dest, picture src, bool group=true, filltype filltype=NoFill,
+ bool above=true)
+{
+ dest.add(src,group,filltype,above);
+}
+
+void add(picture src, bool group=true, filltype filltype=NoFill,
+ bool above=true)
+{
+ currentpicture.add(src,group,filltype,above);
+}
+
+// Fit the picture src using the identity transformation (so user
+// coordinates and truesize coordinates agree) and add it about the point
+// position to picture dest.
+void add(picture dest, picture src, pair position, bool group=true,
+ filltype filltype=NoFill, bool above=true)
+{
+ add(dest,src.fit(identity()),position,group,filltype,above);
+}
+
+void add(picture src, pair position, bool group=true, filltype filltype=NoFill,
+ bool above=true)
+{
+ add(currentpicture,src,position,group,filltype,above);
+}
+
+// Fill a region about the user-coordinate 'origin'.
+void fill(pair origin, picture pic=currentpicture, path[] g, pen p=currentpen)
+{
+ picture opic;
+ fill(opic,g,p);
+ add(pic,opic,origin);
+}
+
+void postscript(picture pic=currentpicture, string s)
+{
+ pic.add(new void(frame f, transform) {
+ postscript(f,s);
+ },true);
+}
+
+void postscript(picture pic=currentpicture, string s, pair min, pair max)
+{
+ pic.add(new void(frame f, transform t) {
+ postscript(f,s,t*min,t*max);
+ },true);
+}
+
+void tex(picture pic=currentpicture, string s)
+{
+ // Force TeX string s to be evaluated immediately (in case it is a macro).
+ frame g;
+ tex(g,s);
+ size(g);
+ pic.add(new void(frame f, transform) {
+ tex(f,s);
+ },true);
+}
+
+void tex(picture pic=currentpicture, string s, pair min, pair max)
+{
+ frame g;
+ tex(g,s);
+ size(g);
+ pic.add(new void(frame f, transform t) {
+ tex(f,s,t*min,t*max);
+ },true);
+}
+
+void layer(picture pic=currentpicture)
+{
+ pic.add(new void(frame f, transform) {
+ layer(f);
+ },true);
+}
+
+void erase(picture pic=currentpicture)
+{
+ pic.uptodate=false;
+ pic.erase();
+}
+
+void begin(picture pic=currentpicture, string name, string id="",
+ bool visible=true)
+{
+ if(!latex() || !pdf()) return;
+ settings.twice=true;
+ if(id == "") id=string(++ocgindex);
+ tex(pic,"\begin{ocg}{"+name+"}{"+id+"}{"+(visible ? "1" : "0")+"}");
+ layer(pic);
+}
+
+void end(picture pic=currentpicture)
+{
+ if(!latex() || !pdf()) return;
+ tex(pic,"\end{ocg}");
+ layer(pic);
+}
+
+// For users of the LaTeX babel package.
+void deactivatequote(picture pic=currentpicture)
+{
+ tex(pic,"\catcode`\"=12");
+}
+
+void activatequote(picture pic=currentpicture)
+{
+ tex(pic,"\catcode`\"=13");
+}
diff --git a/Master/texmf-dist/asymptote/plain_prethree.asy b/Master/texmf-dist/asymptote/plain_prethree.asy
new file mode 100644
index 00000000000..595bac2fad4
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_prethree.asy
@@ -0,0 +1,224 @@
+// Critical definitions for transform3 needed by projection and picture.
+
+typedef real[][] transform3;
+restricted transform3 identity4=identity(4);
+
+// A uniform 3D scaling.
+transform3 scale3(real s)
+{
+ transform3 t=identity(4);
+ t[0][0]=t[1][1]=t[2][2]=s;
+ return t;
+}
+
+// Simultaneous 3D scalings in the x, y, and z directions.
+transform3 scale(real x, real y, real z)
+{
+ transform3 t=identity(4);
+ t[0][0]=x;
+ t[1][1]=y;
+ t[2][2]=z;
+ return t;
+}
+
+transform3 shiftless(transform3 t)
+{
+ transform3 T=copy(t);
+ T[0][3]=T[1][3]=T[2][3]=0;
+ return T;
+}
+
+real camerafactor=2; // Factor used for camera adjustment.
+
+struct transformation {
+ transform3 modelview; // For orientation and positioning
+ transform3 projection; // For 3D to 2D projection
+ bool infinity;
+ void operator init(transform3 modelview) {
+ this.modelview=modelview;
+ this.projection=identity4;
+ infinity=true;
+ }
+ void operator init(transform3 modelview, transform3 projection) {
+ this.modelview=modelview;
+ this.projection=projection;
+ infinity=false;
+ }
+ transform3 compute() {
+ return infinity ? modelview : projection*modelview;
+ }
+ transformation copy() {
+ transformation T=new transformation;
+ T.modelview=copy(modelview);
+ T.projection=copy(projection);
+ T.infinity=infinity;
+ return T;
+ }
+}
+
+struct projection {
+ transform3 t; // projection*modelview (cached)
+ bool infinity;
+ bool absolute=false;
+ triple camera; // Position of camera.
+ triple up; // A vector that should be projected to direction (0,1).
+ triple target; // Point where camera is looking at.
+ triple normal; // Normal vector from target to projection plane.
+ pair viewportshift; // Fractional viewport shift.
+ real zoom=1; // Zoom factor.
+ real angle; // Lens angle (for perspective projection).
+ bool showtarget=true; // Expand bounding volume to include target?
+ typedef transformation projector(triple camera, triple up, triple target);
+ projector projector;
+ bool autoadjust=true; // Adjust camera to lie outside bounding volume?
+ bool center=false; // Center target within bounding volume?
+ int ninterpolate; // Used for projecting nurbs to 2D Bezier curves.
+ bool bboxonly=true; // Typeset label bounding box only.
+
+ transformation T;
+
+ void calculate() {
+ T=projector(camera,up,target);
+ t=T.compute();
+ infinity=T.infinity;
+ ninterpolate=infinity ? 1 : 16;
+ }
+
+ triple vector() {
+ return camera-target;
+ }
+
+ void operator init(triple camera, triple up=(0,0,1), triple target=(0,0,0),
+ triple normal=camera-target,
+ real zoom=1, real angle=0, pair viewportshift=0,
+ bool showtarget=true, bool autoadjust=true,
+ bool center=false, projector projector) {
+ this.camera=camera;
+ this.up=up;
+ this.target=target;
+ this.normal=normal;
+ this.zoom=zoom;
+ this.angle=angle;
+ this.viewportshift=viewportshift;
+ this.showtarget=showtarget;
+ this.autoadjust=autoadjust;
+ this.center=center;
+ this.projector=projector;
+ calculate();
+ }
+
+ projection copy() {
+ projection P=new projection;
+ P.t=t;
+ P.infinity=infinity;
+ P.absolute=absolute;
+ P.camera=camera;
+ P.up=up;
+ P.target=target;
+ P.normal=normal;
+ P.zoom=zoom;
+ P.angle=angle;
+ P.viewportshift=viewportshift;
+ P.showtarget=showtarget;
+ P.autoadjust=autoadjust;
+ P.center=center;
+ P.projector=projector;
+ P.ninterpolate=ninterpolate;
+ P.bboxonly=bboxonly;
+ P.T=T.copy();
+ return P;
+ }
+
+ // Return the maximum distance of box(m,M) from target.
+ real distance(triple m, triple M) {
+ triple[] c={m,(m.x,m.y,M.z),(m.x,M.y,m.z),(m.x,M.y,M.z),
+ (M.x,m.y,m.z),(M.x,m.y,M.z),(M.x,M.y,m.z),M};
+ return max(abs(c-target));
+ }
+
+
+ // This is redefined here to make projection as self-contained as possible.
+ static private real sqrtEpsilon = sqrt(realEpsilon);
+
+ // Move the camera so that the box(m,M) rotated about target will always
+ // lie in front of the clipping plane.
+ bool adjust(triple m, triple M) {
+ triple v=camera-target;
+ real d=distance(m,M);
+ static real lambda=camerafactor*(1-sqrtEpsilon);
+ if(lambda*d >= abs(v)) {
+ camera=target+camerafactor*d*unit(v);
+ calculate();
+ return true;
+ }
+ return false;
+ }
+}
+
+projection currentprojection;
+
+struct light {
+ real[][] diffuse;
+ real[][] ambient;
+ real[][] specular;
+ pen background=nullpen; // Background color of the 3D canvas.
+ real specularfactor;
+ bool viewport; // Are the lights specified (and fixed) in the viewport frame?
+ triple[] position; // Only directional lights are currently implemented.
+
+ transform3 T=identity(4); // Transform to apply to normal vectors.
+
+ bool on() {return position.length > 0;}
+
+ void operator init(pen[] diffuse,
+ pen[] ambient=array(diffuse.length,black),
+ pen[] specular=diffuse, pen background=nullpen,
+ real specularfactor=1,
+ bool viewport=false, triple[] position) {
+ int n=diffuse.length;
+ assert(ambient.length == n && specular.length == n && position.length == n);
+
+ this.diffuse=new real[n][];
+ this.ambient=new real[n][];
+ this.specular=new real[n][];
+ this.background=background;
+ this.position=new triple[n];
+ for(int i=0; i < position.length; ++i) {
+ this.diffuse[i]=rgba(diffuse[i]);
+ this.ambient[i]=rgba(ambient[i]);
+ this.specular[i]=rgba(specular[i]);
+ this.position[i]=unit(position[i]);
+ }
+ this.specularfactor=specularfactor;
+ this.viewport=viewport;
+ }
+
+ void operator init(pen diffuse=white, pen ambient=black, pen specular=diffuse,
+ pen background=nullpen, real specularfactor=1,
+ bool viewport=false...triple[] position) {
+ int n=position.length;
+ operator init(array(n,diffuse),array(n,ambient),array(n,specular),
+ background,specularfactor,viewport,position);
+ }
+
+ void operator init(pen diffuse=white, pen ambient=black, pen specular=diffuse,
+ pen background=nullpen, bool viewport=false,
+ real x, real y, real z) {
+ operator init(diffuse,ambient,specular,background,viewport,(x,y,z));
+ }
+
+ void operator init(explicit light light) {
+ diffuse=copy(light.diffuse);
+ ambient=copy(light.ambient);
+ specular=copy(light.specular);
+ background=light.background;
+ specularfactor=light.specularfactor;
+ viewport=light.viewport;
+ position=copy(light.position);
+ }
+
+ real[] background() {return rgba(background == nullpen ? white : background);}
+}
+
+light currentlight;
+
diff --git a/Master/texmf-dist/asymptote/plain_scaling.asy b/Master/texmf-dist/asymptote/plain_scaling.asy
new file mode 100644
index 00000000000..fd2dbae07c0
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_scaling.asy
@@ -0,0 +1,246 @@
+real expansionfactor=sqrt(2);
+
+// A coordinate in "flex space." A linear combination of user and true-size
+// coordinates.
+struct coord {
+ real user,truesize;
+
+ // Build a coord.
+ static coord build(real user, real truesize) {
+ coord c=new coord;
+ c.user=user;
+ c.truesize=truesize;
+ return c;
+ }
+
+ // Deep copy of coordinate. Users may add coords to the picture, but then
+ // modify the struct. To prevent this from yielding unexpected results, deep
+ // copying is used.
+ coord copy() {
+ return build(user, truesize);
+ }
+
+ void clip(real min, real max) {
+ user=min(max(user,min),max);
+ truesize=0;
+ }
+}
+
+bool operator <= (coord a, coord b)
+{
+ return a.user <= b.user && a.truesize <= b.truesize;
+}
+
+bool operator >= (coord a, coord b)
+{
+ return a.user >= b.user && a.truesize >= b.truesize;
+}
+
+// Find the maximal elements of the input array, using the partial ordering
+// given.
+coord[] maxcoords(coord[] in, bool operator <= (coord,coord))
+{
+ // As operator <= is defined in the parameter list, it has a special
+ // meaning in the body of the function.
+
+ coord best;
+ coord[] c;
+
+ int n=in.length;
+
+ if(n == 0)
+ return c;
+
+ int first=0;
+ // Add the first coord without checking restrictions (as there are none).
+ best=in[first];
+ c.push(best);
+
+ static int NONE=-1;
+
+ int dominator(coord x)
+ {
+ // This assumes it has already been checked against the best.
+ for(int i=1; i < c.length; ++i)
+ if(x <= c[i])
+ return i;
+ return NONE;
+ }
+
+ void promote(int i)
+ {
+ // Swap with the top
+ coord x=c[i];
+ c[i]=best;
+ best=c[0]=x;
+ }
+
+ void addmaximal(coord x)
+ {
+ coord[] newc;
+
+ // Check if it beats any others.
+ for(int i=0; i < c.length; ++i) {
+ coord y=c[i];
+ if(!(y <= x))
+ newc.push(y);
+ }
+ newc.push(x);
+ c=newc;
+ best=c[0];
+ }
+
+ void add(coord x)
+ {
+ if(x <= best)
+ return;
+ else {
+ int i=dominator(x);
+ if(i == NONE)
+ addmaximal(x);
+ else
+ promote(i);
+ }
+ }
+
+ for(int i=1; i < n; ++i)
+ add(in[i]);
+
+ return c;
+}
+
+struct coords2 {
+ coord[] x,y;
+ void erase() {
+ x.delete();
+ y.delete();
+ }
+ // Only a shallow copy of the individual elements of x and y
+ // is needed since, once entered, they are never modified.
+ coords2 copy() {
+ coords2 c=new coords2;
+ c.x=copy(x);
+ c.y=copy(y);
+ return c;
+ }
+ void append(coords2 c) {
+ x.append(c.x);
+ y.append(c.y);
+ }
+ void push(pair user, pair truesize) {
+ x.push(coord.build(user.x,truesize.x));
+ y.push(coord.build(user.y,truesize.y));
+ }
+ void push(coord cx, coord cy) {
+ x.push(cx);
+ y.push(cy);
+ }
+ void push(transform t, coords2 c1, coords2 c2) {
+ for(int i=0; i < c1.x.length; ++i) {
+ coord cx=c1.x[i], cy=c2.y[i];
+ pair tinf=shiftless(t)*(0,0);
+ pair z=t*(cx.user,cy.user);
+ pair w=(cx.truesize,cy.truesize);
+ w=length(w)*unit(shiftless(t)*w);
+ coord Cx,Cy;
+ Cx.user=z.x;
+ Cy.user=z.y;
+ Cx.truesize=w.x;
+ Cy.truesize=w.y;
+ push(Cx,Cy);
+ }
+ }
+ void xclip(real min, real max) {
+ for(int i=0; i < x.length; ++i)
+ x[i].clip(min,max);
+ }
+ void yclip(real min, real max) {
+ for(int i=0; i < y.length; ++i)
+ y[i].clip(min,max);
+ }
+}
+
+// The scaling in one dimension: x --> a*x + b
+struct scaling {
+ real a,b;
+ static scaling build(real a, real b) {
+ scaling s=new scaling;
+ s.a=a; s.b=b;
+ return s;
+ }
+ real scale(real x) {
+ return a*x+b;
+ }
+ real scale(coord c) {
+ return scale(c.user) + c.truesize;
+ }
+}
+
+// Calculate the minimum point in scaling the coords.
+real min(real m, scaling s, coord[] c) {
+ for(int i=0; i < c.length; ++i)
+ if(s.scale(c[i]) < m)
+ m=s.scale(c[i]);
+ return m;
+}
+
+// Calculate the maximum point in scaling the coords.
+real max(real M, scaling s, coord[] c) {
+ for(int i=0; i < c.length; ++i)
+ if(s.scale(c[i]) > M)
+ M=s.scale(c[i]);
+ return M;
+}
+
+// Calculate the sizing constants for the given array and maximum size.
+real calculateScaling(string dir, coord[] m, coord[] M, real size,
+ bool warn=true) {
+ access simplex;
+ simplex.problem p=new simplex.problem;
+
+ void addMinCoord(coord c) {
+ // (a*user + b) + truesize >= 0:
+ p.addRestriction(c.user,1,c.truesize);
+ }
+ void addMaxCoord(coord c) {
+ // (a*user + b) + truesize <= size:
+ p.addRestriction(-c.user,-1,size-c.truesize);
+ }
+
+ for (int i=0; i < m.length; ++i)
+ addMinCoord(m[i]);
+ for (int i=0; i < M.length; ++i)
+ addMaxCoord(M[i]);
+
+ int status=p.optimize();
+ if(status == simplex.problem.OPTIMAL) {
+ // TODO: Could just be return a;
+ return scaling.build(p.a(),p.b()).a;
+ } else if(status == simplex.problem.UNBOUNDED) {
+ if(warn) warning("unbounded",dir+" scaling in picture unbounded");
+ return 0;
+ } else {
+ if(!warn) return 1;
+
+ bool userzero(coord[] coords) {
+ for(var coord : coords)
+ if(coord.user != 0) return false;
+ return true;
+ }
+
+ if((userzero(m) && userzero(M)) || size >= infinity) return 1;
+
+ warning("cannotfit","cannot fit picture to "+dir+"size "+(string) size
+ +"...enlarging...");
+
+ return calculateScaling(dir,m,M,expansionfactor*size,warn);
+ }
+}
+
+real calculateScaling(string dir, coord[] coords, real size, bool warn=true)
+{
+ coord[] m=maxcoords(coords,operator >=);
+ coord[] M=maxcoords(coords,operator <=);
+
+ return calculateScaling(dir, m, M, size, warn);
+}
diff --git a/Master/texmf-dist/asymptote/plain_shipout.asy b/Master/texmf-dist/asymptote/plain_shipout.asy
new file mode 100644
index 00000000000..ccf94e6c9fd
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_shipout.asy
@@ -0,0 +1,123 @@
+// Default file prefix used for inline LaTeX mode
+string defaultfilename;
+
+string[] file3;
+
+string outprefix(string prefix=defaultfilename) {
+ return stripextension(prefix != "" ? prefix : outname());
+}
+
+string outformat(string format="")
+{
+ if(format == "") format=settings.outformat;
+ if(format == "") format=nativeformat();
+ return format;
+}
+
+bool shipped; // Was a picture or frame already shipped out?
+
+frame currentpatterns;
+
+frame Portrait(frame f) {return f;};
+frame Landscape(frame f) {return rotate(90)*f;};
+frame UpsideDown(frame f) {return rotate(180)*f;};
+frame Seascape(frame f) {return rotate(-90)*f;};
+typedef frame orientation(frame);
+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="");
+
+bool prc0(string format="")
+{
+ return settings.prc && (outformat(format) == "pdf" || settings.inlineimage);
+}
+
+bool prc(string format="") {
+ return prc0(format) && Embed != null;
+}
+
+bool is3D(string format="")
+{
+ return prc(format) || settings.render != 0;
+}
+
+frame enclose(string prefix=defaultfilename, object F, string format="")
+{
+ if(prc(format)) {
+ frame f;
+ label(f,F.L);
+ return f;
+ } return F.f;
+}
+
+include plain_xasy;
+
+void shipout(string prefix=defaultfilename, frame f,
+ string format="", bool wait=false, bool view=true,
+ string options="", string script="",
+ light light=currentlight, projection P=currentprojection)
+{
+ if(is3D(f)) {
+ f=enclose(prefix,embed3(prefix,f,format,options,script,light,P));
+ if(settings.render != 0 && !prc(format)) {
+ shipped=true;
+ return;
+ }
+ }
+
+ if(inXasyMode) {
+ erase();
+ add(f,group=false);
+ return;
+ }
+
+ // Applications like LaTeX cannot handle large PostScript coordinates.
+ pair m=min(f);
+ int limit=2000;
+ if(abs(m.x) > limit || abs(m.y) > limit) f=shift(-m)*f;
+
+ shipout(prefix,f,currentpatterns,format,wait,view,
+ xformStack.empty() ? null : xformStack.pop0);
+ shipped=true;
+}
+
+void shipout(string prefix=defaultfilename, picture pic=currentpicture,
+ orientation orientation=orientation,
+ string format="", bool wait=false, bool view=true,
+ string options="", string script="",
+ light light=currentlight, projection P=currentprojection)
+{
+ if(!uptodate()) {
+ bool inlinetex=settings.inlinetex;
+ bool prc=prc(format);
+ bool empty3=pic.empty3();
+ if(prc && !empty3) {
+ if(settings.render == 0) {
+ string image=outprefix(prefix)+"+"+(string) file3.length;
+ if(settings.inlineimage) image += "_0";
+ settings.inlinetex=false;
+ settings.prc=false;
+ shipout(image,pic,orientation,nativeformat(),view=false,light,P);
+ settings.prc=true;
+ }
+ settings.inlinetex=settings.inlineimage;
+ }
+ frame f=pic.fit(prefix,format,view=view,options,script,light,P);
+ if(!pic.empty2() || settings.render == 0 || prc || empty3)
+ shipout(prefix,orientation(f),format,wait,view);
+ settings.inlinetex=inlinetex;
+ }
+
+ pic.uptodate=true;
+ shipped=true;
+}
+
+void newpage(picture pic=currentpicture)
+{
+ pic.add(new void(frame f, transform) {
+ newpage(f);
+ },true);
+}
diff --git a/Master/texmf-dist/asymptote/plain_strings.asy b/Master/texmf-dist/asymptote/plain_strings.asy
new file mode 100644
index 00000000000..a466159d9c7
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_strings.asy
@@ -0,0 +1,227 @@
+string defaultformat(int n, string trailingzero="", bool fixed=false,
+ bool signed=true)
+{
+ return "$%"+trailingzero+"."+string(n)+(fixed ? "f" : "g")+"$";
+}
+
+string defaultformat=defaultformat(4);
+string defaultseparator="\!\times\!";
+
+string ask(string prompt)
+{
+ write(stdout,prompt);
+ return stdin;
+}
+
+string getstring(string name="", string default="", string prompt="",
+ bool store=true)
+{
+ string[] history=history(name,1);
+ if(history.length > 0) default=history[0];
+ if(prompt == "") prompt=name+"? [%s] ";
+ prompt=replace(prompt,new string[][] {{"%s",default}});
+ string s=readline(prompt,name);
+ if(s == "") s=default;
+ else saveline(name,s,store);
+ return s;
+}
+
+int getint(string name="", int default=0, string prompt="", bool store=true)
+{
+ return (int) getstring(name,(string) default,prompt,store);
+}
+
+real getreal(string name="", real default=0, string prompt="", bool store=true)
+{
+ return (real) getstring(name,(string) default,prompt,store);
+}
+
+pair getpair(string name="", pair default=0, string prompt="", bool store=true)
+{
+ return (pair) getstring(name,(string) default,prompt,store);
+}
+
+triple gettriple(string name="", triple default=(0,0,0), string prompt="",
+ bool store=true)
+{
+ return (triple) getstring(name,(string) default,prompt,store);
+}
+
+// returns a string with all occurrences of string 'before' in string 's'
+// changed to string 'after'.
+string replace(string s, string before, string after)
+{
+ return replace(s,new string[][] {{before,after}});
+}
+
+// Like texify but don't convert embedded TeX commands: \${}
+string TeXify(string s)
+{
+ static string[][] t={{"&","\&"},{"%","\%"},{"_","\_"},{"#","\#"},{"<","$<$"},
+ {">","$>$"},{"|","$|$"},{"^","$\hat{\ }$"},
+ {"~","$\tilde{\ }$"},{" ","\phantom{ }"}};
+ return replace(s,t);
+}
+
+private string[][] trans1={{'\\',"\backslash "},
+ {"$","\$"},{"{","\{"},{"}","\}"}};
+private string[][] trans2={{"\backslash ","$\backslash$"}};
+
+// Convert string to TeX
+string texify(string s)
+{
+ return TeXify(replace(replace(s,trans1),trans2));
+}
+
+// Convert string to TeX, preserving newlines
+string verbatim(string s)
+{
+ bool space=substr(s,0,1) == '\n';
+ static string[][] t={{'\n',"\\"}};
+ t.append(trans1);
+ s=TeXify(replace(replace(s,t),trans2));
+ return space ? "\ "+s : s;
+}
+
+// Split a string into an array of substrings delimited by delimiter
+// If delimiter is an empty string, use space delimiter, discarding duplicates.
+string[] split(string s, string delimiter="")
+{
+ bool prune=false;
+ if(delimiter == "") {
+ prune=true;
+ delimiter=" ";
+ }
+
+ string[] S;
+ int last=0;
+ int i;
+ int N=length(delimiter);
+ int n=length(s);
+ while((i=find(s,delimiter,last)) >= 0) {
+ if(i >= last)
+ 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));
+ return S;
+}
+
+int system(string s)
+{
+ return system(split(s));
+}
+
+int[] operator ecast(string[] a)
+{
+ return sequence(new int(int i) {return (int) a[i];},a.length);
+}
+
+real[] operator ecast(string[] a)
+{
+ return sequence(new real(int i) {return (real) a[i];},a.length);
+}
+
+// Read contents of file as a string.
+string file(string s)
+{
+ file f=input(s);
+ string s;
+ while(!eof(f)) {
+ s += f+'\n';
+ }
+ return s;
+}
+
+string italic(string s)
+{
+ return s != "" ? "{\it "+s+"}" : s;
+}
+
+string baseline(string s, string template="\strut")
+{
+ return s != "" ? "\vphantom{"+template+"}"+s : s;
+}
+
+string math(string s)
+{
+ return s != "" ? "$"+s+"$" : s;
+}
+
+private void notimplemented(string text)
+{
+ abort(text+" is not implemented for the '"+settings.tex+"' TeX engine");
+}
+
+string jobname(string name)
+{
+ int pos=rfind(name,"-");
+ return pos >= 0 ? "\ASYprefix\jobname"+substr(name,pos) : name;
+}
+
+string graphic(string name, string options="")
+{
+ if(latex()) {
+ if(options != "") options="["+options+"]";
+ bool pdf=pdf();
+ string includegraphics="\includegraphics"+options;
+ if(settings.inlinetex)
+ return includegraphics+"{"+jobname(name)+"}";
+ else
+ return includegraphics+
+ (find(name," ") < 0 ? "{"+name+"}" :
+ (pdf ? "{\""+stripextension(name)+"\".pdf}" : "{\""+name+"\"}"));
+ }
+ if(settings.tex != "context")
+ notimplemented("graphic");
+ return "\externalfigure["+name+"]["+options+"]";
+}
+
+string minipage(string s, real width=100bp)
+{
+ if(latex())
+ return "\begin{minipage}{"+(string) (width/pt)+"pt}"+s+"\end{minipage}";
+ if(settings.tex != "context")
+ notimplemented("minipage");
+ return "\startframedtext[none][frame=off,width="+(string) (width/pt)+
+ "pt]"+s+"\stopframedtext";
+}
+
+void usepackage(string s, string options="")
+{
+ if(!latex()) notimplemented("usepackage");
+ string usepackage="\usepackage";
+ if(options != "") usepackage += "["+options+"]";
+ texpreamble(usepackage+"{"+s+"}");
+}
+
+void pause(string w="Hit enter to continue")
+{
+ write(w);
+ w=stdin;
+}
+
+string math(real x)
+{
+ return math((string) x);
+}
+
+string format(string format, real x, string locale="")
+{
+ return format(format,defaultseparator,x,locale);
+}
+
+string format(real x, string locale="")
+{
+ return format(defaultformat,defaultseparator,x,locale);
+}
+
+string phantom(string s)
+{
+ return "\phantom{"+s+"}";
+}
+
+restricted int ocgindex=0;
diff --git a/Master/texmf-dist/asymptote/plain_xasy.asy b/Master/texmf-dist/asymptote/plain_xasy.asy
new file mode 100644
index 00000000000..efc739d8385
--- /dev/null
+++ b/Master/texmf-dist/asymptote/plain_xasy.asy
@@ -0,0 +1,132 @@
+restricted bool inXasyMode=false;
+bool diagnostics=false;
+void report(string text)
+{
+ if(diagnostics)
+ write(text);
+}
+void report(transform t)
+{
+ if(diagnostics)
+ write(t);
+}
+void report(int i)
+{
+ if(diagnostics)
+ write(i);
+}
+void initXasyMode()
+{
+ size(0,0);
+ inXasyMode=true;
+}
+
+void exitXasyMode()
+{
+ inXasyMode=false;
+}
+private picture[] tempStore;
+private picture newPic;
+void startScript()
+{
+ tempStore.push(currentpicture.copy());
+ newPic=new picture;
+ currentpicture=newPic;
+}
+
+void endScript()
+{
+ if(tempStore.length < 1) {
+ abort("endScript() without matching beginScript()");
+ } else {
+ currentpicture=tempStore.pop();
+ add(currentpicture,newPic.fit(),group=false);
+ }
+ shipped=false;
+}
+
+struct indexedTransform {
+ int index;
+ transform t;
+ bool active;
+ void operator init(int index, transform t, bool active=true) {
+ this.index=index;
+ this.t=t;
+ this.active=active;
+ }
+}
+
+struct framedTransformStack {
+ struct transact {
+ transform t;
+ bool active;
+ void operator init(transform t, bool active=true) {
+ this.t=t;
+ this.active=active;
+ }
+ void operator init(indexedTransform i){
+ this.t=i.t;
+ this.active=i.active;
+ }
+ void operator init() {
+ this.t=identity();
+ this.active=true;
+ }
+ }
+ private transact[] stack;
+ private int[] frames;
+ private int stackBase=0;
+ transform pop() {
+ if(stack.length == 0)
+ return identity();
+ else {
+ transform popped=stack[0].t;
+ stack.delete(0);
+ report("Popped");
+ report(popped);
+ return popped;
+ }
+ }
+
+ transform pop0() {
+ if(stack.length == 0)
+ return identity();
+ else {
+ static transform zerotransform=(0,0,0,0,0,0);
+ transform popped=stack[0].active ? stack[0].t : zerotransform;
+ stack.delete(0);
+ report("Popped");
+ report(popped);
+ return popped;
+ }
+ }
+
+ void push(transform t, bool Active=true) {
+ report("Pushed");
+ report(t);
+ stack.push(transact(t,Active));
+ }
+
+ void add(... indexedTransform[] tList) {
+ transact[] toPush;
+ for(int a=0; a < tList.length; ++a)
+ toPush[tList[a].index]=transact(tList[a]);
+ for(int a=0; a < toPush.length; ++a)
+ if(!toPush.initialized(a))
+ toPush[a]=transact();
+ report("Added");
+ report(toPush.length);
+ stack.append(toPush);
+ }
+
+ bool empty() {
+ return stack.length == 0;
+ }
+}
+
+framedTransformStack xformStack;
+
+void deconstruct(picture pic=currentpicture, real magnification=1)
+{
+ deconstruct(pic.fit(),currentpatterns,magnification,xformStack.pop);
+}
diff --git a/Master/texmf-dist/asymptote/pstoedit.asy b/Master/texmf-dist/asymptote/pstoedit.asy
new file mode 100644
index 00000000000..7baccd3ed69
--- /dev/null
+++ b/Master/texmf-dist/asymptote/pstoedit.asy
@@ -0,0 +1,18 @@
+pen textpen=basealign;
+pair align=Align;
+
+// Compatibility routines for the pstoedit (version 3.43 or later) backend.
+void gsave(picture pic=currentpicture)
+{
+ pic.add(new void (frame f, transform) {
+ gsave(f);
+ },true);
+}
+
+void grestore(picture pic=currentpicture)
+{
+ pic.add(new void (frame f, transform) {
+ grestore(f);
+ },true);
+}
+
diff --git a/Master/texmf-dist/asymptote/reload.js b/Master/texmf-dist/asymptote/reload.js
new file mode 100644
index 00000000000..a6526803056
--- /dev/null
+++ b/Master/texmf-dist/asymptote/reload.js
@@ -0,0 +1,23 @@
+// Load/reload the document associated with a given path.
+
+// UNIX: Copy to ~/.adobe/Acrobat/x.x/JavaScripts/
+// To avoid random window placement we recommend specifying an acroread
+// geometry option, for example: -geometry +0+0
+
+// MSWindows: Copy to %APPDATA%/Adobe/Acrobat/x.x/JavaScripts/
+
+// Note: x.x represents the appropriate Acrobat Reader version number.
+
+reload = app.trustedFunction(function(path) {
+ app.beginPriv();
+ n=app.activeDocs.length;
+ for(i=app.activeDocs.length-1; i >= 0; --i) {
+ Doc=app.activeDocs[i];
+ if(Doc.path == path && Doc != this) {
+ Doc.closeDoc();
+ break;
+ }
+ }
+ app.openDoc(path);
+ app.endPriv();
+ });
diff --git a/Master/texmf-dist/asymptote/roundedpath.asy b/Master/texmf-dist/asymptote/roundedpath.asy
new file mode 100644
index 00000000000..1a426f91dcb
--- /dev/null
+++ b/Master/texmf-dist/asymptote/roundedpath.asy
@@ -0,0 +1,84 @@
+// a function to round sharp edges of open and cyclic paths
+// written by stefan knorr
+
+path roundedpath(path A, real R, real S = 1)
+// create rounded path from path A with radius R and scale S = 1
+{
+ path RoundPath; // returned path
+ path LocalPath; // local straight subpath
+ path LocalCirc; // local edge circle for intersection
+ real LocalTime; // local intersectiontime between . and ..
+ pair LocalPair; // local point to be added to 'RoundPath'
+
+ int len=length(A); // length of given path 'A'
+ bool PathClosed=cyclic(A); // true, if given path 'A' is cyclic
+
+ // initialisation: define first Point of 'RoundPath' as
+ if (PathClosed) // ? is 'A' cyclic
+ RoundPath=scale(S)*point(point(A,0)--point(A,1), 0.5); // centerpoint of first straight subpath of 'A'
+ else
+ RoundPath=scale(S)*point(A,0); // first point of 'A'
+
+ // doing everything between start and end
+ // create round paths subpath by subpath for every i-th edge
+ for(int i=1; i < len; ++i)
+ {
+ // straight subpath towards i-th edge
+ LocalPath=point(A,i-1)---point(A,i);
+ // circle with radius 'R' around i-th edge
+ LocalCirc=circle(point(A,i),R);
+ // calculate intersection time between straight subpath and circle
+ real[] t=intersect(LocalPath, LocalCirc);
+ if(t.length > 0) {
+ LocalTime=t[0];
+ // define intersectionpoint between both paths
+ LocalPair=point(subpath(LocalPath, 0, LocalTime), 1);
+ // add straight subpath towards i-th curvature to 'RoundPath'
+ RoundPath=RoundPath--scale(S)*LocalPair;
+ }
+
+ // straight subpath from i-th edge to (i+1)-th edge
+ LocalPath=point(A,i)---point(A,i+1);
+ // calculate intersection-time between straight subpath and circle
+ real[] t=intersect(LocalPath, LocalCirc);
+ if(t.length > 0) {
+ LocalTime=t[0];
+ // define intersectionpoint between both paths
+ LocalPair=point(subpath(LocalPath, 0, LocalTime), 1);
+ // add curvature near i-th edge to 'RoundPath'
+ RoundPath=RoundPath..scale(S)*LocalPair;
+ }
+ }
+
+ // final steps to have a correct termination
+ if(PathClosed) { // Is 'A' cyclic?
+ // straight subpath towards 0-th edge
+ LocalPath=point(A,len-1)---point(A,0);
+ // circle with radius 'R' around 0-th edge
+ LocalCirc=circle(point(A,0),R);
+ // calculate intersection-time between straight subpath and circle
+ real[] t=intersect(LocalPath, LocalCirc);
+ if(t.length > 0) {
+ LocalTime=t[0];
+ // define intersectionpoint between both paths
+ LocalPair=point(subpath(LocalPath, 0, LocalTime), 1);
+ // add straight subpath towards 0-th curvature to 'RoundPath'
+ RoundPath=RoundPath--scale(S)*LocalPair;
+ }
+
+
+ // straight subpath from 0-th edge to 1st edge
+ LocalPath=point(A,0)---point(A,1);
+ // calculate intersection-time between straight subpath and circle
+ real[] t=intersect(LocalPath, LocalCirc);
+ if(t.length > 0) {
+ LocalTime=t[0];
+ // define intersectionpoint between both paths
+ LocalPair=point(subpath(LocalPath, 0, LocalTime), 1);
+ // add curvature near 0-th edge to 'RoundPath' and close path
+ RoundPath=RoundPath..scale(S)*LocalPair--cycle;
+ }
+ } else
+ RoundPath=RoundPath--scale(S)*point(A,len);
+ return RoundPath;
+}
diff --git a/Master/texmf-dist/asymptote/simplex.asy b/Master/texmf-dist/asymptote/simplex.asy
new file mode 100644
index 00000000000..43920109ff4
--- /dev/null
+++ b/Master/texmf-dist/asymptote/simplex.asy
@@ -0,0 +1,274 @@
+/*****
+ * simplex.asy
+ * Andy Hammerlindl 2004/07/27
+ *
+ * Solves the two-variable linear programming problem using the simplex method.
+ * This problem is specialized in that the second variable, "b", does not have
+ * a non-negativity condition, and the first variable, "a", is the quantity
+ * being maximized.
+ * Correct execution of the algorithm also assumes that the coefficient of "b"
+ * will be +1 or -1 in every added restriction, and that the problem can be
+ * initialized to a valid state by pivoting b with one of the slack
+ * variables. This assumption may in fact be incorrect.
+ *****/
+
+private real infinity=sqrt(0.25*realMax);
+
+struct problem {
+ typedef int var;
+ static var VAR_A = 0;
+ static var VAR_B = 1;
+
+ static int OPTIMAL = -1;
+ static var UNBOUNDED = -2;
+ static int INVALID = -3;
+
+ struct row {
+ real c, t[];
+ }
+
+ // The variables of the rows.
+ // Initialized for the two variable problem.
+ var[] v = {VAR_A, VAR_B};
+
+ // The rows of equalities.
+ row rowA() {
+ row r = new row;
+ r.c = 0;
+ r.t = new real[] {1, 0};
+ return r;
+ }
+ row rowB() {
+ row r = new row;
+ r.c = 0;
+ r.t = new real[] {0, 1};
+ return r;
+ }
+ row[] rows = {rowA(), rowB()};
+
+ // The number of original variables.
+ int n = rows.length;
+
+ // Pivot the variable v[col] with vp.
+ void pivot(int col, var vp)
+ {
+ var vc=v[col];
+
+ // Recalculate rows v[col] and vp for the pivot-swap.
+ row rvc = rows[vc], rvp = rows[vp];
+ real factor=1/rvp.t[col]; // NOTE: Handle rvp.t[col] == 0 case.
+ rvc.c=-rvp.c*factor;
+ rvp.c=0;
+ rvc.t=-rvp.t*factor;
+ rvp.t *= 0;
+ rvc.t[col]=factor;
+ rvp.t[col]=1;
+
+ var a=min(vc,vp);
+ var b=max(vc,vp);
+
+ // Recalculate the rows other than the two used for the above pivot.
+ for (var i = 0; i < a; ++i) {
+ row r=rows[i];
+ real m = r.t[col];
+ r.c += m*rvc.c;
+ r.t += m*rvc.t;
+ r.t[col]=m*factor;
+ }
+ for (var i = a+1; i < b; ++i) {
+ row r=rows[i];
+ real m = r.t[col];
+ r.c += m*rvc.c;
+ r.t += m*rvc.t;
+ r.t[col]=m*factor;
+ }
+ for (var i = b+1; i < rows.length; ++i) {
+ row r=rows[i];
+ real m = r.t[col];
+ r.c += m*rvc.c;
+ r.t += m*rvc.t;
+ r.t[col]=m*factor;
+ }
+
+ // Relabel the vars.
+ v[col] = vp;
+ }
+
+ // As b does not have a non-negativity condition, it must initially be
+ // pivoted out for a variable that does. This selects the initial
+ // variable to pivot with b. It also assumes that there is a valid
+ // solution with a == 0 to the linear programming problem, and if so, it
+ // picks a pivot to get to that state. In our case, a == 0 corresponds to
+ // a picture with the user coordinates shrunk down to zero, and if that
+ // doesn't fit, nothing will.
+ var initVar()
+ {
+ real min=infinity, max=-infinity;
+ var argmin=0, argmax=0;
+
+ for (var i = 2; i < rows.length; ++i) {
+ row r=rows[i];
+ if (r.t[VAR_B] > 0) {
+ real val=r.c/r.t[VAR_B];
+ if (val < min) {
+ min=val;
+ argmin=i;
+ }
+ } else if (r.t[VAR_B] < 0) {
+ real val=r.c/r.t[VAR_B];
+ if (val > max) {
+ max=val;
+ argmax=i;
+ }
+ }
+ }
+
+ // If b has a minimal value, choose a pivot that will give b its minimal
+ // value. Otherwise, if b has maximal value, choose a pivot to give b its
+ // maximal value.
+ return argmin != 0 ? argmin :
+ argmax != 0 ? argmax :
+ UNBOUNDED;
+ }
+
+ // Initialize the linear program problem by moving into an acceptable state
+ // this assumes that b is unrestrained and is the second variable.
+ // NOTE: Works in limited cases, may be bug-ridden.
+ void init()
+ {
+ // Find the lowest constant term in the equations.
+ var lowest = 0;
+ for (var i = 2; i < rows.length; ++i) {
+ if (rows[i].c < rows[lowest].c)
+ lowest = i;
+ }
+
+ // Pivot if necessary.
+ if (lowest != 0)
+ pivot(VAR_B, lowest);
+ }
+
+ // Selects a column to pivot on. Returns OPTIMAL if the current state is
+ // optimal. Assumes we are optimizing the first row.
+ int selectColumn()
+ {
+ int i=find(rows[0].t > 0,1);
+ return (i >= 0) ? i : OPTIMAL;
+ }
+
+ // Select the new variable associated with a pivot on the column given.
+ // Returns UNBOUNDED if the space is unbounded.
+ var selectVar(int col)
+ {
+ // We assume that the first two vars (a and b) once swapped out, won't be
+ // swapped back in. This finds the variable which gives the tightest
+ // non-negativity condition restricting our optimization. This turns
+ // out to be the max of c/t[col]. Note that as c is positive, and
+ // t[col] is negative, all c/t[col] will be negative, so we are finding
+ // the smallest in magnitude.
+ var vp=UNBOUNDED;
+ real max=-infinity;
+ for (int i = 2; i < rows.length; ++i) {
+ row r=rows[i];
+ if(r.c < max*r.t[col]) {
+ max=r.c/r.t[col]; vp=i;
+ }
+ }
+
+ return vp;
+ }
+
+ // Checks that the rows are in a valid state.
+ bool valid()
+ {
+ // Checks that constants are valid.
+ bool validConstants() {
+ for (int i = 0; i < rows.length; ++i)
+ // Do not test the row for b, as it does not have a non-negativity
+ // condition.
+ if (i != VAR_B && rows[i].c < 0)
+ return false;
+ return true;
+ }
+
+ // Check a variable to see if its row is simple.
+ // NOTE: Simple rows could be optimized out, since they are not really
+ // used.
+ bool validVar(int col) {
+
+ var vc = v[col];
+ row rvc = rows[vc];
+
+ if (rvc.c != 0)
+ return false;
+ for (int i = 0; i < n; ++i)
+ if (rvc.t[i] != (i == col ? 1 : 0))
+ return false;
+
+ return true;
+ }
+
+ if (!validConstants()) {
+ return false;
+ }
+ for (int i = 0; i < n; ++i)
+ if (!validVar(i)) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ // Perform the algorithm to find the optimal solution. Returns OPTIMAL,
+ // UNBOUNDED, or INVALID (if no solution is possible).
+ int optimize()
+ {
+ // Put into a valid state to begin and pivot b out.
+ var iv=initVar();
+ if (iv == UNBOUNDED)
+ return iv;
+ pivot(VAR_B, iv);
+
+ if (!valid())
+ return INVALID;
+
+ while(true) {
+ int col = selectColumn();
+
+ if (col == OPTIMAL)
+ return col;
+ var vp = selectVar(col);
+
+ if (vp == UNBOUNDED)
+ return vp;
+
+ pivot(col, vp);
+ }
+
+ // Shouldn't reach here.
+ return INVALID;
+ }
+
+ // Add a restriction to the problem:
+ // t1*a + t2*b + c >= 0
+ void addRestriction(real t1, real t2, real c)
+ {
+ row r = new row;
+ r.c = c;
+ r.t = new real[] {t1, t2};
+ rows.push(r);
+ }
+
+ // Return the value of a computed.
+ real a()
+ {
+ return rows[VAR_A].c;
+ }
+
+ // Return the value of b computed.
+ real b()
+ {
+ return rows[VAR_B].c;
+ }
+}
diff --git a/Master/texmf-dist/asymptote/size10.asy b/Master/texmf-dist/asymptote/size10.asy
new file mode 100644
index 00000000000..3bbd31227a9
--- /dev/null
+++ b/Master/texmf-dist/asymptote/size10.asy
@@ -0,0 +1,12 @@
+texpreamble("\makeatletter%
+\renewcommand\normalsize{\@setfontsize\normalsize\@xpt\@xiipt}%
+\renewcommand\small{\@setfontsize\small\@ixpt{11}}%
+\renewcommand\footnotesize{\@setfontsize\footnotesize\@viiipt{9.5}}%
+\renewcommand\scriptsize{\@setfontsize\scriptsize\@viipt\@viiipt}%
+\renewcommand\tiny{\@setfontsize\tiny\@vpt\@vipt}%
+\renewcommand\large{\@setfontsize\large\@xiipt{14}}%
+\renewcommand\Large{\@setfontsize\Large\@xivpt{18}}%
+\renewcommand\LARGE{\@setfontsize\LARGE\@xviipt{22}}%
+\renewcommand\huge{\@setfontsize\huge\@xxpt{25}}%
+\renewcommand\Huge{\@setfontsize\Huge\@xxvpt{30}}%
+\makeatother");
diff --git a/Master/texmf-dist/asymptote/size11.asy b/Master/texmf-dist/asymptote/size11.asy
new file mode 100644
index 00000000000..93712288152
--- /dev/null
+++ b/Master/texmf-dist/asymptote/size11.asy
@@ -0,0 +1,12 @@
+texpreamble("\makeatletter%
+\renewcommand\normalsize{\@setfontsize\normalsize\@xipt{13.6}}%
+\renewcommand\small{\@setfontsize\small\@xpt\@xiipt}%
+\renewcommand\footnotesize{\@setfontsize\footnotesize\@ixpt{11}}%
+\renewcommand\scriptsize{\@setfontsize\scriptsize\@viiipt{9.5}}
+\renewcommand\tiny{\@setfontsize\tiny\@vipt\@viipt}
+\renewcommand\large{\@setfontsize\large\@xiipt{14}}
+\renewcommand\Large{\@setfontsize\Large\@xivpt{18}}
+\renewcommand\LARGE{\@setfontsize\LARGE\@xviipt{22}}
+\renewcommand\huge{\@setfontsize\huge\@xxpt{25}}
+\renewcommand\Huge{\@setfontsize\Huge\@xxvpt{30}}
+\makeatother");
diff --git a/Master/texmf-dist/asymptote/slide.asy b/Master/texmf-dist/asymptote/slide.asy
new file mode 100644
index 00000000000..0a3e0f65fab
--- /dev/null
+++ b/Master/texmf-dist/asymptote/slide.asy
@@ -0,0 +1,609 @@
+import fontsize;
+usepackage("asycolors");
+
+bool reverse=false; // Set to true to enable reverse video.
+bool stepping=false; // Set to true to enable stepping.
+bool itemstep=true; // Set to false to disable stepping on each item.
+
+settings.toolbar=false; // Disable 3D toolbar by default.
+if(settings.render < 0) settings.render=4;
+
+bool allowstepping=false; // Allow stepping for current slide.
+
+real pagemargin=0.5cm;
+real pagewidth=-2pagemargin;
+real pageheight=-2pagemargin;
+
+bool landscape=orientation == Landscape || orientation == Seascape;
+
+if(landscape) {
+ orientation=Portrait;
+ pagewidth += settings.paperheight;
+ pageheight += settings.paperwidth;
+} else {
+ pagewidth += settings.paperwidth;
+ pageheight += settings.paperheight;
+}
+
+size(pagewidth,pageheight,IgnoreAspect);
+picture background;
+
+real minipagemargin=1inch;
+real minipagewidth=pagewidth-2minipagemargin;
+
+transform tinv=inverse(fixedscaling((-1,-1),(1,1),currentpen));
+
+pen itempen=fontsize(24pt);
+pen codepen=fontsize(20pt);
+pen titlepagepen=fontsize(36pt);
+pen authorpen=fontsize(24pt);
+pen institutionpen=authorpen;
+pen datepen=fontsize(18pt);
+pen urlpen=datepen;
+
+real itemskip=0.5;
+real codeskip=0.25;
+pair dateskip=(0,0.1);
+pair urlskip=(0,0.2);
+
+pair titlealign=3S;
+pen titlepen=fontsize(32pt);
+real titleskip=0.5;
+
+string oldbulletcolor;
+string newbulletcolor="red";
+string bullet="{\bulletcolor\textbullet}";
+
+pair pagenumberposition=S+E;
+pair pagenumberalign=4NW;
+pen pagenumberpen=fontsize(12);
+pen steppagenumberpen=colorless(pagenumberpen);
+
+real figureborder=0.25cm;
+pen figuremattpen;
+
+pen backgroundcolor;
+pen foregroundcolor;
+
+pair titlepageposition=(-0.8,0.4);
+pair startposition=(-0.8,0.9);
+pair currentposition=startposition;
+
+string bulletcolor(string color)
+{
+ return "\def\bulletcolor{"+'\\'+"color{"+color+"}}%";
+}
+
+int[] firstnode=new int[] {currentpicture.nodes.length};
+int[] lastnode;
+bool firststep=true;
+
+int page=0;
+bool havepagenumber=true;
+
+int preamblenodes=2;
+
+bool empty()
+{
+ return currentpicture.nodes.length <= preamblenodes;
+}
+
+void background()
+{
+ if(!background.empty()) {
+ add(background);
+ layer();
+ preamblenodes += 2;
+ }
+}
+
+void color(string name, string color)
+{
+ texpreamble("\def"+'\\'+name+"#1{{\color{"+color+"}#1}}%");
+}
+
+string texcolor(pen p)
+{
+ real[] colors=colors(p);
+ string s;
+ if(colors.length > 0) {
+ s="{"+colorspace(p)+"}{";
+ for(int i=0; i < colors.length-1; ++i)
+ s += format("%.6f",colors[i],"C")+",";
+ s += format("%.6f",colors[colors.length-1],"C")+"}";
+ }
+ return s;
+}
+
+void setpens(pen red=red, pen blue=blue, pen steppen=red)
+{
+ itempen=colorless(itempen);
+ codepen=colorless(codepen);
+ pagenumberpen=colorless(pagenumberpen);
+ steppagenumberpen=colorless(steppagenumberpen)+steppen;
+ titlepagepen=colorless(titlepagepen)+red;
+ authorpen=colorless(authorpen)+blue;
+ institutionpen=colorless(institutionpen)+blue;
+ datepen=colorless(datepen);
+ urlpen=colorless(urlpen);
+}
+
+void reversevideo()
+{
+ backgroundcolor=black;
+ foregroundcolor=white;
+ fill(background,box((-1,-1),(1,1)),backgroundcolor);
+ setpens(mediumred,paleblue,mediumblue);
+ // Work around pdflatex bug, in which white is mapped to black!
+ figuremattpen=pdf() ? cmyk(0,0,0,1/255) : white;
+ color("Red","mediumred");
+ color("Green","green");
+ color("Blue","paleblue");
+ color("Foreground","white");
+ color("Background","black");
+ oldbulletcolor="white";
+ defaultpen(itempen+foregroundcolor);
+}
+
+void normalvideo() {
+ backgroundcolor=invisible;
+ foregroundcolor=black;
+ background=new picture;
+ size(background,currentpicture);
+ setpens();
+ figuremattpen=invisible;
+ color("Red","red");
+ color("Green","heavygreen");
+ color("Blue","blue");
+ color("Foreground","black");
+ color("Background","white");
+ oldbulletcolor="black";
+ defaultpen(itempen+foregroundcolor);
+}
+
+normalvideo();
+
+texpreamble(bulletcolor(newbulletcolor));
+texpreamble("\hyphenpenalty=10000\tolerance=1000");
+
+// Evaluate user command line option.
+void usersetting()
+{
+ plain.usersetting();
+ if(reverse) { // Black background
+ reversevideo();
+ } else { // White background
+ normalvideo();
+ }
+}
+
+void numberpage(pen p=pagenumberpen)
+{
+ if(havepagenumber) {
+ label((string) page,pagenumberposition,pagenumberalign,p);
+ }
+}
+
+void nextpage(pen p=pagenumberpen)
+{
+ if(!empty()) {
+ numberpage(p);
+ newpage();
+ }
+ background();
+ firststep=true;
+}
+
+void newslide(bool stepping=true)
+{
+ allowstepping=stepping;
+ nextpage();
+ ++page;
+ havepagenumber=true;
+ currentposition=startposition;
+ firstnode=new int[] {currentpicture.nodes.length};
+ lastnode.delete();
+}
+
+bool checkposition()
+{
+ if(abs(currentposition.x) > 1 || abs(currentposition.y) > 1) {
+ newslide();
+ return false;
+ }
+ return true;
+}
+
+void erasestep(int erasenode) {
+ if(!stepping || !allowstepping) return;
+ if(!checkposition()) return;
+ lastnode.push(erasenode);
+ nextpage(steppagenumberpen);
+ for(int i=0; i < firstnode.length; ++i) {
+ for(int j=firstnode[i]; j <= lastnode[i]; ++j) {
+ tex(bulletcolor(oldbulletcolor));
+ currentpicture.add(currentpicture.nodes[j]);
+ }
+ }
+ firstnode.push(currentpicture.nodes.length-1);
+ tex(bulletcolor(newbulletcolor));
+}
+
+void step()
+{
+ // Step without erasing anything.
+ erasestep(currentpicture.nodes.length-1);
+}
+
+void incrementposition(pair z)
+{
+ currentposition += z;
+}
+
+void title(string s, pair position=N, pair align=titlealign,
+ pen p=titlepen, bool newslide=true)
+{
+ if(newslide) newslide();
+ checkposition();
+ frame f;
+ if(s != "") label(f,minipage("\center "+s,minipagewidth),(0,0),align,p);
+ add(f,position,labelmargin(p)*align);
+ currentposition=(currentposition.x,position.y+
+ (tinv*(min(f)-titleskip*I*lineskip(p)*pt)).y);
+}
+
+void outline(string s="Outline", pair position=N, pair align=titlealign,
+ pen p=titlepen)
+{
+ newslide(stepping=false);
+ title(s,position,align,p,newslide=false);
+}
+
+void remark(bool center=false, string s, pair align=0, pen p=itempen,
+ real indent=0, bool minipage=true, real skip=itemskip,
+ filltype filltype=NoFill, bool step=false)
+{
+ checkposition();
+ if(minipage) s=minipage(s,minipagewidth);
+
+ pair offset;
+ if(center) {
+ if(align == 0) align=S;
+ offset=(0,currentposition.y);
+ } else {
+ if(align == 0) align=SE;
+ offset=currentposition;
+ }
+
+ frame f;
+ label(f,s,(indent,0),align,p,filltype);
+ pair m=tinv*min(f);
+ pair M=tinv*min(f);
+
+ if(abs(offset.x+M.x) > 1)
+ warning("slidetoowide","slide too wide on page "+(string) page+':\n'+
+ (string) s);
+
+ if(abs(offset.y+M.y) > 1) {
+ void toohigh() {
+ warning("slidetoohigh","slide too high on page "+(string) page+':\n'+
+ (string) s);
+ }
+ if(M.y-m.y < 2) {
+ newslide(); offset=(offset.x,currentposition.y);
+ if(offset.y+M.y > 1 || offset.y+m.y < -1) toohigh();
+ } else toohigh();
+ }
+
+ if(step) {
+ if(!firststep) step();
+ firststep=false;
+ }
+
+ add(f,offset);
+ incrementposition((0,(tinv*(min(f)-skip*I*lineskip(p)*pt)).y));
+}
+
+void center(string s, pen p=itempen)
+{
+ remark("\center "+s,p);
+}
+
+void equation(string s, pen p=itempen)
+{
+ remark(center=true,"\vbox{$$"+s+"$$}",p,minipage=false,skip=0);
+}
+
+void vbox(string s, pen p=itempen)
+{
+ remark(center=true,"\vbox{"+s+"}",p,minipage=false,skip=0);
+}
+
+void equations(string s, pen p=itempen)
+{
+ vbox("\begin{eqnarray*}"+s+"\end{eqnarray*}",p);
+}
+
+void skip(real n=1)
+{
+ incrementposition((0,(tinv*(-n*itemskip*I*lineskip(itempen)*pt)).y));
+}
+
+void display(frame[] f, real margin=0, pair align=S, pen p=itempen,
+ pen figuremattpen=figuremattpen, bool final=true)
+{
+ if(f.length == 0) return;
+ real[] width=new real[f.length];
+ real sum;
+ for(int i=0; i < f.length; ++i) {
+ width[i]=size(f[i]).x;
+ sum += width[i];
+ }
+ if(sum > pagewidth)
+ warning("toowide","slide too wide on page "+(string) page);
+ else margin=(pagewidth-sum)/(f.length+1);
+ real pos;
+ frame F;
+ for(int i=0; i < f.length; ++i) {
+ real w=0.5*(margin+width[i]);
+ pos += w;
+ add(F,f[i],(pos,0),Fill(figureborder,figuremattpen));
+ pos += w;
+ }
+ add(F,(0,currentposition.y),align);
+ if (final) {
+ real a=0.5(unit(align).y-1);
+ incrementposition(
+ (0, (tinv*(a*(max(F)-min(F))-itemskip*I*lineskip(p)*pt)).y));
+ }
+}
+
+void display(frame f, real margin=0, pair align=S, pen p=itempen,
+ pen figuremattpen=figuremattpen, bool final=true)
+{
+ display(new frame[] {f},margin,align,p,figuremattpen, final);
+}
+
+void display(string[] s, real margin=0, string[] captions=new string[],
+ string caption="", pair align=S, pen p=itempen,
+ pen figuremattpen=figuremattpen, bool final=true)
+{
+ frame[] f=new frame[s.length];
+ frame F;
+ for(int i=0; i < s.length; ++i) {
+ f[i]=newframe;
+ label(f[i],s[i]);
+ add(F,f[i],(0,0));
+ }
+ real y=point(F,S).y;
+ int stop=min(s.length,captions.length);
+ for(int i=0; i < stop; ++i) {
+ if(captions[i] != "")
+ label(f[i],captions[i],point(f[i],S).x+I*y,S);
+ }
+ display(f,margin,align,p,figuremattpen, final);
+ if(caption != "") center(caption,p);
+}
+
+void display(string s, string caption="", pair align=S, pen p=itempen,
+ pen figuremattpen=figuremattpen, bool final=true)
+{
+ display(new string[] {s},caption,align,p,figuremattpen, final);
+}
+
+void figure(string[] s, string options="", real margin=0,
+ string[] captions=new string[], string caption="",
+ pair align=S, pen p=itempen, pen figuremattpen=figuremattpen,
+ bool final=true)
+{
+ string[] S;
+ for(int i=0; i < s.length; ++i) {
+ S[i]=graphic(s[i],options);
+ }
+
+ display(S,margin,captions,caption,align,itempen,figuremattpen,final);
+}
+
+void figure(string s, string options="", string caption="", pair align=S,
+ pen p=itempen, pen figuremattpen=figuremattpen, bool final=true)
+{
+ figure(new string[] {s},options,caption,align,p,figuremattpen,final);
+}
+
+void multifigure(string[] slist, string options="", string caption="",
+ pair align=S, pen p=itempen, pen figuremattpen=figuremattpen,
+ bool step=itemstep)
+{
+ if(step) {
+ int lastnode=currentpicture.nodes.length-1;
+ for (int i=0; i<slist.length-1; ++i) {
+ figure(slist[i],options,caption,align,p,figuremattpen,final=false);
+ erasestep(lastnode);
+ }
+ }
+ figure(slist[slist.length-1],options,caption,align,p,figuremattpen,final=true);
+
+ if(!firststep) step();
+ firststep=false;
+}
+
+void indexedfigure(string prefix, int first, int last,
+ string options="", string caption="",
+ pair align=S, pen p=itempen, pen figuremattpen=figuremattpen,
+ bool step=itemstep)
+{
+ string[] s;
+ for(int i=first; i <= last; ++i)
+ s.push(prefix+string(i));
+ multifigure(s,options,caption,align,p,figuremattpen,step=step);
+}
+
+string[] codefile;
+
+void asyinclude(string s, real xsize=0, real ysize=xsize)
+{
+ picture currentpictureSave=currentpicture;
+ currentpicture=new picture;
+ _eval("include \""+s+"\";",true);
+ s=stripdirectory(outprefix()+"_"+s);
+ codefile.push(s);
+ frame f=(xsize > 0 || ysize > 0) ?
+ currentpicture.fit(xsize,ysize) : currentpicture.fit();
+ currentpicture=currentpictureSave;
+ display(f);
+}
+
+string cropcode(string s)
+{
+ while(substr(s,0,1) == '\n') s=substr(s,1,length(s));
+ while(substr(s,length(s)-1,1) == '\n') s=substr(s,0,length(s)-1);
+ return s;
+}
+
+void code(bool center=false, string s, pen p=codepen,
+ real indent=0, real skip=codeskip,
+ filltype filltype=NoFill)
+{
+ remark(center,"{\tt "+verbatim(cropcode(s))+"}",p,indent,skip,filltype);
+}
+
+void filecode(bool center=false, string s, pen p=codepen, real indent=0,
+ real skip=codeskip, filltype filltype=NoFill)
+{
+ code(center,file(s),p,indent,skip,filltype);
+}
+
+void asyfigure(string s, string options="", string caption="", pair align=S,
+ pen p=codepen, pen figuremattpen=figuremattpen,
+ filltype filltype=NoFill, bool newslide=false)
+{
+ string a=s+".asy";
+ asy(nativeformat(),s);
+ s += "."+nativeformat();
+ if(newslide && !empty()) {
+ newslide();
+ currentposition=(currentposition.x,0);
+ align=0;
+ }
+ figure(s,options,caption,align,p,figuremattpen);
+}
+
+string asywrite(string s, string preamble="")
+{
+ static int count=0;
+ string name=outprefix()+"_slide"+(string) count;
+ ++count;
+ file temp=output(name+".asy");
+ write(temp,preamble);
+ write(temp,s);
+ close(temp);
+ codefile.push(name);
+ return name;
+}
+
+void asycode(bool center=false, string s, string options="",
+ string caption="", string preamble="",
+ pair align=S, pen p=codepen, pen figuremattpen=figuremattpen,
+ real indent=0, real skip=codeskip,
+ filltype filltype=NoFill, bool newslide=false)
+{
+ code(center,s,p,indent,skip,filltype);
+ asyfigure(asywrite(s,preamble),options,caption,align,p,figuremattpen,filltype,
+ newslide);
+}
+
+void asyfilecode(bool center=false, string s, string options="",
+ string caption="",
+ pair align=S, pen p=codepen, pen figuremattpen=figuremattpen,
+ real indent=0, real skip=codeskip,
+ filltype filltype=NoFill, bool newslide=false)
+{
+ filecode(center,s+".asy",p,indent,skip,filltype);
+ asyfigure(s,options,caption,align,p,figuremattpen,filltype,newslide);
+}
+
+void item(string s, pen p=itempen, bool step=itemstep)
+{
+ frame b;
+ label(b,bullet,(0,0),p);
+ real bulletwidth=max(b).x-min(b).x;
+ remark(bullet+"\hangindent"+(string) (bulletwidth/pt)+"pt$\,$"+s,p,
+ -bulletwidth,step=step);
+}
+
+void subitem(string s, pen p=itempen)
+{
+ remark("\quad -- "+s,p);
+}
+
+void titlepage(string title, string author, string institution="",
+ string date="", string url="", bool newslide=false)
+{
+ newslide();
+ currentposition=titlepageposition;
+ center(title,titlepagepen);
+ center(author,authorpen);
+ if(institution != "") center(institution,institutionpen);
+ currentposition -= dateskip;
+ if(date != "") center(date,datepen);
+ currentposition -= urlskip;
+ if(url != "") center("{\tt "+url+"}",urlpen);
+}
+
+// Resolve optional bibtex citations:
+void bibliographystyle(string name)
+{
+ settings.twice=true;
+ settings.keepaux=true;
+ texpreamble("\bibliographystyle{"+name+"}");
+}
+
+void bibliography(string name)
+{
+ numberpage();
+ havepagenumber=false;
+ string s=texcolor(backgroundcolor);
+ if(s != "") tex("\definecolor{Background}"+s+"\pagecolor{Background}%");
+ label("",itempen);
+ tex("\eject\def\refname{\fontsize{"+string(fontsize(titlepen))+"}{"+
+ string(lineskip(titlepen))+"}\selectfont References}%");
+ real hmargin,vmargin;
+ if(pdf()) {
+ hmargin=1;
+ vmargin=0;
+ } else {
+ hmargin=1.5;
+ vmargin=1;
+ }
+ string s;
+ if(landscape) {
+ s="{\centering\textheight="+string(pageheight-1inch)+"bp\textwidth="+
+ string(pagewidth-1.5inches)+"bp"+
+ "\vsize=\textheight\hsize=\textwidth\linewidth=\hsize"+
+ "\topmargin="+string(vmargin)+"in\oddsidemargin="+string(hmargin)+"in";
+ } else
+ s="{\centering\textheight="+string(pageheight-0.5inches)+"bp\textwidth="+
+ string(pagewidth-0.5inches)+
+ "bp\hsize=\textwidth\linewidth=\textwidth\vsize=\textheight"+
+ "\topmargin=0.5in\oddsidemargin=1in";
+ s += "\evensidemargin=\oddsidemargin\bibliography{"+name+"}\eject}";
+ tex(s);
+}
+
+exitfcn currentexitfunction=atexit();
+
+void exitfunction()
+{
+ numberpage();
+ if(currentexitfunction != null) currentexitfunction();
+ if(!settings.keep)
+ for(int i=0; i < codefile.length; ++i) {
+ string name=codefile[i];
+ delete(name+"."+nativeformat());
+ delete(name+"_.aux");
+ delete(name+".asy");
+ }
+ codefile=new string[];
+}
+
+atexit(exitfunction);
diff --git a/Master/texmf-dist/asymptote/slopefield.asy b/Master/texmf-dist/asymptote/slopefield.asy
new file mode 100644
index 00000000000..bdbb0252670
--- /dev/null
+++ b/Master/texmf-dist/asymptote/slopefield.asy
@@ -0,0 +1,87 @@
+import graph_settings;
+real stepfraction=0.05;
+
+picture slopefield(real f(real,real), pair a, pair b,
+ int nx=nmesh, int ny=nx,
+ real tickfactor=0.5, pen p=currentpen, arrowbar arrow=None)
+{
+ picture pic;
+ real dx=(b.x-a.x)/nx;
+ real dy=(b.y-a.y)/ny;
+ real step=0.5*tickfactor*min(dx,dy);
+
+ for(int i=0; i <= nx; ++i) {
+ real x=a.x+i*dx;
+ for(int j=0; j <= ny; ++j) {
+ pair cp=(x,a.y+j*dy);
+ real slope=f(cp.x,cp.y);
+ real mp=step/sqrt(1+slope^2);
+ draw(pic,(cp.x-mp,cp.y-mp*slope)--(cp.x+mp,cp.y+mp*slope),p,arrow);
+ }
+ }
+ clip(pic,box(a,b));
+ return pic;
+}
+
+picture slopefield(real f(real), pair a, pair b,
+ int nx=nmesh, int ny=nx, pen p=currentpen,
+ arrowbar arrow=None)
+{
+ return slopefield(new real(real x, real y) {return f(x);},a,b,nx,ny,p,arrow);
+}
+
+path curve(pair c, real f(real,real), pair a, pair b)
+{
+ real step=stepfraction*(b.x-a.x);
+ real halfstep=0.5*step;
+ real sixthstep=step/6;
+
+ path follow(real sign) {
+ pair cp=c;
+ guide g=cp;
+ real dx,dy;
+ real factor=1;
+ do {
+ real slope;
+ pair S(pair z) {
+ slope=f(z.x,z.y);
+ return factor*sign/sqrt(1+slope^2)*(1,slope);
+ }
+ pair S3;
+ pair advance() {
+ pair S0=S(cp);
+ pair S1=S(cp+halfstep*S0);
+ pair S2=S(cp+halfstep*S1);
+ S3=S(cp+step*S2);
+ pair cp0=cp+sixthstep*(S0+2S1+2S2+S3);
+ dx=min(cp0.x-a.x,b.x-cp0.x);
+ dy=min(cp0.y-a.y,b.y-cp0.y);
+ return cp0;
+ }
+ pair cp0=advance();
+ if(dx < 0) {
+ factor=(step+dx)/step;
+ cp0=advance();
+ g=g..{S3}cp0{S3};
+ break;
+ }
+ if(dy < 0) {
+ factor=(step+dy)/step;
+ cp0=advance();
+ g=g..{S3}cp0{S3};
+ break;
+ }
+ cp=cp0;
+ g=g..{S3}cp{S3};
+ } while (dx > 0 && dy > 0);
+ return g;
+ }
+
+ return reverse(follow(-1))&follow(1);
+}
+
+path curve(pair c, real f(real), pair a, pair b)
+{
+ return curve(c,new real(real x, real y){return f(x);},a,b);
+}
+
diff --git a/Master/texmf-dist/asymptote/solids.asy b/Master/texmf-dist/asymptote/solids.asy
new file mode 100644
index 00000000000..e6613fc29c5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/solids.asy
@@ -0,0 +1,408 @@
+import graph3;
+
+pen defaultbackpen=linetype(new real[] {4,4},4,scale=false);
+
+// A solid geometry package.
+
+// Try to find a bounding tangent line between two paths.
+real[] tangent(path p, path q, bool side)
+{
+ static real fuzz=1.0e-5;
+
+ if((cyclic(p) && inside(p,point(q,0)) ||
+ cyclic(q) && inside(q,point(p,0))) &&
+ intersect(p,q,fuzz).length == 0) return new real[];
+
+ for(int i=0; i < 100; ++i) {
+ real ta=side ? mintimes(p)[1] : maxtimes(p)[1];
+ real tb=side ? mintimes(q)[1] : maxtimes(q)[1];
+ pair a=point(p,ta);
+ pair b=point(q,tb);
+ real angle=angle(b-a,warn=false);
+ if(abs(angle) <= sqrtEpsilon || abs(abs(0.5*angle)-pi) <= sqrtEpsilon)
+ return new real[] {ta,tb};
+ transform t=rotate(-degrees(angle));
+ p=t*p;
+ q=t*q;
+ }
+ return new real[];
+}
+
+path line(path p, path q, real[] t)
+{
+ return point(p,t[0])--point(q,t[1]);
+}
+
+// Return the projection of a generalized cylinder of height h constructed
+// from area base in the XY plane and aligned with axis.
+path[] cylinder(path3 base, real h, triple axis=Z, projection P)
+{
+ base=rotate(-colatitude(axis),cross(axis,Z))*base;
+ path3 top=shift(h*axis)*base;
+ path Base=project(base,P);
+ path Top=project(top,P);
+ real[] t1=tangent(Base,Top,true);
+ real[] t2=tangent(Base,Top,false);
+ path p=subpath(Base,t1[0]/P.ninterpolate,t2[0]/P.ninterpolate);
+ path q=subpath(Base,t2[0]/P.ninterpolate,t1[0]/P.ninterpolate);
+ return Base^^Top^^line(Base,Top,t1)^^line(Base,Top,t2);
+}
+
+// The three-dimensional "wireframe" used to visualize a volume of revolution
+struct skeleton {
+ struct curve {
+ path3[] front;
+ path3[] back;
+ }
+ // transverse skeleton (perpendicular to axis of revolution)
+ curve transverse;
+ // longitudinal skeleton (parallel to axis of revolution)
+ curve longitudinal;
+}
+
+// A surface of revolution generated by rotating a planar path3 g
+// from angle1 to angle2 about c--c+axis.
+struct revolution {
+ triple c;
+ path3 g;
+ triple axis;
+ real angle1,angle2;
+ triple M;
+ triple m;
+
+ static real epsilon=10*sqrtEpsilon;
+
+ void operator init(triple c=O, path3 g, triple axis=Z, real angle1=0,
+ real angle2=360) {
+ this.c=c;
+ this.g=g;
+ this.axis=unit(axis);
+ this.angle1=angle1;
+ this.angle2=angle2;
+ M=max(g);
+ m=min(g);
+ }
+
+
+ revolution copy() {
+ return revolution(c,g,axis,angle1,angle2);
+ }
+
+ triple vertex(int i, real j) {
+ triple v=point(g,i);
+ triple center=c+dot(v-c,axis)*axis;
+ triple perp=v-center;
+ triple normal=cross(axis,perp);
+ return center+Cos(j)*perp+Sin(j)*normal;
+ }
+
+ // Construct the surface of rotation generated by rotating g
+ // from angle1 to angle2 sampled n times about the line c--c+axis.
+ // An optional surface pen color(int i, real j) may be specified
+ // to override the color at vertex(i,j).
+ surface surface(int n=nslice, pen color(int i, real j)=null) {
+ return surface(c,g,axis,n,angle1,angle2,color);
+ }
+
+ path3 slice(real position, int n=nCircle) {
+ triple v=point(g,position);
+ triple center=c+dot(v-c,axis)*axis;
+ triple perp=v-center;
+ if(abs(perp) <= epsilon*max(abs(m),abs(M))) return center;
+ triple v1=center+rotate(angle1,axis)*perp;
+ triple v2=center+rotate(angle2,axis)*perp;
+ path3 p=Arc(center,v1,v2,axis,n);
+ return (angle2-angle1) % 360 == 0 ? p&cycle : p;
+ }
+
+ triple camera(projection P) {
+ triple camera=P.camera;
+ if(P.infinity) {
+ real s=abs(M-m)+abs(m-P.target);
+ camera=P.target+camerafactor*s*unit(P.vector());
+ }
+ return camera;
+ }
+
+ // add transverse slice to skeleton s;
+ void transverse(skeleton s, real t, int n=nslice, projection P) {
+ skeleton.curve s=s.transverse;
+ path3 S=slice(t,n);
+ triple camera=camera(P);
+ int L=length(g);
+ real midtime=0.5*L;
+ real sign=sgn(dot(axis,camera-P.target))*sgn(dot(axis,dir(g,midtime)));
+ if(dot(M-m,axis) == 0 || (t <= epsilon && sign < 0) ||
+ (t >= L-epsilon && sign > 0))
+ s.front.push(S);
+ else {
+ path3 Sp=slice(t+epsilon,n);
+ path3 Sm=slice(t-epsilon,n);
+ path sp=project(Sp,P);
+ path sm=project(Sm,P);
+ real[] t1=tangent(sp,sm,true);
+ real[] t2=tangent(sp,sm,false);
+ if(t1.length > 1 && t2.length > 1) {
+ real t1=t1[0]/P.ninterpolate;
+ real t2=t2[0]/P.ninterpolate;
+ int len=length(S);
+ if(t2 < t1) {
+ real temp=t1;
+ t1=t2;
+ t2=temp;
+ }
+ path3 p1=subpath(S,t1,t2);
+ path3 p2=subpath(S,t2,len);
+ path3 P2=subpath(S,0,t1);
+ if(abs(midpoint(p1)-camera) <= abs(midpoint(p2)-camera)) {
+ s.front.push(p1);
+ if(cyclic(S))
+ s.back.push(p2 & P2);
+ else {
+ s.back.push(p2);
+ s.back.push(P2);
+ }
+ } else {
+ if(cyclic(S))
+ s.front.push(p2 & P2);
+ else {
+ s.front.push(p2);
+ s.front.push(P2);
+ }
+ s.back.push(p1);
+ }
+ } else {
+ if((t <= midtime && sign < 0) || (t >= midtime && sign > 0))
+ s.front.push(S);
+ else
+ s.back.push(S);
+ }
+ }
+ }
+
+ // add m evenly spaced transverse slices to skeleton s
+ void transverse(skeleton s, int m=0, int n=nslice, projection P) {
+ if(m == 0) {
+ int N=size(g);
+ for(int i=0; i < N; ++i)
+ transverse(s,(real) i,n,P);
+ } else if(m == 1)
+ transverse(s,reltime(g,0.5),n,P);
+ else {
+ real factor=1/(m-1);
+ for(int i=0; i < m; ++i)
+ transverse(s,reltime(g,i*factor),n,P);
+ }
+ }
+
+ // return approximate silhouette based on m evenly spaced transverse slices;
+ // must be recomputed if camera is adjusted
+ path3[] silhouette(int m=64, projection P=currentprojection) {
+ if(is3D())
+ warning("2Dsilhouette",
+ "silhouette routine is intended only for 2d projections");
+ path3 G,H;
+ int N=size(g);
+ int M=(m == 0) ? N : m;
+ real factor=m == 1 ? 0 : 1/(m-1);
+ int n=nslice;
+
+ real tfirst=-1;
+ real tlast;
+ for(int i=0; i < M; ++i) {
+ real t=(m == 0) ? i : reltime(g,i*factor);
+ path3 S=slice(t,n);
+ triple camera=camera(P);
+ path3 Sp=slice(t+epsilon,n);
+ path3 Sm=slice(t-epsilon,n);
+ path sp=project(Sp,P);
+ path sm=project(Sm,P);
+ real[] t1=tangent(sp,sm,true);
+ real[] t2=tangent(sp,sm,false);
+ if(t1.length > 1 && t2.length > 1) {
+ real t1=t1[0]/P.ninterpolate;
+ real t2=t2[0]/P.ninterpolate;
+ if(t1 != t2) {
+ G=G..point(S,t1);
+ H=point(S,t2)..H;
+ if(tfirst < 0) tfirst=t;
+ tlast=t;
+ }
+ }
+ }
+ int L=length(g);
+ real midtime=0.5*L;
+ triple camera=camera(P);
+ real sign=sgn(dot(axis,camera-P.target))*sgn(dot(axis,dir(g,midtime)));
+
+ skeleton sfirst;
+ transverse(sfirst,tfirst,n,P);
+ triple delta=this.M-this.m;
+ path3 cap;
+ if(dot(delta,axis) == 0 || (tfirst <= epsilon && sign < 0)) {
+ cap=sfirst.transverse.front[0];
+ } else {
+ if(sign > 0) {
+ if(sfirst.transverse.front.length > 0)
+ G=reverse(sfirst.transverse.front[0])..G;
+ } else {
+ if(sfirst.transverse.back.length > 0)
+ G=sfirst.transverse.back[0]..G;
+ }
+ }
+
+ skeleton slast;
+ transverse(slast,tlast,n,P);
+ if(dot(delta,axis) == 0 || (tlast >= L-epsilon && sign > 0)) {
+ cap=slast.transverse.front[0];
+ } else {
+ if(sign > 0) {
+ if(slast.transverse.back.length > 0)
+ H=reverse(slast.transverse.back[0])..H;
+ } else {
+ if(slast.transverse.front.length > 0)
+ H=slast.transverse.front[0]..H;
+ }
+ }
+
+ return size(cap) == 0 ? G^^H : G^^H^^cap;
+ }
+
+ // add longitudinal curves to skeleton;
+ void longitudinal(skeleton s, int n=nslice, projection P) {
+ real t, d=0;
+ // Find a point on g of maximal distance from the axis.
+ int N=size(g);
+ for(int i=0; i < N; ++i) {
+ triple v=point(g,i);
+ triple center=c+dot(v-c,axis)*axis;
+ real r=abs(v-center);
+ if(r > d) {
+ t=i;
+ d=r;
+ }
+ }
+ path3 S=slice(t,n);
+ path3 Sm=slice(t+epsilon,n);
+ path3 Sp=slice(t-epsilon,n);
+ path sp=project(Sp,P);
+ path sm=project(Sm,P);
+ real[] t1=tangent(sp,sm,true);
+ real[] t2=tangent(sp,sm,false);
+ transform3 T=transpose(align(axis));
+ real Longitude(triple v) {return longitude(T*(v-c),warn=false);}
+ real ref=Longitude(point(g,t));
+ real angle(real t) {return Longitude(point(S,t/P.ninterpolate))-ref;}
+ triple camera=camera(P);
+ void push(real[] T) {
+ if(T.length > 1) {
+ path3 p=rotate(angle(T[0]),c,c+axis)*g;
+ path3 p1=subpath(p,0,t);
+ path3 p2=subpath(p,t,length(p));
+ if(length(p1) > 0 &&
+ abs(midpoint(p1)-camera) <= abs(midpoint(p2)-camera)) {
+ s.longitudinal.front.push(p1);
+ s.longitudinal.back.push(p2);
+ } else {
+ s.longitudinal.back.push(p1);
+ s.longitudinal.front.push(p2);
+ }
+ }
+ }
+ push(t1);
+ push(t2);
+ }
+
+ skeleton skeleton(int m=0, int n=nslice, projection P) {
+ skeleton s;
+ transverse(s,m,n,P);
+ longitudinal(s,n,P);
+ return s;
+ }
+}
+
+surface surface(revolution r, int n=nslice, pen color(int i, real j)=null)
+{
+ return r.surface(n,color);
+}
+
+// Draw on picture pic the skeleton of the surface of revolution r.
+// Draw the front portion of each of the m transverse slices with pen p and
+// the back portion with pen backpen. Rotational arcs are based on
+// n-point approximations to the unit circle.
+void draw(picture pic=currentpicture, revolution r, int m=0, int n=nslice,
+ pen frontpen=currentpen, pen backpen=frontpen,
+ pen longitudinalpen=frontpen, pen longitudinalbackpen=backpen,
+ light light=currentlight, string name="",
+ render render=defaultrender, projection P=currentprojection)
+{
+ if(is3D()) {
+ pen thin=thin();
+ void drawskeleton(frame f, transform3 t, projection P) {
+ skeleton s=r.skeleton(m,n,inverse(t)*P);
+ if(frontpen != nullpen) {
+ draw(f,t*s.transverse.back,thin+defaultbackpen+backpen,light);
+ draw(f,t*s.transverse.front,thin+frontpen,light);
+ }
+ if(longitudinalpen != nullpen) {
+ draw(f,t*s.longitudinal.back,thin+defaultbackpen+longitudinalbackpen,
+ light);
+ draw(f,t*s.longitudinal.front,thin+longitudinalpen,light);
+ }
+ }
+
+ 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)
+ pic.addBox(min(f,P),max(f,P),min(frontpen),max(frontpen));
+ });
+ frame f;
+ drawskeleton(f,identity4,P);
+ pic.addBox(min3(f),max3(f));
+ endgroup3(pic);
+ } else {
+ skeleton s=r.skeleton(m,n,P);
+ if(frontpen != nullpen) {
+ draw(pic,s.transverse.back,defaultbackpen+backpen,light);
+ draw(pic,s.transverse.front,frontpen,light);
+ }
+ if(longitudinalpen != nullpen) {
+ draw(pic,s.longitudinal.back,defaultbackpen+longitudinalbackpen,
+ light);
+ draw(pic,s.longitudinal.front,longitudinalpen,light);
+ }
+ }
+}
+
+revolution operator * (transform3 t, revolution r)
+{
+ triple trc=t*r.c;
+ return revolution(trc,t*r.g,t*(r.c+r.axis)-trc,r.angle1,r.angle2);
+}
+
+// Return a right circular cylinder of height h in the direction of axis
+// based on a circle centered at c with radius r.
+revolution cylinder(triple c=O, real r, real h, triple axis=Z)
+{
+ triple C=c+r*perp(axis);
+ axis=h*unit(axis);
+ return revolution(c,C--C+axis,axis);
+}
+
+// Return a right circular cone of height h in the direction of axis
+// based on a circle centered at c with radius r. The parameter n
+// controls the accuracy near the degenerate point at the apex.
+revolution cone(triple c=O, real r, real h, triple axis=Z, int n=nslice)
+{
+ axis=unit(axis);
+ return revolution(c,approach(c+r*perp(axis)--c+h*axis,n),axis);
+}
+
+// Return an approximate sphere of radius r centered at c obtained by rotating
+// an (n+1)-point approximation to a half circle about the Z axis.
+// Note: unitsphere provides a smoother and more efficient surface.
+revolution sphere(triple c=O, real r, int n=nslice)
+{
+ return revolution(c,Arc(c,r,180,0,0,0,Y,n),Z);
+}
diff --git a/Master/texmf-dist/asymptote/stats.asy b/Master/texmf-dist/asymptote/stats.asy
new file mode 100644
index 00000000000..be9efdfa429
--- /dev/null
+++ b/Master/texmf-dist/asymptote/stats.asy
@@ -0,0 +1,292 @@
+private import graph;
+
+real legendmarkersize=2mm;
+
+real mean(real A[])
+{
+ return sum(A)/A.length;
+}
+
+// unbiased estimate
+real variance(real A[])
+{
+ return sum((A-mean(A))^2)/(A.length-1);
+}
+
+real variancebiased(real A[])
+{
+ return sum((A-mean(A))^2)/A.length;
+}
+
+// unbiased estimate
+real stdev(real A[])
+{
+ return sqrt(variance(A));
+}
+
+real rms(real A[])
+{
+ return sqrt(sum(A^2)/A.length);
+}
+
+real skewness(real A[])
+{
+ real[] diff=A-mean(A);
+ return sum(diff^3)/sqrt(sum(diff^2)^3/A.length);
+}
+
+real kurtosis(real A[])
+{
+ real[] diff=A-mean(A);
+ return sum(diff^4)/sum(diff^2)^2*A.length;
+}
+
+real kurtosisexcess(real A[])
+{
+ return kurtosis(A)-3;
+}
+
+real Gaussian(real x, real sigma)
+{
+ static real sqrt2pi=sqrt(2pi);
+ return exp(-0.5*(x/sigma)^2)/(sigma*sqrt2pi);
+}
+
+real Gaussian(real x)
+{
+ static real invsqrt2pi=1/sqrt(2pi);
+ return exp(-0.5*x^2)*invsqrt2pi;
+}
+
+// Return frequency count of data in [bins[i],bins[i+1]) for i=0,...,n-1.
+int[] frequency(real[] data, real[] bins)
+{
+ int n=bins.length-1;
+ int[] freq=new int[n];
+ for(int i=0; i < n; ++i)
+ freq[i]=sum(bins[i] <= data & data < bins[i+1]);
+ return freq;
+}
+
+// Return frequency count in n uniform bins from a to b
+// (faster than the above more general algorithm).
+int[] frequency(real[] data, real a, real b, int n)
+{
+ int[] freq=sequence(new int(int x) {return 0;},n);
+ real h=n/(b-a);
+ for(int i=0; i < data.length; ++i) {
+ int I=Floor((data[i]-a)*h);
+ if(I >= 0 && I < n)
+ ++freq[I];
+ }
+ return freq;
+}
+
+// Return frequency count in [xbins[i],xbins[i+1]) and [ybins[j],ybins[j+1]).
+int[][] frequency(real[] x, real[] y, real[] xbins, real[] ybins)
+{
+ int n=xbins.length-1;
+ int m=ybins.length-1;
+ int[][] freq=new int[n][m];
+ bool[][] inybin=new bool[m][y.length];
+ for(int j=0; j < m; ++j)
+ inybin[j]=ybins[j] <= y & y < ybins[j+1];
+ for(int i=0; i < n; ++i) {
+ bool[] inxbini=xbins[i] <= x & x < xbins[i+1];
+ int[] freqi=freq[i];
+ for(int j=0; j < m; ++j)
+ freqi[j]=sum(inxbini & inybin[j]);
+ }
+ return freq;
+}
+
+// Return frequency count in nx by ny uniform bins in box(a,b).
+int[][] frequency(real[] x, real[] y, pair a, pair b, int nx, int ny=nx)
+{
+ int[][] freq=new int[nx][];
+ for(int i=0; i < nx; ++i)
+ freq[i]=sequence(new int(int x) {return 0;},ny);
+ real hx=nx/(b.x-a.x);
+ real hy=ny/(b.y-a.y);
+ real ax=a.x;
+ real ay=a.y;
+ for(int i=0; i < x.length; ++i) {
+ int I=Floor((x[i]-ax)*hx);
+ int J=Floor((y[i]-ay)*hy);
+ if(I >= 0 && I <= nx && J >= 0 && J <= ny)
+ ++freq[I][J];
+ }
+ return freq;
+}
+
+int[][] frequency(pair[] z, pair a, pair b, int nx, int ny=nx)
+{
+ int[][] freq=new int[nx][];
+ for(int i=0; i < nx; ++i)
+ freq[i]=sequence(new int(int x) {return 0;},ny);
+ real hx=nx/(b.x-a.x);
+ real hy=ny/(b.y-a.y);
+ real ax=a.x;
+ real ay=a.y;
+ for(int i=0; i < z.length; ++i) {
+ int I=Floor((z[i].x-ax)*hx);
+ int J=Floor((z[i].y-ay)*hy);
+ if(I >= 0 && I < nx && J >= 0 && J < ny)
+ ++freq[I][J];
+ }
+ return freq;
+}
+
+path halfbox(pair a, pair b)
+{
+ return a--(a.x,b.y)--b;
+}
+
+path topbox(pair a, pair b)
+{
+ return a--(a.x,b.y)--b--(b.x,a.y);
+}
+
+// Draw a histogram for bin boundaries bin[n+1] of frequency data in count[n].
+void histogram(picture pic=currentpicture, real[] bins, real[] count,
+ real low=-infinity,
+ pen fillpen=nullpen, pen drawpen=nullpen, bool bars=false,
+ Label legend="", real markersize=legendmarkersize)
+{
+ if((fillpen == nullpen || bars == true) && drawpen == nullpen)
+ drawpen=currentpen;
+ bool[] valid=count > 0;
+ real m=min(valid ? count : null);
+ real M=max(valid ? count : null);
+ bounds my=autoscale(pic.scale.y.scale.T(m),pic.scale.y.T(M),
+ pic.scale.y.scale);
+ if(low == -infinity) low=pic.scale.y.scale.Tinv(my.min);
+ real last=low;
+ int n=count.length;
+ begingroup(pic);
+ for(int i=0; i < n; ++i) {
+ if(valid[i]) {
+ real c=count[i];
+ pair b=Scale(pic,(bins[i+1],c));
+ pair a=Scale(pic,(bins[i],low));
+ if(fillpen != nullpen) {
+ fill(pic,box(a,b),fillpen);
+ if(!bars) draw(pic,b--(b.x,a.y),fillpen);
+ }
+ if(!bars)
+ draw(pic,halfbox(Scale(pic,(bins[i],last)),b),drawpen);
+ else draw(pic,topbox(a,b),drawpen);
+ last=c;
+ } else {
+ if(!bars && last != low) {
+ draw(pic,Scale(pic,(bins[i],last))--Scale(pic,(bins[i],low)),drawpen);
+ last=low;
+ }
+ }
+ }
+ if(!bars && last != low)
+ draw(pic,Scale(pic,(bins[n],last))--Scale(pic,(bins[n],low)),drawpen);
+ endgroup(pic);
+
+ if(legend.s != "") {
+ marker m=marker(scale(markersize)*shift((-0.5,-0.5))*unitsquare,
+ drawpen,fillpen == nullpen ? Draw :
+ (drawpen == nullpen ? Fill(fillpen) : FillDraw(fillpen)));
+ legend.p(drawpen);
+ pic.legend.push(Legend(legend.s,legend.p,invisible,m.f));
+ }
+}
+
+// Draw a histogram for data in n uniform bins between a and b
+// (optionally normalized).
+void histogram(picture pic=currentpicture, real[] data, real a, real b, int n,
+ bool normalize=false, real low=-infinity,
+ pen fillpen=nullpen, pen drawpen=nullpen, bool bars=false,
+ Label legend="", real markersize=legendmarkersize)
+{
+ real dx=(b-a)/n;
+ real[] freq=frequency(data,a,b,n);
+ if(normalize) freq /= dx*sum(freq);
+ histogram(pic,a+sequence(n+1)*dx,freq,low,fillpen,drawpen,bars,legend,
+ markersize);
+}
+
+// Method of Shimazaki and Shinomoto for selecting the optimal number of bins.
+// Shimazaki H. and Shinomoto S., A method for selecting the bin size of a
+// time histogram, Neural Computation (2007), Vol. 19(6), 1503-1527.
+// cf. http://www.ton.scphys.kyoto-u.ac.jp/~hideaki/res/histogram.html
+int bins(real[] data, int max=100)
+{
+ real m=min(data);
+ real M=max(data)*(1+epsilon);
+ real n=data.length;
+ int bins=1;
+ real minC=2n-n^2; // Cost function for N=1.
+ for(int N=2; N <= max; ++N) {
+ real C=N*(2n-sum(frequency(data,m,M,N)^2));
+ if(C < minC) {
+ minC=C;
+ bins=N;
+ }
+ }
+
+ return bins;
+}
+
+// return a pair of central Gaussian random numbers with unit variance
+pair Gaussrandpair()
+{
+ real r2,v1,v2;
+ do {
+ v1=2.0*unitrand()-1.0;
+ v2=2.0*unitrand()-1.0;
+ r2=v1*v1+v2*v2;
+ } while(r2 >= 1.0 || r2 == 0.0);
+ return (v1,v2)*sqrt(-log(r2)/r2);
+}
+
+// return a central Gaussian random number with unit variance
+real Gaussrand()
+{
+ static real sqrt2=sqrt(2.0);
+ static pair z;
+ static bool cached=true;
+ cached=!cached;
+ if(cached) return sqrt2*z.y;
+ z=Gaussrandpair();
+ return sqrt2*z.x;
+}
+
+struct linefit {
+ real m,b; // slope, intercept
+ real dm,db; // standard error in slope, intercept
+ real r; // correlation coefficient
+ real fit(real x) {
+ return m*x+b;
+ }
+}
+
+// Do a least-squares fit of data in real arrays x and y to the line y=m*x+b
+linefit leastsquares(real[] x, real[] y)
+{
+ linefit L;
+ int n=x.length;
+ if(n == 1) abort("Least squares fit requires at least 2 data points");
+ real sx=sum(x);
+ real sy=sum(y);
+ real sxx=n*sum(x^2)-sx^2;
+ real sxy=n*sum(x*y)-sx*sy;
+ L.m=sxy/sxx;
+ L.b=(sy-L.m*sx)/n;
+ if(n > 2) {
+ real syy=n*sum(y^2)-sy^2;
+ if(sxx == 0 || syy == 0) return L;
+ L.r=sxy/sqrt(sxx*syy);
+ real arg=syy-sxy^2/sxx;
+ if(arg <= 0) return L;
+ real s=sqrt(arg/(n-2));
+ L.dm=s*sqrt(1/sxx);
+ L.db=s*sqrt(1+sx^2/sxx)/n;
+ }
+ return L;
+}
diff --git a/Master/texmf-dist/asymptote/syzygy.asy b/Master/texmf-dist/asymptote/syzygy.asy
new file mode 100644
index 00000000000..93889b252e5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/syzygy.asy
@@ -0,0 +1,926 @@
+/***** syzygy.asy {{{1
+ * Andy Hammerlindl 2006/12/02
+ *
+ * Automates the drawing of braids, relations, and syzygies, along with the
+ * corresponding equations.
+ *
+ * See
+ * http://katlas.math.toronto.edu/drorbn/index.php?title=06-1350/Syzygies_in_Asymptote
+ * For more information.
+ *****/
+struct Component { // {{{1
+ // The number of strings coming in or out of the component.
+ int in;
+ int out;
+
+ // Which 'out' string each 'in' string is connected to. For deriving
+ // equations.
+ int[] connections;
+
+ string symbol; // For pullback notation.
+ string lsym; // For linear equations.
+ string codename; // For Mathematica code.
+
+ guide[] draw(picture pic, guide[] ins);
+}
+
+// Utility functions {{{1
+pair[] endpoints(guide[] a) {
+ pair[] z;
+ for (int i=0; i<a.length; ++i)
+ z.push(endpoint(a[i]));
+ return z;
+}
+
+pair min(pair[] z) {
+ pair m=(infinity, infinity);
+ for (int i=0; i<z.length; ++i) {
+ if (z[i].x < m.x)
+ m=(z[i].x,m.y);
+ if (z[i].y < m.y)
+ m=(m.x,z[i].y);
+ }
+ return m;
+}
+
+pair max(pair[] z) {
+ pair M=(-infinity, -infinity);
+ for (int i=0; i<z.length; ++i) {
+ if (z[i].x > M.x)
+ M=(z[i].x,M.y);
+ if (z[i].y > M.y)
+ M=(M.x,z[i].y);
+ }
+ return M;
+}
+
+// Component Definitions {{{1
+real hwratio=1.4;
+real gapfactor=6;
+
+Component bp=new Component;
+bp.in=2; bp.out=2;
+bp.connections=new int[] {1,0};
+bp.symbol="B^+"; bp.lsym="b^+"; bp.codename="bp";
+bp.draw=new guide[] (picture pic, guide[] ins) {
+ pair[] z=endpoints(ins);
+ pair m=min(z), M=max(z);
+ real w=M.x-m.x, h=hwratio*w;
+ pair centre=(0.5(m.x+M.x),M.y+h/2);
+
+ /*
+ return new guide[] {ins[1]..centre{NW}..z[0]+h*N,
+ ins[0]..centre{NE}..z[1]+h*N};
+ */
+
+ real offset=gapfactor*linewidth(currentpen);
+ draw(pic, ins[1]..(centre-offset*NW){NW});
+ return new guide[] {(centre+offset*NW){NW}..z[0]+h*N,
+ ins[0]..centre{NE}..z[1]+h*N};
+};
+
+Component bm=new Component;
+bm.in=2; bm.out=2;
+bm.connections=new int[] {1,0};
+bm.symbol="B^-"; bm.lsym="b^-"; bm.codename="bm";
+bm.draw=new guide[] (picture pic, guide[] ins) {
+ pair[] z=endpoints(ins);
+ pair m=min(z), M=max(z);
+ real w=M.x-m.x, h=hwratio*w;
+ pair centre=(0.5(m.x+M.x),M.y+h/2);
+
+ /*
+ return new guide[] {ins[1]..centre{NW}..z[0]+h*N,
+ ins[0]..centre{NE}..z[1]+h*N};
+ */
+
+ real offset=gapfactor*linewidth(currentpen);
+ draw(pic, ins[0]..(centre-offset*NE){NE});
+ return new guide[] {ins[1]..centre{NW}..z[0]+h*N,
+ (centre+offset*NE){NE}..z[1]+h*N};
+};
+
+Component phi=new Component;
+phi.in=2; phi.out=1;
+phi.connections=new int[] {0,0};
+phi.symbol="\Phi"; phi.lsym="\phi"; phi.codename="phi";
+phi.draw=new guide[] (picture pic, guide[] ins) {
+ pair[] z=endpoints(ins);
+ pair m=min(z), M=max(z);
+ real w=M.x-m.x, h=hwratio*w;
+ pair centre=(0.5(m.x+M.x),M.y+h/2);
+
+
+ //real offset=4*linewidth(currentpen);
+ draw(pic, ins[0]..centre{NE});
+ draw(pic, ins[1]..centre{NW});
+ draw(pic, centre,linewidth(5*linewidth(currentpen)));
+ dot(pic, centre);
+ return new guide[] {centre..centre+0.5h*N};
+};
+
+Component wye=new Component;
+wye.in=1; wye.out=2;
+wye.connections=null; // TODO: Fix this!
+wye.symbol="Y"; wye.lsym="y"; wye.codename="wye";
+wye.draw=new guide[] (picture pic, guide[] ins) {
+ pair z=endpoint(ins[0]);
+ real w=10, h=hwratio*w; // The 10 is a guess here, and may produce badness.
+ pair centre=(z.x,z.y+h/2);
+
+
+ draw(pic, ins[0]..centre);
+ draw(pic, centre,linewidth(5*linewidth(currentpen)));
+ return new guide[] {centre{NW}..centre+(-0.5w,0.5h),
+ centre{NE}..centre+(0.5w,0.5h)};
+};
+
+
+struct Braid { // {{{1
+ // Members {{{2
+ // Number of lines initially.
+ int n;
+
+ struct Placement {
+ Component c;
+ int place;
+
+ Placement copy() {
+ Placement p=new Placement;
+ p.c=this.c; p.place=this.place;
+ return p;
+ }
+ }
+ Placement[] places;
+
+ void add(Component c, int place) {
+ Placement p=new Placement;
+ p.c=c; p.place=place;
+ places.push(p);
+ }
+
+ void add(Braid sub, int place) {
+ for (int i=0; i<sub.places.length; ++i)
+ add(sub.places[i].c,sub.places[i].place+place);
+ }
+
+ // Drawing {{{2
+ guide[] drawStep(picture pic, Placement p, guide[] ins) {
+ int i=0,j=0;
+
+ // Draw the component.
+ Component c=p.c;
+ //write("drawing "+c.symbol+" at place "+(string)p.place);
+ guide[] couts=c.draw(pic, ins[sequence(c.in)+p.place]);
+
+ pair M=max(endpoints(couts));
+
+ // Extend lines not in the component.
+ guide[] outs;
+ pair[] z=endpoints(ins);
+ while (i<p.place) {
+ outs.push(ins[i]..(z[i].x,M.y));
+ ++i;
+ }
+
+ outs.append(couts);
+ i+=c.in;
+
+ while (i<ins.length) {
+ outs.push(ins[i]..(z[i].x,M.y));
+ ++i;
+ }
+
+ return outs;
+ }
+
+ void drawEnd(picture pic, guide[] ins, real minheight=0) {
+ pair[] z=endpoints(ins);
+ for (int i=0; i<ins.length; ++i) {
+ draw(pic, z[i].y >= minheight ? ins[i] : ins[i]..(z[i].x,minheight));
+ }
+ }
+
+ void draw(picture pic, guide[] ins, real minheight=0) {
+ int steps=places.length;
+
+ guide[] nodes=ins;
+ for (int i=0; i<steps; ++i) {
+ Placement p=places[i];
+ nodes=drawStep(pic, places[i], nodes);
+ }
+
+ drawEnd(pic, nodes, minheight);
+ }
+
+ void draw(picture pic=currentpicture, real spacing=15,
+ real minheight=2hwratio*spacing) {
+ pair[] ins;
+ for (int i=0; i<n; ++i)
+ ins.push((spacing*i,0));
+
+ draw(pic, ins, minheight);
+ }
+
+ // Utilities {{{2
+ int in() {
+ return n;
+ }
+ int out() {
+ int steps=places.length;
+ int num=n; // The number of nodes at this step.
+
+ for (int i=0; i<steps; ++i) {
+ Placement p=places[i];
+ int nextNum=num-p.c.in+p.c.out;
+ num=nextNum;
+ }
+ return num;
+ }
+
+ // Deep copy of a braid.
+ Braid copy() {
+ Braid b=new Braid;
+ b.n=this.n;
+ for (int i=0; i<this.places.length; ++i)
+ b.add(this.places[i].c,this.places[i].place);
+ return b;
+ }
+
+ // Matching {{{2
+ // Tests if a component p can be swapped with a component q which is assumed
+ // to be directly above it.
+ static bool swapable(Placement p, Placement q) {
+ return p.place + p.c.out <= q.place || // p is left of q or
+ q.place + q.c.in <= p.place; // q is left of p
+ }
+
+ // Creates a new braid with a transposition of two components.
+ Braid swap(int i, int j) {
+ if (i>j)
+ return swap(j,i);
+ else {
+ assert(j==i+1); assert(swapable(places[i],places[j]));
+
+ Placement p=places[i].copy();
+ Placement q=places[j].copy();
+ /*write("swap:");
+ write("p originally at " + (string)p.place);
+ write("q originally at " + (string)q.place);
+ write("p.c.in: " + (string)p.c.in + " p.c.out: " + (string)p.c.out);
+ write("q.c.in: " + (string)q.c.in + " q.c.out: " + (string)q.c.out);*/
+ if (q.place + q.c.in <= p.place)
+ // q is left of p - adjust for q renumbering strings.
+ p.place+=q.c.out-q.c.in;
+ else if (p.place + p.c.out <= q.place)
+ // q is right of p - adjust for p renumbering strings.
+ q.place+=p.c.in-p.c.out;
+ else
+ // q is directly on top of p
+ assert(false, "swapable");
+
+ /*write("q now at " + (string)q.place);
+ write("p now at " + (string)p.place);*/
+
+ Braid b=this.copy();
+ b.places[i]=q;
+ b.places[j]=p;
+ return b;
+ }
+ }
+
+ // Tests if the component at index 'start' can be moved to index 'end'
+ // without interfering with other components.
+ bool moveable(int start, int end) {
+ assert(start<places.length); assert(end<places.length);
+ if (start==end)
+ return true;
+ else if (end<start)
+ return moveable(end,start);
+ else {
+ assert(start<end);
+ Placement p=places[start].copy();
+ for (int step=start; step<end; ++step) {
+ Placement q=places[step+1];
+ if (q.place + q.c.in <= p.place)
+ // q is left of p - adjust for q renumbering strings.
+ p.place+=q.c.out-q.c.in;
+ else if (p.place + p.c.out <= q.place)
+ // q is right of p - nothing to do.
+ continue;
+ else
+ // q is directly on top of p
+ return false;
+ }
+ return true;
+ }
+ }
+
+ bool matchComponent(Braid sub, int subindex, int place, int step) {
+ int i=subindex;
+ return sub.places[i].c == this.places[step].c &&
+ sub.places[i].place + place == this.places[step].place;
+ }
+
+ // Returns true if a sub-braid occurs within the one at the specified
+ // coordinates with no component occuring anywhere inbetween.
+ bool exactMatch(Braid sub, int place, int step) {
+ for (int i=0; i<sub.places.length; ++i) {
+ if (!matchComponent(sub, i, place, i+step)) {
+ write("match failed at iteration: ", i);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ bool findSubsequence(Braid sub, int place, int size, int[] acc) {
+ // If we've matched all the components, we've won.
+ if (acc.length >= sub.places.length)
+ return true;
+
+ // The next component to match.
+ Placement p=sub.places[acc.length];
+
+ // Start looking immediately after the last match.
+ for (int step=acc[acc.length-1]+1; step<this.places.length; ++step) {
+ Placement q=this.places[step];
+ */
+
+ bool tryMatch(Braid sub, int place, int size, int[] acc) {
+ // If we've matched all the components, we've won.
+ if (acc.length >= sub.places.length)
+ return true;
+
+ // The next component to match.
+ Placement p=sub.places[acc.length];
+
+ // Start looking immediately after the last match.
+ for (int step=acc[acc.length-1]+1; step<this.places.length; ++step) {
+ Placement q=this.places[step];
+ // Check if the next component is in the set of strings used by the
+ // subbraid.
+ if (q.place + q.c.in > place && q.place < place + size) {
+ // It's in the window, so it must match the next component in the
+ // subbraid.
+ if (p.c==q.c && p.place+place==q.place) {
+ // A match - go on to the next component.
+ acc.push(step);
+ return tryMatch(sub, place, size, acc); // TODO: Adjust place/size.
+ }
+ else
+ return false;
+ }
+
+ // TODO: Adjust place and size.
+ }
+
+ // We've run out of components to match.
+ return false;
+ }
+
+
+ // This attempts to find a subbraid within the braid. It allows other
+ // components to be interspersed with the components of the subbraid so long
+ // as they don't occur on the same string as the ones the subbraid lies on.
+ // Returns null on failure.
+ int[] match(Braid sub, int place) {
+ for (int i=0; i<=this.places.length-sub.places.length; ++i) {
+ // Find where the first component of the subbraid matches and try to
+ // match the rest of the braid starting from there.
+ if (matchComponent(sub, 0, place, i)) {
+ int[] result;
+ result.push(i);
+ if (tryMatch(sub,place,sub.n,result))
+ return result;
+ }
+ }
+ return null;
+ }
+
+ // Equations {{{2
+ // Returns the string that 'place' moves to when going through the section
+ // with Placement p.
+ static int advancePast(Placement p, int place) {
+ // If it's to the left of the component, it is unaffected.
+ return place<p.place ? place :
+ // If it's to the right of the component, adjust the numbering due
+ // to the change of the number of strings in the component.
+ p.place+p.c.in <= place ? place - p.c.in + p.c.out :
+ // If it's in the component, ask the component to do the work.
+ p.place + p.c.connections[place-p.place];
+ }
+
+ // Adjust the place (at step 0) to the step given, to find which string it is
+ // on in that part of the diagram.
+ int advanceToStep(int step, int place) {
+ assert(place>=0 && place<n);
+ assert(step>=0 && step<places.length);
+
+ for (int i=0; i<step; ++i)
+ place=advancePast(places[i], place);
+
+ return place;
+ }
+
+ int pullbackWindowPlace(int step, int place,
+ int w_place, int w_size) {
+ place=advanceToStep(step,place);
+ return place < w_place ? 1 : // The shielding.
+ w_place + w_size <= place ? 0 : // The string doesn't touch it.
+ place-w_place+2;
+ }
+
+ int pullbackPlace(int step, int place) {
+ // Move to the right step.
+ //write("advance: ", step, place, advanceToStep(step,place));
+ //place=advanceToStep(step,place);
+ Placement p=places[step];
+ return pullbackWindowPlace(step,place, p.place, p.c.in);
+ /*return place < p.place ? 1 : // The shielding.
+ p.place + p.c.in <= place ? 0 : // The string doesn't touch it.
+ place-p.place+2;*/
+ }
+
+ int[] pullbackWindow(int step, int w_place, int w_size) {
+ int[] a={1};
+ for (int place=0; place<n; ++place)
+ a.push(pullbackWindowPlace(step, place, w_place, w_size));
+ return a;
+ }
+
+ int[] pullback(int step) {
+ Placement p=places[step];
+ return pullbackWindow(step, p.place, p.c.in);
+ /*int[] a={1};
+ for (int place=0; place<n; ++place)
+ a.push(pullbackPlace(step, place));
+ return a;*/
+ }
+
+ string stepToFormula(int step) {
+ // Determine the pullbacks.
+ string s="(1";
+ for (int place=0; place<n; ++place)
+ //write("pullback: ", step, place, pullbackString(step,place));
+ s+=(string)pullbackPlace(step, place);
+ s+=")^\star "+places[step].c.symbol;
+ return s;
+ }
+
+ // Write it as a formula with pullback notation.
+ string toFormula() {
+ if (places.length==0)
+ return "1";
+ else {
+ string s;
+ for (int step=0; step<places.length; ++step) {
+ if (step>0)
+ s+=" ";
+ s+=stepToFormula(step);
+ }
+ return s;
+ }
+ }
+
+ string windowToLinear(int step, int w_place, int w_size) {
+ int[] a=pullbackWindow(step, w_place, w_size);
+ string s="(";
+ for (int arg=1; arg<=w_size+1; ++arg) {
+ if (arg>1)
+ s+=",";
+ bool first=true;
+ for (int var=0; var<a.length; ++var) {
+ if (a[var]==arg) {
+ if (first)
+ first=false;
+ else
+ s+="+";
+ s+="x_"+(string)(var+1);
+ }
+ }
+ }
+ return s+")";
+ }
+
+ string windowToCode(int step, int w_place, int w_size) {
+ int[] a=pullbackWindow(step, w_place, w_size);
+ string s="[";
+ for (int arg=1; arg<=w_size+1; ++arg) {
+ if (arg>1)
+ s+=", ";
+ bool first=true;
+ for (int var=0; var<a.length; ++var) {
+ if (a[var]==arg) {
+ if (first)
+ first=false;
+ else
+ s+=" + ";
+ s+="x"+(string)(var+1);
+ }
+ }
+ }
+ return s+"]";
+ }
+
+ string stepToLinear(int step) {
+ //int[] a=pullback(step);
+ Placement p=places[step];
+ return p.c.lsym+windowToLinear(step, p.place, p.c.in);
+
+ /*string s=p.c.lsym+"(";
+ for (int arg=1; arg<=p.c.in+1; ++arg) {
+ if (arg>1)
+ s+=",";
+ bool first=true;
+ for (int var=0; var<a.length; ++var) {
+ if (a[var]==arg) {
+ if (first)
+ first=false;
+ else
+ s+="+";
+ s+="x_"+(string)(var+1);
+ }
+ }
+ }
+ return s+")";*/
+ }
+
+ string stepToCode(int step) {
+ Placement p=places[step];
+ return p.c.codename+windowToCode(step, p.place, p.c.in);
+ }
+
+ string toLinear(bool subtract=false) {
+ if (places.length==0)
+ return subtract ? "0" : ""; // or "1" ?
+ else {
+ string s = subtract ? " - " : "";
+ for (int step=0; step<places.length; ++step) {
+ if (step>0)
+ s+= subtract ? " - " : " + ";
+ s+=stepToLinear(step);
+ }
+ return s;
+ }
+ }
+
+ string toCode(bool subtract=false) {
+ if (places.length==0)
+ return subtract ? "0" : ""; // or "1" ?
+ else {
+ string s = subtract ? " - " : "";
+ for (int step=0; step<places.length; ++step) {
+ if (step>0)
+ s+= subtract ? " - " : " + ";
+ s+=stepToCode(step);
+ }
+ return s;
+ }
+ }
+}
+
+struct Relation { // {{{1
+ Braid lhs, rhs;
+
+ string lsym, codename;
+ bool inverted=false;
+
+ string toFormula() {
+ return lhs.toFormula() + " = " + rhs.toFormula();
+ }
+
+ string linearName() {
+ assert(lhs.n==rhs.n);
+ assert(lsym!="");
+
+ string s=(inverted ? "-" : "") + lsym+"(";
+ for (int i=1; i<=lhs.n+1; ++i) {
+ if (i>1)
+ s+=",";
+ s+="x_"+(string)i;
+ }
+ return s+")";
+ }
+
+ string fullCodeName() {
+ assert(lhs.n==rhs.n);
+ assert(codename!="");
+
+ string s=(inverted ? "minus" : "") + codename+"[";
+ for (int i=1; i<=lhs.n+1; ++i) {
+ if (i>1)
+ s+=", ";
+ s+="x"+(string)i+"_";
+ }
+ return s+"]";
+ }
+
+ string toLinear() {
+ return linearName() + " = " + lhs.toLinear() + rhs.toLinear(true);
+ }
+
+ string toCode() {
+ return fullCodeName() + " :> " + lhs.toCode() + rhs.toCode(true);
+ }
+
+ void draw(picture pic=currentpicture) {
+ picture left; lhs.draw(left);
+ frame l=left.fit();
+ picture right; rhs.draw(right);
+ frame r=right.fit();
+
+ real xpad=30;
+
+ add(pic, l);
+ label(pic, "=", (max(l).x + 0.5xpad, 0.25(max(l).y+max(r).y)));
+ add(pic, r, (max(l).x+xpad,0));
+ }
+}
+
+Relation operator- (Relation r) {
+ Relation opposite;
+ opposite.lhs=r.rhs;
+ opposite.rhs=r.lhs;
+ opposite.lsym=r.lsym;
+ opposite.codename=r.codename;
+ opposite.inverted=!r.inverted;
+ return opposite;
+}
+
+
+Braid apply(Relation r, Braid b, int step, int place) {
+ bool valid=b.exactMatch(r.lhs,place,step);
+ if (valid) {
+ Braid result=new Braid;
+ result.n=b.n;
+ for (int i=0; i<step; ++i)
+ result.places.push(b.places[i]);
+ result.add(r.rhs,place);
+ for (int i=step+r.lhs.places.length; i<b.places.length; ++i)
+ result.places.push(b.places[i]);
+ return result;
+ }
+ else {
+ write("Invalid match!");
+ return null;
+ }
+}
+
+// Tableau {{{1
+
+// Draw a number of frames in a nice circular arrangement.
+picture tableau(frame[] cards, bool number=false) {
+ int n=cards.length;
+
+ // Calculate the max height and width of the frames (assuming min(f)=(0,0)).
+ pair M=(0,0);
+ for (int i=0; i<n; ++i) {
+ pair z=max(cards[i]);
+ if (z.x > M.x)
+ M=(z.x,M.y);
+ if (z.y > M.y)
+ M=(M.x,z.y);
+ }
+
+ picture pic;
+ real xpad=2.0, ypad=1.3;
+ void place(int index, real row, real column) {
+ pair z=((M.x*xpad)*column,(M.y*ypad)*row);
+ add(pic, cards[index], z);
+ if (number) {
+ label(pic,(string)index, z+(0.5M.x,0), S);
+ }
+ }
+
+ // Handle small collections.
+ if (n<=4) {
+ for (int i=0; i<n; ++i)
+ place(i,0,i);
+ }
+ else {
+ int rows=quotient(n-1,2), columns=3;
+
+ // Add the top middle card.
+ place(0,rows-1,1);
+
+ // place cards down the right side.
+ for (int i=1; i<rows; ++i)
+ place(i, rows-i,2);
+
+ // place cards at the bottom.
+ if (n%2==0) {
+ place(rows,0,2);
+ place(rows+1,0,1);
+ place(rows+2,0,0);
+ }
+ else {
+ place(rows,0,1.5);
+ place(rows+1,0,0.5);
+ }
+
+ // place cards up the left side.
+ for (int i=1; i<rows; ++i)
+ place(i+n-rows,i,0);
+ }
+
+ return pic;
+}
+
+struct Syzygy { // {{{1
+ // Setup {{{2
+ Braid initial=null;
+ bool cyclic=true;
+ bool showall=false;
+ bool number=false; // Number the diagrams when drawn.
+
+ string lsym, codename;
+
+ bool watched=false;
+ bool uptodate=true;
+
+ struct Move {
+ Braid action(Braid);
+ Relation rel;
+ int place, step;
+ }
+
+ Move[] moves;
+
+ void apply(Relation r, int step, int place) {
+ Move m=new Move;
+ m.rel=r;
+ m.place=place; m.step=step;
+ m.action=new Braid (Braid b) {
+ return apply(r, b, step, place);
+ };
+ moves.push(m);
+
+ uptodate = false;
+ }
+
+ void swap(int i, int j) {
+ Move m=new Move;
+ m.rel=null;
+ m.action=new Braid (Braid b) {
+ return b.swap(i, j);
+ };
+ moves.push(m);
+
+ uptodate = false;
+ }
+
+ // Drawing {{{2
+ picture[] drawMoves() {
+ picture[] pics;
+
+ assert(initial!=null, "must set initial braid");
+ Braid b=initial;
+
+ picture pic;
+ b.draw(pic);
+ pics.push(pic);
+
+ for (int i=0; i<moves.length; ++i) {
+ b=moves[i].action(b);
+ if (showall || moves[i].rel != null) {
+ picture pic;
+ b.draw(pic);
+ pics.push(pic);
+ }
+ }
+
+ // Remove the last picture.
+ if (this.cyclic)
+ pics.pop();
+
+ return pics;
+ }
+
+ void draw(picture pic=currentpicture) {
+ pic.add(tableau(fit(drawMoves()), this.number));
+ }
+
+ void updatefunction() {
+ if (!uptodate) {
+ picture pic; this.draw(pic);
+ shipout(pic);
+ uptodate = true;
+ }
+ }
+
+ void oldupdatefunction() = null;
+
+ void watch() {
+ if (!watched) {
+ watched = true;
+ oldupdatefunction = atupdate();
+ atupdate(this.updatefunction);
+ uptodate = false;
+ }
+ }
+
+ void unwatch() {
+ assert(watched == true);
+ atupdate(oldupdatefunction);
+ uptodate = false;
+ }
+
+ // Writing {{{2
+ string linearName() {
+ assert(lsym!="");
+
+ string s=lsym+"(";
+ for (int i=1; i<=initial.n+1; ++i) {
+ if (i>1)
+ s+=",";
+ s+="x_"+(string)i;
+ }
+ return s+")";
+ }
+
+ string fullCodeName() {
+ assert(codename!="");
+
+ string s=codename+"[";
+ for (int i=1; i<=initial.n+1; ++i) {
+ if (i>1)
+ s+=", ";
+ s+="x"+(string)i+"_";
+ }
+ return s+"]";
+ }
+
+ string toLinear() {
+ string s=linearName()+" = ";
+
+ Braid b=initial;
+ bool first=true;
+ for (int i=0; i<moves.length; ++i) {
+ Move m=moves[i];
+ if (m.rel != null) {
+ if (first) {
+ first=false;
+ if (m.rel.inverted)
+ s+=" - ";
+ }
+ else
+ s+=m.rel.inverted ? " - " : " + ";
+ s+=m.rel.lsym+b.windowToLinear(m.step, m.place, m.rel.lhs.n);
+ }
+ b=m.action(b);
+ }
+
+ return s;
+ }
+
+ string toCode() {
+ string s=fullCodeName()+" :> ";
+
+ Braid b=initial;
+ bool first=true;
+ for (int i=0; i<moves.length; ++i) {
+ Move m=moves[i];
+ if (m.rel != null) {
+ if (first) {
+ first=false;
+ if (m.rel.inverted)
+ s+=" - ";
+ }
+ else
+ s+=m.rel.inverted ? " - " : " + ";
+ s+=m.rel.codename+b.windowToCode(m.step, m.place, m.rel.lhs.n);
+ }
+ b=m.action(b);
+ }
+
+ return s;
+ }
+
+}
+
+// Relation definitions {{{1
+// If you define more relations that you think would be useful, please email
+// them to me, and I'll add them to the script. --Andy.
+Relation r3;
+r3.lhs.n=3;
+r3.lsym="\rho_3"; r3.codename="rho3";
+r3.lhs.add(bp,0); r3.lhs.add(bp,1); r3.lhs.add(bp,0);
+r3.rhs.n=3;
+r3.rhs.add(bp,1); r3.rhs.add(bp,0); r3.rhs.add(bp,1);
+
+Relation r4a;
+r4a.lhs.n=3;
+r4a.lsym="\rho_{4a}"; r4a.codename="rho4a";
+r4a.lhs.add(bp,0); r4a.lhs.add(bp,1); r4a.lhs.add(phi,0);
+r4a.rhs.n=3;
+r4a.rhs.add(phi,1); r4a.rhs.add(bp,0);
+
+Relation r4b;
+r4b.lhs.n=3;
+r4b.lsym="\rho_{4b}"; r4b.codename="rho4b";
+r4b.lhs.add(bp,1); r4b.lhs.add(bp,0); r4b.lhs.add(phi,1);
+r4b.rhs.n=3;
+r4b.rhs.add(phi,0); r4b.rhs.add(bp,0);
+
diff --git a/Master/texmf-dist/asymptote/texcolors.asy b/Master/texmf-dist/asymptote/texcolors.asy
new file mode 100644
index 00000000000..90d9606cb70
--- /dev/null
+++ b/Master/texmf-dist/asymptote/texcolors.asy
@@ -0,0 +1,68 @@
+pen GreenYellow=cmyk(0.15,0,0.69,0);
+pen Yellow=cmyk(0,0,1,0);
+pen Goldenrod=cmyk(0,0.10,0.84,0);
+pen Dandelion=cmyk(0,0.29,0.84,0);
+pen Apricot=cmyk(0,0.32,0.52,0);
+pen Peach=cmyk(0,0.50,0.70,0);
+pen Melon=cmyk(0,0.46,0.50,0);
+pen YellowOrange=cmyk(0,0.42,1,0);
+pen Orange=cmyk(0,0.61,0.87,0);
+pen BurntOrange=cmyk(0,0.51,1,0);
+pen Bittersweet=cmyk(0,0.75,1,0.24);
+pen RedOrange=cmyk(0,0.77,0.87,0);
+pen Mahogany=cmyk(0,0.85,0.87,0.35);
+pen Maroon=cmyk(0,0.87,0.68,0.32);
+pen BrickRed=cmyk(0,0.89,0.94,0.28);
+pen Red=cmyk(0,1,1,0);
+pen OrangeRed=cmyk(0,1,0.50,0);
+pen RubineRed=cmyk(0,1,0.13,0);
+pen WildStrawberry=cmyk(0,0.96,0.39,0);
+pen Salmon=cmyk(0,0.53,0.38,0);
+pen CarnationPink=cmyk(0,0.63,0,0);
+pen Magenta=cmyk(0,1,0,0);
+pen VioletRed=cmyk(0,0.81,0,0);
+pen Rhodamine=cmyk(0,0.82,0,0);
+pen Mulberry=cmyk(0.34,0.90,0,0.02);
+pen RedViolet=cmyk(0.07,0.90,0,0.34);
+pen Fuchsia=cmyk(0.47,0.91,0,0.08);
+pen Lavender=cmyk(0,0.48,0,0);
+pen Thistle=cmyk(0.12,0.59,0,0);
+pen Orchid=cmyk(0.32,0.64,0,0);
+pen DarkOrchid=cmyk(0.40,0.80,0.20,0);
+pen Purple=cmyk(0.45,0.86,0,0);
+pen Plum=cmyk(0.50,1,0,0);
+pen Violet=cmyk(0.79,0.88,0,0);
+pen RoyalPurple=cmyk(0.75,0.90,0,0);
+pen BlueViolet=cmyk(0.86,0.91,0,0.04);
+pen Periwinkle=cmyk(0.57,0.55,0,0);
+pen CadetBlue=cmyk(0.62,0.57,0.23,0);
+pen CornflowerBlue=cmyk(0.65,0.13,0,0);
+pen MidnightBlue=cmyk(0.98,0.13,0,0.43);
+pen NavyBlue=cmyk(0.94,0.54,0,0);
+pen RoyalBlue=cmyk(1,0.50,0,0);
+pen Blue=cmyk(1,1,0,0);
+pen Cerulean=cmyk(0.94,0.11,0,0);
+pen Cyan=cmyk(1,0,0,0);
+pen ProcessBlue=cmyk(0.96,0,0,0);
+pen SkyBlue=cmyk(0.62,0,0.12,0);
+pen Turquoise=cmyk(0.85,0,0.20,0);
+pen TealBlue=cmyk(0.86,0,0.34,0.02);
+pen Aquamarine=cmyk(0.82,0,0.30,0);
+pen BlueGreen=cmyk(0.85,0,0.33,0);
+pen Emerald=cmyk(1,0,0.50,0);
+pen JungleGreen=cmyk(0.99,0,0.52,0);
+pen SeaGreen=cmyk(0.69,0,0.50,0);
+pen Green=cmyk(1,0,1,0);
+pen ForestGreen=cmyk(0.91,0,0.88,0.12);
+pen PineGreen=cmyk(0.92,0,0.59,0.25);
+pen LimeGreen=cmyk(0.50,0,1,0);
+pen YellowGreen=cmyk(0.44,0,0.74,0);
+pen SpringGreen=cmyk(0.26,0,0.76,0);
+pen OliveGreen=cmyk(0.64,0,0.95,0.40);
+pen RawSienna=cmyk(0,0.72,1,0.45);
+pen Sepia=cmyk(0,0.83,1,0.70);
+pen Brown=cmyk(0,0.81,1,0.60);
+pen Tan=cmyk(0.14,0.42,0.56,0);
+pen Gray=cmyk(0,0,0,0.50);
+pen Black=cmyk(0,0,0,1);
+pen White=cmyk(0,0,0,0);
diff --git a/Master/texmf-dist/asymptote/three.asy b/Master/texmf-dist/asymptote/three.asy
new file mode 100644
index 00000000000..5d7ae1a8ba5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/three.asy
@@ -0,0 +1,3319 @@
+private import math;
+
+if(inXasyMode) settings.render=0;
+
+if(prc0()) {
+ if(settings.tex == "context") settings.prc=false;
+ else {
+ access embed;
+ Embed=embed.embed;
+ Link=embed.link;
+ }
+}
+
+// Useful lossy compression values.
+restricted real Zero=0;
+restricted real Low=0.0001;
+restricted real Medium=0.001;
+restricted real High=0.01;
+
+restricted int PRCsphere=0; // Renders slowly but produces smaller PRC files.
+restricted int NURBSsphere=1; // Renders fast but produces larger PRC files.
+
+struct render
+{
+ // PRC parameters:
+ real compression; // lossy compression parameter (0=no compression)
+ real granularity; // PRC rendering granularity
+
+ bool closed; // use one-sided rendering?
+ bool tessellate; // use tessellated mesh to store straight patches?
+
+ bool3 merge; // merge nodes before rendering, for faster but
+ // lower quality PRC rendering (the value default means
+ // merge opaque patches only).
+
+ int sphere; // PRC sphere type (PRCsphere or NURBSsphere).
+
+ // General parameters:
+ real margin; // shrink amount for rendered openGL viewport, in bp.
+ real tubegranularity; // granularity for rendering tubes
+ bool labelfill; // fill subdivision cracks in unlighted labels
+
+ static render defaultrender;
+
+ void operator init(real compression=defaultrender.compression,
+ real granularity=defaultrender.granularity,
+ bool closed=defaultrender.closed,
+ bool tessellate=defaultrender.tessellate,
+ bool3 merge=defaultrender.merge,
+ int sphere=defaultrender.sphere,
+ real margin=defaultrender.margin,
+ real tubegranularity=defaultrender.tubegranularity,
+ bool labelfill=defaultrender.labelfill)
+ {
+ this.compression=compression;
+ this.granularity=granularity;
+ this.closed=closed;
+ this.tessellate=tessellate;
+ this.merge=merge;
+ this.sphere=sphere;
+ this.margin=margin;
+ this.tubegranularity=tubegranularity;
+ this.labelfill=labelfill;
+ }
+}
+
+render operator init() {return render();}
+
+render defaultrender=render.defaultrender=new render;
+defaultrender.compression=High;
+defaultrender.granularity=Medium;
+defaultrender.closed=false;
+defaultrender.tessellate=false;
+defaultrender.merge=false;
+defaultrender.margin=0.02;
+defaultrender.tubegranularity=0.005;
+defaultrender.sphere=NURBSsphere;
+defaultrender.labelfill=true;
+
+real defaultshininess=0.25;
+
+real angleprecision=1e-5; // Precision for centering perspective projections.
+int maxangleiterations=25;
+
+string defaultembed3Doptions;
+string defaultembed3Dscript;
+real defaulteyetoview=63mm/1000mm;
+
+string partname(int i=0)
+{
+ return string(i+1);
+}
+
+triple O=(0,0,0);
+triple X=(1,0,0), Y=(0,1,0), Z=(0,0,1);
+
+// A translation in 3D space.
+transform3 shift(triple v)
+{
+ transform3 t=identity(4);
+ t[0][3]=v.x;
+ t[1][3]=v.y;
+ t[2][3]=v.z;
+ return t;
+}
+
+// Avoid two parentheses.
+transform3 shift(real x, real y, real z)
+{
+ return shift((x,y,z));
+}
+
+transform3 shift(transform3 t)
+{
+ transform3 T=identity(4);
+ T[0][3]=t[0][3];
+ T[1][3]=t[1][3];
+ T[2][3]=t[2][3];
+ return T;
+}
+
+// A 3D scaling in the x direction.
+transform3 xscale3(real x)
+{
+ transform3 t=identity(4);
+ t[0][0]=x;
+ return t;
+}
+
+// A 3D scaling in the y direction.
+transform3 yscale3(real y)
+{
+ transform3 t=identity(4);
+ t[1][1]=y;
+ return t;
+}
+
+// A 3D scaling in the z direction.
+transform3 zscale3(real z)
+{
+ transform3 t=identity(4);
+ t[2][2]=z;
+ return t;
+}
+
+// A 3D scaling by s in the v direction.
+transform3 scale(triple v, real s)
+{
+ v=unit(v);
+ s -= 1;
+ return new real[][] {
+ {1+s*v.x^2, s*v.x*v.y, s*v.x*v.z, 0},
+ {s*v.x*v.y, 1+s*v.y^2, s*v.y*v.z, 0},
+ {s*v.x*v.z, s*v.y*v.z, 1+s*v.z^2, 0},
+ {0, 0, 0, 1}};
+}
+
+// A transformation representing rotation by an angle in degrees about
+// an axis v through the origin (in the right-handed direction).
+transform3 rotate(real angle, triple v)
+{
+ if(v == O) abort("cannot rotate about the zero vector");
+ v=unit(v);
+ real x=v.x, y=v.y, z=v.z;
+ real s=Sin(angle), c=Cos(angle), t=1-c;
+
+ return new real[][] {
+ {t*x^2+c, t*x*y-s*z, t*x*z+s*y, 0},
+ {t*x*y+s*z, t*y^2+c, t*y*z-s*x, 0},
+ {t*x*z-s*y, t*y*z+s*x, t*z^2+c, 0},
+ {0, 0, 0, 1}};
+}
+
+// A transformation representing rotation by an angle in degrees about
+// the line u--v (in the right-handed direction).
+transform3 rotate(real angle, triple u, triple v)
+{
+ return shift(u)*rotate(angle,v-u)*shift(-u);
+}
+
+// Reflects about the plane through u, v, and w.
+transform3 reflect(triple u, triple v, triple w)
+{
+ triple n=unit(cross(v-u,w-u));
+ if(n == O)
+ abort("points determining reflection plane cannot be colinear");
+
+ return new real[][] {
+ {1-2*n.x^2, -2*n.x*n.y, -2*n.x*n.z, u.x},
+ {-2*n.x*n.y, 1-2*n.y^2, -2*n.y*n.z, u.y},
+ {-2*n.x*n.z, -2*n.y*n.z, 1-2*n.z^2, u.z},
+ {0, 0, 0, 1}
+ }*shift(-u);
+}
+
+// Project u onto v.
+triple project(triple u, triple v)
+{
+ v=unit(v);
+ return dot(u,v)*v;
+}
+
+// Return a unit vector perpendicular to a given unit vector v.
+triple perp(triple v)
+{
+ triple u=cross(v,Y);
+ if(abs(u) > sqrtEpsilon) return unit(u);
+ u=cross(v,Z);
+ return (abs(u) > sqrtEpsilon) ? unit(u) : X;
+}
+
+// Return the transformation corresponding to moving the camera from the target
+// (looking in the negative z direction) to the point 'eye' (looking at target),
+// orienting the camera so that direction 'up' points upwards.
+// Since, in actuality, we are transforming the points instead of the camera,
+// we calculate the inverse matrix.
+// Based on the gluLookAt implementation in the OpenGL manual.
+transform3 look(triple eye, triple up=Z, triple target=O)
+{
+ triple f=unit(target-eye);
+ if(f == O)
+ f=-Z; // The eye is already at the origin: look down.
+
+ triple s=cross(f,up);
+
+ // If the eye is pointing either directly up or down, there is no
+ // preferred "up" direction. Pick one arbitrarily.
+ s=s != O ? unit(s) : perp(f);
+
+ triple u=cross(s,f);
+
+ transform3 M={{ s.x, s.y, s.z, 0},
+ { u.x, u.y, u.z, 0},
+ {-f.x, -f.y, -f.z, 0},
+ { 0, 0, 0, 1}};
+
+ return M*shift(-eye);
+}
+
+// Return a matrix to do perspective distortion based on a triple v.
+transform3 distort(triple v)
+{
+ transform3 t=identity(4);
+ real d=length(v);
+ if(d == 0) return t;
+ t[3][2]=-1/d;
+ t[3][3]=0;
+ return t;
+}
+
+projection operator * (transform3 t, projection P)
+{
+ projection P=P.copy();
+ if(!P.absolute) {
+ P.camera=t*P.camera;
+ triple target=P.target;
+ P.target=t*P.target;
+ if(P.infinity)
+ P.normal=t*(target+P.normal)-P.target;
+ else
+ P.normal=P.vector();
+ P.calculate();
+ }
+ return P;
+}
+
+// With this, save() and restore() in plain also save and restore the
+// currentprojection.
+addSaveFunction(new restoreThunk() {
+ projection P=currentprojection.copy();
+ return new void() {
+ currentprojection=P;
+ };
+ });
+
+pair project(triple v, projection P=currentprojection)
+{
+ return project(v,P.t);
+}
+
+pair dir(triple v, triple dir, projection P)
+{
+ return unit(project(v+0.5dir,P)-project(v-0.5*dir,P));
+}
+
+// Uses the homogenous coordinate to perform perspective distortion.
+// When combined with a projection to the XY plane, this effectively maps
+// points in three space to a plane through target and
+// perpendicular to the vector camera-target.
+projection perspective(triple camera, triple up=Z, triple target=O,
+ real zoom=1, real angle=0, pair viewportshift=0,
+ bool showtarget=true, bool autoadjust=true,
+ bool center=autoadjust)
+{
+ if(camera == target)
+ abort("camera cannot be at target");
+ return projection(camera,up,target,zoom,angle,viewportshift,
+ showtarget,autoadjust,center,
+ new transformation(triple camera, triple up, triple target)
+ {return transformation(look(camera,up,target),
+ distort(camera-target));});
+}
+
+projection perspective(real x, real y, real z, triple up=Z, triple target=O,
+ real zoom=1, real angle=0, pair viewportshift=0,
+ bool showtarget=true, bool autoadjust=true,
+ bool center=autoadjust)
+{
+ return perspective((x,y,z),up,target,zoom,angle,viewportshift,showtarget,
+ autoadjust,center);
+}
+
+projection orthographic(triple camera, triple up=Z, triple target=O,
+ real zoom=1, pair viewportshift=0,
+ bool showtarget=true, bool center=false)
+{
+ return projection(camera,up,target,zoom,viewportshift,showtarget,
+ center=center,new transformation(triple camera, triple up,
+ triple target) {
+ return transformation(look(camera,up,target));});
+}
+
+projection orthographic(real x, real y, real z, triple up=Z,
+ triple target=O, real zoom=1, pair viewportshift=0,
+ bool showtarget=true, bool center=false)
+{
+ return orthographic((x,y,z),up,target,zoom,viewportshift,showtarget,
+ center=center);
+}
+
+// Compute camera position with x axis below the horizontal at angle alpha,
+// y axis below the horizontal at angle beta, and z axis up.
+triple camera(real alpha, real beta)
+{
+ real denom=Tan(alpha+beta);
+ real Tanalpha=Tan(alpha);
+ real Tanbeta=Tan(beta);
+ return (sqrt(Tanalpha/denom),sqrt(Tanbeta/denom),sqrt(Tanalpha*Tanbeta));
+}
+
+projection oblique(real angle=45)
+{
+ transform3 t=identity(4);
+ real c2=Cos(angle)^2;
+ real s2=1-c2;
+ t[0][2]=-c2;
+ t[1][2]=-s2;
+ t[2][2]=1;
+ t[2][3]=-1;
+ return projection((c2,s2,1),up=Y,normal=(0,0,1),
+ new transformation(triple,triple,triple) {
+ return transformation(t);});
+}
+
+projection obliqueZ(real angle=45) {return oblique(angle);}
+
+projection obliqueX(real angle=45)
+{
+ transform3 t=identity(4);
+ real c2=Cos(angle)^2;
+ real s2=1-c2;
+ t[0][0]=-c2;
+ t[1][0]=-s2;
+ t[1][1]=0;
+ t[0][1]=1;
+ t[1][2]=1;
+ t[2][2]=0;
+ t[2][0]=1;
+ t[2][3]=-1;
+ return projection((1,c2,s2),normal=(1,0,0),
+ new transformation(triple,triple,triple) {
+ return transformation(t);});
+}
+
+projection obliqueY(real angle=45)
+{
+ transform3 t=identity(4);
+ real c2=Cos(angle)^2;
+ real s2=1-c2;
+ t[0][1]=c2;
+ t[1][1]=s2;
+ t[1][2]=1;
+ t[2][1]=-1;
+ t[2][2]=0;
+ t[2][3]=-1;
+ return projection((c2,-1,s2),normal=(0,-1,0),
+ new transformation(triple,triple,triple) {
+ return transformation(t);});
+}
+
+projection oblique=oblique();
+projection obliqueX=obliqueX(), obliqueY=obliqueY(), obliqueZ=obliqueZ();
+
+projection LeftView=orthographic(-X,showtarget=true);
+projection RightView=orthographic(X,showtarget=true);
+projection FrontView=orthographic(-Y,showtarget=true);
+projection BackView=orthographic(Y,showtarget=true);
+projection BottomView=orthographic(-Z,up=-Y,showtarget=true);
+projection TopView=orthographic(Z,up=Y,showtarget=true);
+
+currentprojection=perspective(5,4,2);
+
+projection projection()
+{
+ projection P;
+ real[] a=_projection();
+ if(a.length == 0 || a[10] == 0.0) return currentprojection;
+ int k=0;
+ return a[0] == 1 ?
+ orthographic((a[++k],a[++k],a[++k]),(a[++k],a[++k],a[++k]),
+ (a[++k],a[++k],a[++k]),a[++k],(a[k += 2],a[++k])) :
+ perspective((a[++k],a[++k],a[++k]),(a[++k],a[++k],a[++k]),
+ (a[++k],a[++k],a[++k]),a[++k],a[++k],(a[++k],a[++k]));
+}
+
+// Map pair z to a triple by inverting the projection P onto the
+// plane perpendicular to normal and passing through point.
+triple invert(pair z, triple normal, triple point,
+ projection P=currentprojection)
+{
+ transform3 t=P.t;
+ real[][] A={{t[0][0]-z.x*t[3][0],t[0][1]-z.x*t[3][1],t[0][2]-z.x*t[3][2]},
+ {t[1][0]-z.y*t[3][0],t[1][1]-z.y*t[3][1],t[1][2]-z.y*t[3][2]},
+ {normal.x,normal.y,normal.z}};
+ real[] b={z.x*t[3][3]-t[0][3],z.y*t[3][3]-t[1][3],dot(normal,point)};
+ real[] x=solve(A,b,warn=false);
+ return x.length > 0 ? (x[0],x[1],x[2]) : P.camera;
+}
+
+// Map pair to a triple on the projection plane.
+triple invert(pair z, projection P=currentprojection)
+{
+ return invert(z,P.normal,P.target,P);
+}
+
+// Map pair dir to a triple direction at point v on the projection plane.
+triple invert(pair dir, triple v, projection P=currentprojection)
+{
+ return invert(project(v,P)+dir,P.normal,v,P)-v;
+}
+
+pair xypart(triple v)
+{
+ return (v.x,v.y);
+}
+
+struct control {
+ triple post,pre;
+ bool active=false;
+ bool straight=true;
+ void operator init(triple post, triple pre, bool straight=false) {
+ this.post=post;
+ this.pre=pre;
+ active=true;
+ this.straight=straight;
+ }
+}
+
+control nocontrol;
+
+control operator * (transform3 t, control c)
+{
+ control C;
+ C.post=t*c.post;
+ C.pre=t*c.pre;
+ C.active=c.active;
+ C.straight=c.straight;
+ return C;
+}
+
+void write(file file, control c)
+{
+ write(file,".. controls ");
+ write(file,c.post);
+ write(file," and ");
+ write(file,c.pre);
+}
+
+struct Tension {
+ real out,in;
+ bool atLeast;
+ bool active;
+ void operator init(real out=1, real in=1, bool atLeast=false,
+ bool active=true) {
+ real check(real val) {
+ if(val < 0.75) abort("tension cannot be less than 3/4");
+ return val;
+ }
+ this.out=check(out);
+ this.in=check(in);
+ this.atLeast=atLeast;
+ this.active=active;
+ }
+}
+
+Tension operator init()
+{
+ return Tension();
+}
+
+Tension noTension;
+noTension.active=false;
+
+void write(file file, Tension t)
+{
+ write(file,"..tension ");
+ if(t.atLeast) write(file,"atleast ");
+ write(file,t.out);
+ write(file," and ");
+ write(file,t.in);
+}
+
+struct dir {
+ triple dir;
+ real gamma=1; // endpoint curl
+ bool Curl; // curl specified
+ bool active() {
+ return dir != O || Curl;
+ }
+ void init(triple v) {
+ this.dir=v;
+ }
+ void init(real gamma) {
+ if(gamma < 0) abort("curl cannot be less than 0");
+ this.gamma=gamma;
+ this.Curl=true;
+ }
+ void init(dir d) {
+ dir=d.dir;
+ gamma=d.gamma;
+ Curl=d.Curl;
+ }
+ void default(triple v) {
+ if(!active()) init(v);
+ }
+ void default(dir d) {
+ if(!active()) init(d);
+ }
+ dir copy() {
+ dir d=new dir;
+ d.init(this);
+ return d;
+ }
+}
+
+void write(file file, dir d)
+{
+ if(d.dir != O) {
+ write(file,"{"); write(file,unit(d.dir)); write(file,"}");
+ } else if(d.Curl) {
+ write(file,"{curl "); write(file,d.gamma); write(file,"}");
+ }
+}
+
+dir operator * (transform3 t, dir d)
+{
+ dir D=d.copy();
+ D.init(unit(shiftless(t)*d.dir));
+ return D;
+}
+
+void checkEmpty(int n) {
+ if(n == 0)
+ abort("nullpath3 has no points");
+}
+
+int adjustedIndex(int i, int n, bool cycles)
+{
+ checkEmpty(n);
+ if(cycles)
+ return i % n;
+ else if(i < 0)
+ return 0;
+ else if(i >= n)
+ return n-1;
+ else
+ return i;
+}
+
+struct flatguide3 {
+ triple[] nodes;
+ bool[] cyclic; // true if node is really a cycle
+ control[] control; // control points for segment starting at node
+ Tension[] Tension; // Tension parameters for segment starting at node
+ dir[] in,out; // in and out directions for segment starting at node
+
+ bool cyclic() {int n=cyclic.length; return n > 0 ? cyclic[n-1] : false;}
+ bool precyclic() {int i=find(cyclic); return i >= 0 && i < cyclic.length-1;}
+
+ int size() {
+ return cyclic() ? nodes.length-1 : nodes.length;
+ }
+
+ void node(triple v, bool b=false) {
+ nodes.push(v);
+ control.push(nocontrol);
+ Tension.push(noTension);
+ in.push(new dir);
+ out.push(new dir);
+ cyclic.push(b);
+ }
+
+ void control(triple post, triple pre) {
+ if(control.length > 0) {
+ control c=control(post,pre,false);
+ control[control.length-1]=c;
+ }
+ }
+
+ void Tension(real out, real in, bool atLeast) {
+ if(Tension.length > 0)
+ Tension[Tension.length-1]=Tension(out,in,atLeast,true);
+ }
+
+ void in(triple v) {
+ if(in.length > 0) {
+ in[in.length-1].init(v);
+ }
+ }
+
+ void out(triple v) {
+ if(out.length > 0) {
+ out[out.length-1].init(v);
+ }
+ }
+
+ void in(real gamma) {
+ if(in.length > 0) {
+ in[in.length-1].init(gamma);
+ }
+ }
+
+ void out(real gamma) {
+ if(out.length > 0) {
+ out[out.length-1].init(gamma);
+ }
+ }
+
+ void cycleToken() {
+ if(nodes.length > 0)
+ node(nodes[0],true);
+ }
+
+ // Return true if outgoing direction at node i is known.
+ bool solved(int i) {
+ return out[i].active() || control[i].active;
+ }
+}
+
+void write(file file, string s="", explicit flatguide3 x, suffix suffix=none)
+{
+ write(file,s);
+ if(x.size() == 0) write(file,"<nullpath3>");
+ else for(int i=0; i < x.nodes.length; ++i) {
+ if(i > 0) write(file,endl);
+ if(x.cyclic[i]) write(file,"cycle");
+ else write(file,x.nodes[i]);
+ if(i < x.nodes.length-1) {
+ // Explicit control points trump other specifiers
+ if(x.control[i].active)
+ write(file,x.control[i]);
+ else {
+ write(file,x.out[i]);
+ if(x.Tension[i].active) write(file,x.Tension[i]);
+ }
+ write(file,"..");
+ if(!x.control[i].active) write(file,x.in[i]);
+ }
+ }
+ write(file,suffix);
+}
+
+void write(string s="", flatguide3 x, suffix suffix=endl)
+{
+ write(stdout,s,x,suffix);
+}
+
+// A guide3 is most easily represented as something that modifies a flatguide3.
+typedef void guide3(flatguide3);
+
+restricted void nullpath3(flatguide3) {};
+
+guide3 operator init() {return nullpath3;}
+
+guide3 operator cast(triple v)
+{
+ return new void(flatguide3 f) {
+ f.node(v);
+ };
+}
+
+guide3 operator cast(cycleToken) {
+ return new void(flatguide3 f) {
+ f.cycleToken();
+ };
+}
+
+guide3 operator controls(triple post, triple pre)
+{
+ return new void(flatguide3 f) {
+ f.control(post,pre);
+ };
+};
+
+guide3 operator controls(triple v)
+{
+ return operator controls(v,v);
+}
+
+guide3 operator cast(tensionSpecifier t)
+{
+ return new void(flatguide3 f) {
+ f.Tension(t.out, t.in, t.atLeast);
+ };
+}
+
+guide3 operator cast(curlSpecifier spec)
+{
+ return new void(flatguide3 f) {
+ if(spec.side == JOIN_OUT) f.out(spec.value);
+ else if(spec.side == JOIN_IN) f.in(spec.value);
+ else
+ abort("invalid curl specifier");
+ };
+}
+
+guide3 operator spec(triple v, int side)
+{
+ return new void(flatguide3 f) {
+ if(side == JOIN_OUT) f.out(v);
+ else if(side == JOIN_IN) f.in(v);
+ else
+ abort("invalid direction specifier");
+ };
+}
+
+guide3 operator -- (... guide3[] g)
+{
+ return new void(flatguide3 f) {
+ if(g.length > 0) {
+ for(int i=0; i < g.length-1; ++i) {
+ g[i](f);
+ f.out(1);
+ f.in(1);
+ }
+ g[g.length-1](f);
+ }
+ };
+}
+
+guide3 operator .. (... guide3[] g)
+{
+ return new void(flatguide3 f) {
+ for(int i=0; i < g.length; ++i)
+ g[i](f);
+ };
+}
+
+typedef guide3 interpolate3(... guide3[]);
+
+interpolate3 join3(tensionSpecifier t)
+{
+ return new guide3(... guide3[] a) {
+ if(a.length == 0) return nullpath3;
+ guide3 g=a[0];
+ for(int i=1; i < a.length; ++i)
+ g=g..t..a[i];
+ return g;
+ };
+}
+
+interpolate3 operator ::=join3(operator tension(1,true));
+interpolate3 operator ---=join3(operator tension(infinity,true));
+
+flatguide3 operator cast(guide3 g)
+{
+ flatguide3 f;
+ g(f);
+ return f;
+}
+
+flatguide3[] operator cast(guide3[] g)
+{
+ flatguide3[] p=new flatguide3[g.length];
+ for(int i=0; i < g.length; ++i) {
+ flatguide3 f;
+ g[i](f);
+ p[i]=f;
+ }
+ return p;
+}
+
+// A version of asin that tolerates numerical imprecision
+real asin1(real x)
+{
+ return asin(min(max(x,-1),1));
+}
+
+// A version of acos that tolerates numerical imprecision
+real acos1(real x)
+{
+ return acos(min(max(x,-1),1));
+}
+
+struct Controls {
+ triple c0,c1;
+
+ // 3D extension of John Hobby's control point formula
+ // (cf. The MetaFont Book, page 131),
+ // as described in John C. Bowman and A. Hammerlindl,
+ // TUGBOAT: The Communications of the TeX Users Group 29:2 (2008).
+
+ void operator init(triple v0, triple v1, triple d0, triple d1, real tout,
+ real tin, bool atLeast) {
+ triple v=v1-v0;
+ triple u=unit(v);
+ real L=length(v);
+ d0=unit(d0);
+ d1=unit(d1);
+ real theta=acos1(dot(d0,u));
+ real phi=acos1(dot(d1,u));
+ if(dot(cross(d0,v),cross(v,d1)) < 0) phi=-phi;
+ c0=v0+d0*L*relativedistance(theta,phi,tout,atLeast);
+ c1=v1-d1*L*relativedistance(phi,theta,tin,atLeast);
+ }
+}
+
+private triple cross(triple d0, triple d1, triple reference)
+{
+ triple normal=cross(d0,d1);
+ return normal == O ? reference : normal;
+}
+
+private triple dir(real theta, triple d0, triple d1, triple reference)
+{
+ triple normal=cross(d0,d1,reference);
+ if(normal == O) return d1;
+ return rotate(degrees(theta),dot(normal,reference) >= 0 ? normal : -normal)*
+ d1;
+}
+
+private real angle(triple d0, triple d1, triple reference)
+{
+ real theta=acos1(dot(unit(d0),unit(d1)));
+ return dot(cross(d0,d1,reference),reference) >= 0 ? theta : -theta;
+}
+
+// 3D extension of John Hobby's angle formula (The MetaFont Book, page 131).
+// Notational differences: here psi[i] is the turning angle at z[i+1],
+// beta[i] is the tension for segment i, and in[i] is the incoming
+// direction for segment i (where segment i begins at node i).
+
+real[] theta(triple[] v, real[] alpha, real[] beta,
+ triple dir0, triple dirn, real g0, real gn, triple reference)
+{
+ real[] a,b,c,f,l,psi;
+ int n=alpha.length;
+ bool cyclic=v.cyclic;
+ for(int i=0; i < n; ++i)
+ l[i]=1/length(v[i+1]-v[i]);
+ int i0,in;
+ if(cyclic) {i0=0; in=n;}
+ else {i0=1; in=n-1;}
+ for(int i=0; i < in; ++i)
+ psi[i]=angle(v[i+1]-v[i],v[i+2]-v[i+1],reference);
+ if(cyclic) {
+ l.cyclic=true;
+ psi.cyclic=true;
+ } else {
+ psi[n-1]=0;
+ if(dir0 == O) {
+ real a0=alpha[0];
+ real b0=beta[0];
+ real chi=g0*(b0/a0)^2;
+ a[0]=0;
+ b[0]=3a0-a0/b0+chi;
+ real C=chi*(3a0-1)+a0/b0;
+ c[0]=C;
+ f[0]=-C*psi[0];
+ } else {
+ a[0]=c[0]=0;
+ b[0]=1;
+ f[0]=angle(v[1]-v[0],dir0,reference);
+ }
+ if(dirn == O) {
+ real an=alpha[n-1];
+ real bn=beta[n-1];
+ real chi=gn*(an/bn)^2;
+ a[n]=chi*(3bn-1)+bn/an;
+ b[n]=3bn-bn/an+chi;
+ c[n]=f[n]=0;
+ } else {
+ a[n]=c[n]=0;
+ b[n]=1;
+ f[n]=angle(v[n]-v[n-1],dirn,reference);
+ }
+ }
+
+ for(int i=i0; i < n; ++i) {
+ real in=beta[i-1]^2*l[i-1];
+ real A=in/alpha[i-1];
+ a[i]=A;
+ real B=3*in-A;
+ real out=alpha[i]^2*l[i];
+ real C=out/beta[i];
+ b[i]=B+3*out-C;
+ c[i]=C;
+ f[i]=-B*psi[i-1]-C*psi[i];
+ }
+
+ return tridiagonal(a,b,c,f);
+}
+
+triple reference(triple[] v, int n, triple d0, triple d1)
+{
+ triple[] V=sequence(new triple(int i) {
+ return cross(v[i+1]-v[i],v[i+2]-v[i+1]);
+ },n-1);
+ if(n > 0) {
+ V.push(cross(d0,v[1]-v[0]));
+ V.push(cross(v[n]-v[n-1],d1));
+ }
+
+ triple max=V[0];
+ real M=abs(max);
+ for(int i=1; i < V.length; ++i) {
+ triple vi=V[i];
+ real a=abs(vi);
+ if(a > M) {
+ M=a;
+ max=vi;
+ }
+ }
+
+ triple reference;
+ for(int i=0; i < V.length; ++i) {
+ triple u=unit(V[i]);
+ reference += dot(u,max) < 0 ? -u : u;
+ }
+
+ return reference;
+}
+
+// Fill in missing directions for n cyclic nodes.
+void aim(flatguide3 g, int N)
+{
+ bool cyclic=true;
+ int start=0, end=0;
+
+ // If the cycle contains one or more direction specifiers, break the loop.
+ for(int k=0; k < N; ++k)
+ if(g.solved(k)) {cyclic=false; end=k; break;}
+ for(int k=N-1; k >= 0; --k)
+ if(g.solved(k)) {cyclic=false; start=k; break;}
+ while(start < N && g.control[start].active) ++start;
+
+ int n=N-(start-end);
+ if(n <= 1 || (cyclic && n <= 2)) return;
+
+ triple[] v=new triple[cyclic ? n : n+1];
+ real[] alpha=new real[n];
+ real[] beta=new real[n];
+ for(int k=0; k < n; ++k) {
+ int K=(start+k) % N;
+ v[k]=g.nodes[K];
+ alpha[k]=g.Tension[K].out;
+ beta[k]=g.Tension[K].in;
+ }
+ if(cyclic) {
+ v.cyclic=true;
+ alpha.cyclic=true;
+ beta.cyclic=true;
+ } else v[n]=g.nodes[(start+n) % N];
+ int final=(end-1) % N;
+
+ triple d0=g.out[start].dir;
+ triple d1=g.in[final].dir;
+
+ triple reference=reference(v,n,d0,d1);
+
+ real[] theta=theta(v,alpha,beta,d0,d1,g.out[start].gamma,g.in[final].gamma,
+ reference);
+
+ v.cyclic=true;
+ theta.cyclic=true;
+
+ for(int k=1; k < (cyclic ? n+1 : n); ++k) {
+ triple w=dir(theta[k],v[k]-v[k-1],v[k+1]-v[k],reference);
+ g.in[(start+k-1) % N].init(w);
+ g.out[(start+k) % N].init(w);
+ }
+
+ if(g.out[start].dir == O)
+ g.out[start].init(dir(theta[0],v[0]-g.nodes[(start-1) % N],v[1]-v[0],
+ reference));
+ if(g.in[final].dir == O)
+ g.in[final].init(dir(theta[n],v[n-1]-v[n-2],v[n]-v[n-1],reference));
+}
+
+// Fill in missing directions for the sequence of nodes i...n.
+void aim(flatguide3 g, int i, int n)
+{
+ int j=n-i;
+ if(j > 1 || g.out[i].dir != O || g.in[i].dir != O) {
+ triple[] v=new triple[j+1];
+ real[] alpha=new real[j];
+ real[] beta=new real[j];
+ for(int k=0; k < j; ++k) {
+ v[k]=g.nodes[i+k];
+ alpha[k]=g.Tension[i+k].out;
+ beta[k]=g.Tension[i+k].in;
+ }
+ v[j]=g.nodes[n];
+
+ triple d0=g.out[i].dir;
+ triple d1=g.in[n-1].dir;
+
+ triple reference=reference(v,j,d0,d1);
+
+ real[] theta=theta(v,alpha,beta,d0,d1,g.out[i].gamma,g.in[n-1].gamma,
+ reference);
+
+ for(int k=1; k < j; ++k) {
+ triple w=dir(theta[k],v[k]-v[k-1],v[k+1]-v[k],reference);
+ g.in[i+k-1].init(w);
+ g.out[i+k].init(w);
+ }
+ if(g.out[i].dir == O) {
+ triple w=dir(theta[0],g.in[i].dir,v[1]-v[0],reference);
+ if(i > 0) g.in[i-1].init(w);
+ g.out[i].init(w);
+ }
+ if(g.in[n-1].dir == O) {
+ triple w=dir(theta[j],g.out[n-1].dir,v[j]-v[j-1],reference);
+ g.in[n-1].init(w);
+ g.out[n].init(w);
+ }
+ }
+}
+
+private real Fuzz=10*realEpsilon;
+
+triple XYplane(pair z) {return (z.x,z.y,0);}
+triple YZplane(pair z) {return (0,z.x,z.y);}
+triple ZXplane(pair z) {return (z.y,0,z.x);}
+
+bool cyclic(guide3 g) {flatguide3 f; g(f); return f.cyclic();}
+int size(guide3 g) {flatguide3 f; g(f); return f.size();}
+int length(guide3 g) {flatguide3 f; g(f); return f.nodes.length-1;}
+
+triple dir(path3 p)
+{
+ return dir(p,length(p));
+}
+
+triple dir(path3 p, path3 h)
+{
+ return 0.5*(dir(p)+dir(h));
+}
+
+// return the point on path3 p at arclength L
+triple arcpoint(path3 p, real L)
+{
+ return point(p,arctime(p,L));
+}
+
+// return the direction on path3 p at arclength L
+triple arcdir(path3 p, real L)
+{
+ return dir(p,arctime(p,L));
+}
+
+// return the time on path3 p at the relative fraction l of its arclength
+real reltime(path3 p, real l)
+{
+ return arctime(p,l*arclength(p));
+}
+
+// return the point on path3 p at the relative fraction l of its arclength
+triple relpoint(path3 p, real l)
+{
+ return point(p,reltime(p,l));
+}
+
+// return the direction of path3 p at the relative fraction l of its arclength
+triple reldir(path3 p, real l)
+{
+ return dir(p,reltime(p,l));
+}
+
+// return the initial point of path3 p
+triple beginpoint(path3 p)
+{
+ return point(p,0);
+}
+
+// return the point on path3 p at half of its arclength
+triple midpoint(path3 p)
+{
+ return relpoint(p,0.5);
+}
+
+// return the final point of path3 p
+triple endpoint(path3 p)
+{
+ return point(p,length(p));
+}
+
+path3 path3(triple v)
+{
+ triple[] point={v};
+ return path3(point,point,point,new bool[] {false},false);
+}
+
+path3 path3(path p, triple plane(pair)=XYplane)
+{
+ int n=size(p);
+ return path3(sequence(new triple(int i) {return plane(precontrol(p,i));},n),
+ sequence(new triple(int i) {return plane(point(p,i));},n),
+ sequence(new triple(int i) {return plane(postcontrol(p,i));},n),
+ sequence(new bool(int i) {return straight(p,i);},n),
+ cyclic(p));
+}
+
+path3[] path3(explicit path[] g, triple plane(pair)=XYplane)
+{
+ return sequence(new path3(int i) {return path3(g[i],plane);},g.length);
+}
+
+path3 invert(path p, triple normal, triple point,
+ projection P=currentprojection)
+{
+ return path3(p,new triple(pair z) {return invert(z,normal,point,P);});
+}
+
+path3 invert(path p, triple point, projection P=currentprojection)
+{
+ return path3(p,new triple(pair z) {return invert(z,P.normal,point,P);});
+}
+
+path3 invert(path p, projection P=currentprojection)
+{
+ return path3(p,new triple(pair z) {return invert(z,P.normal,P.target,P);});
+}
+
+// Construct a path from a path3 by applying P to each control point.
+path path(path3 p, pair P(triple)=xypart)
+{
+ int n=length(p);
+ if(n < 0) return nullpath;
+ guide g=P(point(p,0));
+ if(n == 0) return g;
+ for(int i=1; i < n; ++i)
+ g=straight(p,i-1) ? g--P(point(p,i)) :
+ g..controls P(postcontrol(p,i-1)) and P(precontrol(p,i))..P(point(p,i));
+
+ if(straight(p,n-1))
+ return cyclic(p) ? g--cycle : g--P(point(p,n));
+
+ pair post=P(postcontrol(p,n-1));
+ pair pre=P(precontrol(p,n));
+ return cyclic(p) ? g..controls post and pre..cycle :
+ g..controls post and pre..P(point(p,n));
+}
+
+void write(file file, string s="", explicit path3 x, suffix suffix=none)
+{
+ write(file,s);
+ int n=length(x);
+ if(n < 0) write("<nullpath3>");
+ else {
+ for(int i=0; i < n; ++i) {
+ write(file,point(x,i));
+ if(i < length(x)) {
+ if(straight(x,i)) write(file,"--");
+ else {
+ write(file,".. controls ");
+ write(file,postcontrol(x,i));
+ write(file," and ");
+ write(file,precontrol(x,i+1),newl);
+ write(file," ..");
+ }
+ }
+ }
+ if(cyclic(x))
+ write(file,"cycle",suffix);
+ else
+ write(file,point(x,n),suffix);
+ }
+}
+
+void write(string s="", explicit path3 x, suffix suffix=endl)
+{
+ write(stdout,s,x,suffix);
+}
+
+void write(file file, string s="", explicit path3[] x, suffix suffix=none)
+{
+ write(file,s);
+ if(x.length > 0) write(file,x[0]);
+ for(int i=1; i < x.length; ++i) {
+ write(file,endl);
+ write(file," ^^");
+ write(file,x[i]);
+ }
+ write(file,suffix);
+}
+
+void write(string s="", explicit path3[] x, suffix suffix=endl)
+{
+ write(stdout,s,x,suffix);
+}
+
+path3 solve(flatguide3 g)
+{
+ int n=g.nodes.length-1;
+
+ // If duplicate points occur consecutively, add dummy controls (if absent).
+ for(int i=0; i < n; ++i) {
+ if(g.nodes[i] == g.nodes[i+1] && !g.control[i].active)
+ g.control[i]=control(g.nodes[i],g.nodes[i],straight=true);
+ }
+
+ // Fill in empty direction specifiers inherited from explicit control points.
+ for(int i=0; i < n; ++i) {
+ if(g.control[i].active) {
+ g.out[i].init(g.control[i].post-g.nodes[i]);
+ g.in[i].init(g.nodes[i+1]-g.control[i].pre);
+ }
+ }
+
+ // Propagate directions across nodes.
+ for(int i=0; i < n; ++i) {
+ int next=g.cyclic[i+1] ? 0 : i+1;
+ if(g.out[next].active())
+ g.in[i].default(g.out[next]);
+ if(g.in[i].active()) {
+ g.out[next].default(g.in[i]);
+ g.out[i+1].default(g.in[i]);
+ }
+ }
+
+ // Compute missing 3D directions.
+ // First, resolve cycles
+ int i=find(g.cyclic);
+ if(i > 0) {
+ aim(g,i);
+ // All other cycles can now be reduced to sequences.
+ triple v=g.out[0].dir;
+ for(int j=i; j <= n; ++j) {
+ if(g.cyclic[j]) {
+ g.in[j-1].default(v);
+ g.out[j].default(v);
+ if(g.nodes[j-1] == g.nodes[j] && !g.control[j-1].active)
+ g.control[j-1]=control(g.nodes[j-1],g.nodes[j-1]);
+ }
+ }
+ }
+
+ // Next, resolve sequences.
+ int i=0;
+ int start=0;
+ while(i < n) {
+ // Look for a missing outgoing direction.
+ while(i <= n && g.solved(i)) {start=i; ++i;}
+ if(i > n) break;
+ // Look for the end of the sequence.
+ while(i < n && !g.solved(i)) ++i;
+
+ while(start < i && g.control[start].active) ++start;
+
+ if(start < i)
+ aim(g,start,i);
+ }
+
+ // Compute missing 3D control points.
+ for(int i=0; i < n; ++i) {
+ int next=g.cyclic[i+1] ? 0 : i+1;
+ if(!g.control[i].active) {
+ control c;
+ if((g.out[i].Curl && g.in[i].Curl) ||
+ (g.out[i].dir == O && g.in[i].dir == O)) {
+ // fill in straight control points for path3 functions
+ triple delta=(g.nodes[i+1]-g.nodes[i])/3;
+ c=control(g.nodes[i]+delta,g.nodes[i+1]-delta,straight=true);
+ } else {
+ Controls C=Controls(g.nodes[i],g.nodes[next],g.out[i].dir,g.in[i].dir,
+ g.Tension[i].out,g.Tension[i].in,
+ g.Tension[i].atLeast);
+ c=control(C.c0,C.c1);
+ }
+ g.control[i]=c;
+ }
+ }
+
+ // Convert to Knuth's format (control points stored with nodes)
+ int n=g.nodes.length;
+ bool cyclic;
+ if(n > 0) {
+ cyclic=g.cyclic[n-1];
+ if(cyclic) --n;
+ }
+ triple[] pre=new triple[n];
+ triple[] point=new triple[n];
+ triple[] post=new triple[n];
+ bool[] straight=new bool[n];
+ if(n > 0) {
+ for(int i=0; i < n-1; ++i) {
+ point[i]=g.nodes[i];
+ post[i]=g.control[i].post;
+ pre[i+1]=g.control[i].pre;
+ straight[i]=g.control[i].straight;
+ }
+ point[n-1]=g.nodes[n-1];
+ if(cyclic) {
+ pre[0]=g.control[n-1].pre;
+ post[n-1]=g.control[n-1].post;
+ straight[n-1]=g.control[n-1].straight;
+ } else {
+ pre[0]=point[0];
+ post[n-1]=point[n-1];
+ straight[n-1]=false;
+ }
+ }
+
+ return path3(pre,point,post,straight,cyclic);
+}
+
+path nurb(path3 p, projection P, int ninterpolate=P.ninterpolate)
+{
+ triple f=P.camera;
+ triple u=unit(P.normal);
+ transform3 t=P.t;
+
+ path nurb(triple v0, triple v1, triple v2, triple v3) {
+ return nurb(project(v0,t),project(v1,t),project(v2,t),project(v3,t),
+ dot(u,f-v0),dot(u,f-v1),dot(u,f-v2),dot(u,f-v3),ninterpolate);
+ }
+
+ path g;
+
+ if(straight(p,0))
+ g=project(point(p,0),t);
+
+ int last=length(p);
+ for(int i=0; i < last; ++i) {
+ if(straight(p,i))
+ g=g--project(point(p,i+1),t);
+ else
+ g=g&nurb(point(p,i),postcontrol(p,i),precontrol(p,i+1),point(p,i+1));
+ }
+
+ int n=length(g);
+ if(cyclic(p)) g=g&cycle;
+
+ return g;
+}
+
+path project(path3 p, projection P=currentprojection,
+ int ninterpolate=P.ninterpolate)
+{
+ guide g;
+
+ int last=length(p);
+ if(last < 0) return g;
+
+ transform3 t=P.t;
+
+ if(ninterpolate == 1 || piecewisestraight(p)) {
+ g=project(point(p,0),t);
+ // Construct the path.
+ int stop=cyclic(p) ? last-1 : last;
+ for(int i=0; i < stop; ++i) {
+ if(straight(p,i))
+ g=g--project(point(p,i+1),t);
+ else {
+ g=g..controls project(postcontrol(p,i),t) and
+ project(precontrol(p,i+1),t)..project(point(p,i+1),t);
+ }
+ }
+ } else return nurb(p,P);
+
+ if(cyclic(p))
+ g=straight(p,last-1) ? g--cycle :
+ g..controls project(postcontrol(p,last-1),t) and
+ project(precontrol(p,last),t)..cycle;
+ return g;
+}
+
+pair[] project(triple[] v, projection P=currentprojection)
+{
+ return sequence(new pair(int i) {return project(v[i],P.t);},v.length);
+}
+
+path[] project(explicit path3[] g, projection P=currentprojection)
+{
+ return sequence(new path(int i) {return project(g[i],P);},g.length);
+}
+
+guide3 operator cast(path3 p)
+{
+ int last=length(p);
+
+ bool cyclic=cyclic(p);
+ int stop=cyclic ? last-1 : last;
+ return new void(flatguide3 f) {
+ if(last >= 0) {
+ f.node(point(p,0));
+ for(int i=0; i < stop; ++i) {
+ if(straight(p,i)) {
+ f.out(1);
+ f.in(1);
+ } else
+ f.control(postcontrol(p,i),precontrol(p,i+1));
+ f.node(point(p,i+1));
+ }
+ if(cyclic) {
+ if(straight(p,stop)) {
+ f.out(1);
+ f.in(1);
+ } else
+ f.control(postcontrol(p,stop),precontrol(p,last));
+ f.cycleToken();
+ }
+ }
+ };
+}
+
+// Return a unit normal vector to a planar path p (or O if the path is
+// nonplanar).
+triple normal(path3 p)
+{
+ triple normal;
+ real fuzz=sqrtEpsilon*abs(max(p)-min(p));
+ real absnormal;
+ real theta;
+
+ bool Cross(triple a, triple b) {
+ if(abs(a) >= fuzz && abs(b) >= fuzz) {
+ triple n=cross(unit(a),unit(b));
+ real absn=abs(n);
+ if(absn < sqrtEpsilon) return false;
+ n=unit(n);
+ if(absnormal > 0 &&
+ abs(normal-n) > sqrtEpsilon && abs(normal+n) > sqrtEpsilon)
+ return true;
+ else {
+ int sign=dot(n,normal) >= 0 ? 1 : -1;
+ theta += sign*asin1(absn);
+ if(absn > absnormal) {
+ absnormal=absn;
+ normal=n;
+ theta=sign*theta;
+ }
+ }
+ }
+ return false;
+ }
+
+ int L=length(p);
+ if(L <= 0) return O;
+
+ triple zi=point(p,0);
+ triple v0=zi-precontrol(p,0);
+ for(int i=0; i < L; ++i) {
+ triple c0=postcontrol(p,i);
+ triple c1=precontrol(p,i+1);
+ triple zp=point(p,i+1);
+ triple v1=c0-zi;
+ triple v2=c1-c0;
+ triple v3=zp-c1;
+ if(Cross(v0,v1) || Cross(v1,v2) || Cross(v2,v3)) return O;
+ v0=v3;
+ zi=zp;
+ }
+ return theta >= 0 ? normal : -normal;
+}
+
+// Return a unit normal vector to a polygon with vertices in p.
+triple normal(triple[] p)
+{
+ triple normal;
+ real fuzz=sqrtEpsilon*abs(maxbound(p)-minbound(p));
+ real absnormal;
+ real theta;
+
+ bool Cross(triple a, triple b) {
+ if(abs(a) >= fuzz && abs(b) >= fuzz) {
+ triple n=cross(unit(a),unit(b));
+ real absn=abs(n);
+ n=unit(n);
+ if(absnormal > 0 && absn > sqrtEpsilon &&
+ abs(normal-n) > sqrtEpsilon && abs(normal+n) > sqrtEpsilon)
+ return true;
+ else {
+ int sign=dot(n,normal) >= 0 ? 1 : -1;
+ theta += sign*asin1(absn);
+ if(absn > absnormal) {
+ absnormal=absn;
+ normal=n;
+ theta=sign*theta;
+ }
+ }
+ }
+ return false;
+ }
+
+ if(p.length <= 0) return O;
+
+ triple zi=p[0];
+ triple v0=zi-p[p.length-1];
+ for(int i=0; i < p.length-1; ++i) {
+ triple zp=p[i+1];
+ triple v1=zp-zi;
+ if(Cross(v0,v1)) return O;
+ v0=v1;
+ zi=zp;
+ }
+ return theta >= 0 ? normal : -normal;
+}
+
+// Transforms that map XY plane to YX, YZ, ZY, ZX, and XZ planes.
+restricted transform3 XY=identity4;
+restricted transform3 YX=rotate(-90,O,Z);
+restricted transform3 YZ=rotate(90,O,Z)*rotate(90,O,X);
+restricted transform3 ZY=rotate(-90,O,X)*YZ;
+restricted transform3 ZX=rotate(-90,O,Z)*rotate(-90,O,Y);
+restricted transform3 XZ=rotate(-90,O,Y)*ZX;
+
+private transform3 flip(transform3 t, triple X, triple Y, triple Z,
+ projection P)
+{
+ static transform3 flip(triple v) {
+ static real s(real x) {return x > 0 ? -1 : 1;}
+ return scale(s(v.x),s(v.y),s(v.z));
+ }
+
+ triple u=unit(P.normal);
+ triple up=unit(perp(P.up,u));
+ bool upright=dot(Z,u) >= 0;
+ if(dot(Y,up) < 0) {
+ t=flip(Y)*t;
+ upright=!upright;
+ }
+ return upright ? t : flip(X)*t;
+}
+
+restricted transform3 XY(projection P=currentprojection)
+{
+ return flip(XY,X,Y,Z,P);
+}
+
+restricted transform3 YX(projection P=currentprojection)
+{
+ return flip(YX,Y,X,Z,P);
+}
+
+restricted transform3 YZ(projection P=currentprojection)
+{
+ return flip(YZ,Y,Z,X,P);
+}
+
+restricted transform3 ZY(projection P=currentprojection)
+{
+ return flip(ZY,Z,Y,X,P);
+}
+
+restricted transform3 ZX(projection P=currentprojection)
+{
+ return flip(ZX,Z,X,Y,P);
+}
+
+restricted transform3 XZ(projection P=currentprojection)
+{
+ return flip(XZ,X,Z,Y,P);
+}
+
+// Transform3 that projects in direction dir onto plane with normal n
+// through point O.
+transform3 planeproject(triple n, triple O=O, triple dir=n)
+{
+ real a=n.x, b=n.y, c=n.z;
+ real u=dir.x, v=dir.y, w=dir.z;
+ real delta=1.0/(a*u+b*v+c*w);
+ real d=-(a*O.x+b*O.y+c*O.z)*delta;
+ return new real[][] {
+ {(b*v+c*w)*delta,-b*u*delta,-c*u*delta,-d*u},
+ {-a*v*delta,(a*u+c*w)*delta,-c*v*delta,-d*v},
+ {-a*w*delta,-b*w*delta,(a*u+b*v)*delta,-d*w},
+ {0,0,0,1}
+ };
+}
+
+// Transform3 that projects in direction dir onto plane defined by p.
+transform3 planeproject(path3 p, triple dir=O)
+{
+ triple n=normal(p);
+ return planeproject(n,point(p,0),dir == O ? n : dir);
+}
+
+// Transform for projecting onto plane through point O with normal cross(u,v).
+transform transform(triple u, triple v, triple O=O,
+ projection P=currentprojection)
+{
+ transform3 t=P.t;
+ static real[] O={0,0,0,1};
+ real[] tO=t*O;
+ real tO3=tO[3];
+ real factor=1/tO3^2;
+ real[] x=(tO3*t[0]-tO[0]*t[3])*factor;
+ real[] y=(tO3*t[1]-tO[1]*t[3])*factor;
+ triple x=(x[0],x[1],x[2]);
+ triple y=(y[0],y[1],y[2]);
+ u=unit(u);
+ v=unit(v);
+ return (0,0,dot(u,x),dot(v,x),dot(u,y),dot(v,y));
+}
+
+// Project Label onto plane through point O with normal cross(u,v).
+Label project(Label L, triple u, triple v, triple O=O,
+ projection P=currentprojection) {
+ Label L=L.copy();
+ L.position=project(O,P.t);
+ L.transform(transform(u,v,O,P));
+ return L;
+}
+
+path3 operator cast(guide3 g) {return solve(g);}
+path3 operator cast(triple v) {return path3(v);}
+
+guide3[] operator cast(triple[] v)
+{
+ return sequence(new guide3(int i) {return v[i];},v.length);
+}
+
+path3[] operator cast(triple[] v)
+{
+ return sequence(new path3(int i) {return v[i];},v.length);
+}
+
+path3[] operator cast(guide3[] g)
+{
+ return sequence(new path3(int i) {return solve(g[i]);},g.length);
+}
+
+guide3[] operator cast(path3[] g)
+{
+ return sequence(new guide3(int i) {return g[i];},g.length);
+}
+
+void write(file file, string s="", explicit guide3[] x, suffix suffix=none)
+{
+ write(file,s,(path3[]) x,suffix);
+}
+
+void write(string s="", explicit guide3[] x, suffix suffix=endl)
+{
+ write(stdout,s,(path3[]) x,suffix);
+}
+
+triple point(explicit guide3 g, int t) {
+ flatguide3 f;
+ g(f);
+ int n=f.size();
+ return f.nodes[adjustedIndex(t,n,f.cyclic())];
+}
+
+triple[] dirSpecifier(guide3 g, int t)
+{
+ flatguide3 f;
+ g(f);
+ int n=f.size();
+ checkEmpty(n);
+ if(f.cyclic()) t=t % n;
+ else if(t < 0 || t >= n-1) return new triple[];
+ return new triple[] {f.out[t].dir,f.in[t].dir};
+}
+
+triple[] controlSpecifier(guide3 g, int t) {
+ flatguide3 f;
+ g(f);
+ int n=f.size();
+ checkEmpty(n);
+ if(f.cyclic()) t=t % n;
+ else if(t < 0 || t >= n-1) return new triple[];
+ control c=f.control[t];
+ if(c.active) return new triple[] {c.post,c.pre};
+ else return new triple[];
+}
+
+tensionSpecifier tensionSpecifier(guide3 g, int t)
+{
+ flatguide3 f;
+ g(f);
+ int n=f.size();
+ checkEmpty(n);
+ if(f.cyclic()) t=t % n;
+ else if(t < 0 || t >= n-1) return operator tension(1,1,false);
+ Tension T=f.Tension[t];
+ return operator tension(T.out,T.in,T.atLeast);
+}
+
+real[] curlSpecifier(guide3 g, int t)
+{
+ flatguide3 f;
+ g(f);
+ int n=f.size();
+ checkEmpty(n);
+ if(f.cyclic()) t=t % n;
+ else if(t < 0 || t >= n-1) return new real[];
+ return new real[] {f.out[t].gamma,f.in[t].gamma};
+}
+
+guide3 reverse(guide3 g)
+{
+ flatguide3 f;
+ bool cyclic=cyclic(g);
+ g(f);
+
+ if(f.precyclic())
+ return reverse(solve(g));
+
+ int n=f.size();
+ checkEmpty(n);
+ guide3 G;
+ if(n >= 0) {
+ int start=cyclic ? n : n-1;
+ for(int i=start; i > 0; --i) {
+ G=G..f.nodes[i];
+ control c=f.control[i-1];
+ if(c.active)
+ G=G..operator controls(c.pre,c.post);
+ else {
+ dir in=f.in[i-1];
+ triple d=in.dir;
+ if(d != O) G=G..operator spec(-d,JOIN_OUT);
+ else if(in.Curl) G=G..operator curl(in.gamma,JOIN_OUT);
+ dir out=f.out[i-1];
+ triple d=out.dir;
+ if(d != O) G=G..operator spec(-d,JOIN_IN);
+ else if(out.Curl) G=G..operator curl(out.gamma,JOIN_IN);
+ }
+ }
+ if(cyclic) G=G..cycle;
+ else G=G..f.nodes[0];
+ }
+ return G;
+}
+
+triple intersectionpoint(path3 p, path3 q, real fuzz=-1)
+{
+ real[] t=intersect(p,q,fuzz);
+ if(t.length == 0) abort("paths do not intersect");
+ return point(p,t[0]);
+}
+
+// return an array containing all intersection points of p and q
+triple[] intersectionpoints(path3 p, path3 q, real fuzz=-1)
+{
+ real[][] t=intersections(p,q,fuzz);
+ return sequence(new triple(int i) {return point(p,t[i][0]);},t.length);
+}
+
+triple[] intersectionpoints(explicit path3[] p, explicit path3[] q,
+ real fuzz=-1)
+{
+ triple[] v;
+ for(int i=0; i < p.length; ++i)
+ for(int j=0; j < q.length; ++j)
+ v.append(intersectionpoints(p[i],q[j],fuzz));
+ return v;
+}
+
+path3 operator &(path3 p, cycleToken tok)
+{
+ int n=length(p);
+ if(n < 0) return nullpath3;
+ triple a=point(p,0);
+ triple b=point(p,n);
+ return subpath(p,0,n-1)..controls postcontrol(p,n-1) and precontrol(p,n)..
+ cycle;
+}
+
+// return the point on path3 p at arclength L
+triple arcpoint(path3 p, real L)
+{
+ return point(p,arctime(p,L));
+}
+
+// return the point on path3 p at arclength L
+triple arcpoint(path3 p, real L)
+{
+ return point(p,arctime(p,L));
+}
+
+// return the direction on path3 p at arclength L
+triple arcdir(path3 p, real L)
+{
+ return dir(p,arctime(p,L));
+}
+
+// return the time on path3 p at the relative fraction l of its arclength
+real reltime(path3 p, real l)
+{
+ return arctime(p,l*arclength(p));
+}
+
+// return the point on path3 p at the relative fraction l of its arclength
+triple relpoint(path3 p, real l)
+{
+ return point(p,reltime(p,l));
+}
+
+// return the direction of path3 p at the relative fraction l of its arclength
+triple reldir(path3 p, real l)
+{
+ return dir(p,reltime(p,l));
+}
+
+// return the point on path3 p at half of its arclength
+triple midpoint(path3 p)
+{
+ return relpoint(p,0.5);
+}
+
+real relative(Label L, path3 g)
+{
+ return L.position.relative ? reltime(g,L.relative()) : L.relative();
+}
+
+// return the linear transformation that maps X,Y,Z to u,v,w.
+transform3 transform3(triple u, triple v, triple w=cross(u,v))
+{
+ return new real[][] {
+ {u.x,v.x,w.x,0},
+ {u.y,v.y,w.y,0},
+ {u.z,v.z,w.z,0},
+ {0,0,0,1}
+ };
+}
+
+// return the rotation that maps Z to a unit vector u about cross(u,Z),
+transform3 align(triple u)
+{
+ real a=u.x;
+ real b=u.y;
+ real c=u.z;
+ real d=a^2+b^2;
+
+ if(d != 0) {
+ d=sqrt(d);
+ real e=1/d;
+ return new real[][] {
+ {-b*e,-a*c*e,a,0},
+ {a*e,-b*c*e,b,0},
+ {0,d,c,0},
+ {0,0,0,1}};
+ }
+ return c >= 0 ? identity(4) : diagonal(1,-1,-1,1);
+}
+
+// return a rotation that maps X,Y to the projection plane.
+transform3 transform3(projection P=currentprojection)
+{
+ triple w=unit(P.normal);
+ triple v=unit(perp(P.up,w));
+ if(v == O) v=cross(perp(w),w);
+ triple u=cross(v,w);
+ return u != O ? transform3(u,v,w) : identity(4);
+}
+
+triple[] triples(real[] x, real[] y, real[] z)
+{
+ if(x.length != y.length || x.length != z.length)
+ abort("arrays have different lengths");
+ return sequence(new triple(int i) {return (x[i],y[i],z[i]);},x.length);
+}
+
+path3[] operator cast(path3 p)
+{
+ return new path3[] {p};
+}
+
+path3[] operator cast(guide3 g)
+{
+ return new path3[] {(path3) g};
+}
+
+path3[] operator ^^ (path3 p, path3 q)
+{
+ return new path3[] {p,q};
+}
+
+path3[] operator ^^ (path3 p, explicit path3[] q)
+{
+ return concat(new path3[] {p},q);
+}
+
+path3[] operator ^^ (explicit path3[] p, path3 q)
+{
+ return concat(p,new path3[] {q});
+}
+
+path3[] operator ^^ (explicit path3[] p, explicit path3[] q)
+{
+ return concat(p,q);
+}
+
+path3[] operator * (transform3 t, explicit path3[] p)
+{
+ return sequence(new path3(int i) {return t*p[i];},p.length);
+}
+
+triple[] operator * (transform3 t, triple[] v)
+{
+ return sequence(new triple(int i) {return t*v[i];},v.length);
+}
+
+triple[][] operator * (transform3 t, triple[][] v)
+{
+ triple[][] V=new triple[v.length][];
+ for(int i=0; i < v.length; ++i) {
+ triple[] vi=v[i];
+ V[i]=sequence(new triple(int j) {return t*vi[j];},vi.length);
+ }
+ return V;
+}
+
+triple min(explicit path3[] p)
+{
+ checkEmpty(p.length);
+ triple minp=min(p[0]);
+ for(int i=1; i < p.length; ++i)
+ minp=minbound(minp,min(p[i]));
+ return minp;
+}
+
+triple max(explicit path3[] p)
+{
+ checkEmpty(p.length);
+ triple maxp=max(p[0]);
+ for(int i=1; i < p.length; ++i)
+ maxp=maxbound(maxp,max(p[i]));
+ return maxp;
+}
+
+path3 randompath3(int n, bool cumulate=true, interpolate3 join=operator ..)
+{
+ guide3 g;
+ triple w;
+ for(int i=0; i <= n; ++i) {
+ triple z=(unitrand()-0.5,unitrand()-0.5,unitrand()-0.5);
+ if(cumulate) w += z;
+ else w=z;
+ g=join(g,w);
+ }
+ return g;
+}
+
+path3[] box(triple v1, triple v2)
+{
+ return
+ (v1.x,v1.y,v1.z)--
+ (v1.x,v1.y,v2.z)--
+ (v1.x,v2.y,v2.z)--
+ (v1.x,v2.y,v1.z)--
+ (v1.x,v1.y,v1.z)--
+ (v2.x,v1.y,v1.z)--
+ (v2.x,v1.y,v2.z)--
+ (v2.x,v2.y,v2.z)--
+ (v2.x,v2.y,v1.z)--
+ (v2.x,v1.y,v1.z)^^
+ (v2.x,v2.y,v1.z)--
+ (v1.x,v2.y,v1.z)^^
+ (v1.x,v2.y,v2.z)--
+ (v2.x,v2.y,v2.z)^^
+ (v2.x,v1.y,v2.z)--
+ (v1.x,v1.y,v2.z);
+}
+
+restricted path3[] unitbox=box(O,(1,1,1));
+restricted path3 unitcircle3=X..Y..-X..-Y..cycle;
+restricted path3 unitsquare3=O--X--X+Y--Y--cycle;
+
+path3 circle(triple c, real r, triple normal=Z)
+{
+ path3 p=normal == Z ? unitcircle3 : align(unit(normal))*unitcircle3;
+ return shift(c)*scale3(r)*p;
+}
+
+// return an arc centered at c from triple v1 to v2 (assuming |v2-c|=|v1-c|),
+// drawing in the given direction.
+// The normal must be explicitly specified if c and the endpoints are colinear.
+path3 arc(triple c, triple v1, triple v2, triple normal=O, bool direction=CCW)
+{
+ v1 -= c;
+ real r=abs(v1);
+ v1=unit(v1);
+ v2=unit(v2-c);
+
+ if(normal == O) {
+ normal=cross(v1,v2);
+ if(normal == O) abort("explicit normal required for these endpoints");
+ }
+
+ transform3 T;
+ bool align=normal != Z;
+ if(align) {
+ T=align(unit(normal));
+ transform3 Tinv=transpose(T);
+ v1=Tinv*v1;
+ v2=Tinv*v2;
+ }
+
+ string invalidnormal="invalid normal vector";
+ real fuzz=sqrtEpsilon*max(abs(v1),abs(v2));
+ if(abs(v1.z) > fuzz || abs(v2.z) > fuzz)
+ abort(invalidnormal);
+
+ real[] t1=intersect(unitcircle3,O--2*(v1.x,v1.y,0));
+ real[] t2=intersect(unitcircle3,O--2*(v2.x,v2.y,0));
+
+ if(t1.length == 0 || t2.length == 0)
+ abort(invalidnormal);
+
+ real t1=t1[0];
+ real t2=t2[0];
+ int n=length(unitcircle3);
+ if(direction) {
+ if (t1 >= t2) t1 -= n;
+ } else if(t2 >= t1) t2 -= n;
+
+ path3 p=subpath(unitcircle3,t1,t2);
+ if(align) p=T*p;
+ return shift(c)*scale3(r)*p;
+}
+
+// return an arc centered at c with radius r from c+r*dir(theta1,phi1) to
+// c+r*dir(theta2,phi2) in degrees, drawing in the given direction
+// relative to the normal vector cross(dir(theta1,phi1),dir(theta2,phi2)).
+// The normal must be explicitly specified if c and the endpoints are colinear.
+path3 arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
+ triple normal=O, bool direction)
+{
+ return arc(c,c+r*dir(theta1,phi1),c+r*dir(theta2,phi2),normal,direction);
+}
+
+// return an arc centered at c with radius r from c+r*dir(theta1,phi1) to
+// c+r*dir(theta2,phi2) in degrees, drawing drawing counterclockwise
+// relative to the normal vector cross(dir(theta1,phi1),dir(theta2,phi2))
+// iff theta2 > theta1 or (theta2 == theta1 and phi2 >= phi1).
+// The normal must be explicitly specified if c and the endpoints are colinear.
+path3 arc(triple c, real r, real theta1, real phi1, real theta2, real phi2,
+ triple normal=O)
+{
+ return arc(c,r,theta1,phi1,theta2,phi2,normal,
+ theta2 > theta1 || (theta2 == theta1 && phi2 >= phi1) ? CCW : CW);
+}
+
+private real epsilon=1000*realEpsilon;
+
+// Return a representation of the plane through point O with normal cross(u,v).
+path3 plane(triple u, triple v, triple O=O)
+{
+ return O--O+u--O+u+v--O+v--cycle;
+}
+
+// PRC/OpenGL support
+
+include three_light;
+
+void draw(frame f, path3 g, material p=currentpen, light light=nolight,
+ string name="", render render=defaultrender,
+ projection P=currentprojection);
+
+void begingroup3(frame f, string name="", render render=defaultrender,
+ triple center=O, int interaction=0)
+{
+ _begingroup3(f,name,render.compression,render.granularity,render.closed,
+ render.tessellate,render.merge == false,
+ render.merge == true,center,interaction);
+}
+
+void begingroup3(picture pic=currentpicture, string name="",
+ render render=defaultrender,
+ triple center=O, int interaction=0)
+{
+ pic.add(new void(frame f, transform3, picture pic, projection) {
+ if(is3D())
+ begingroup3(f,name,render,center,interaction);
+ if(pic != null)
+ begingroup(pic);
+ },true);
+}
+
+void endgroup3(picture pic=currentpicture)
+{
+ pic.add(new void(frame f, transform3, picture pic, projection) {
+ if(is3D())
+ endgroup3(f);
+ if(pic != null)
+ endgroup(pic);
+ },true);
+}
+
+void addPath(picture pic, path3 g, pen p)
+{
+ if(size(g) > 0)
+ pic.addBox(min(g),max(g),min3(p),max3(p));
+}
+
+include three_surface;
+include three_margins;
+
+pair min(path3 p, projection P)
+{
+ path3 q=P.T.modelview*p;
+ if(P.infinity)
+ return xypart(min(q));
+ return maxratio(q)/P.T.projection[3][2];
+}
+
+pair max(path3 p, projection P)
+{
+ path3 q=P.T.modelview*p;
+ if(P.infinity)
+ return xypart(max(q));
+ return minratio(q)/P.T.projection[3][2];
+}
+
+pair min(frame f, projection P)
+{
+ frame g=P.T.modelview*f;
+ if(P.infinity)
+ return xypart(min3(g));
+ return maxratio(g)/P.T.projection[3][2];
+}
+
+pair max(frame f, projection P)
+{
+ frame g=P.T.modelview*f;
+ if(P.infinity)
+ return xypart(max3(g));
+ return minratio(g)/P.T.projection[3][2];
+}
+
+void draw(picture pic=currentpicture, Label L="", path3 g,
+ align align=NoAlign, material p=currentpen, margin3 margin=NoMargin3,
+ light light=nolight, string name="", render render=defaultrender)
+{
+ pen q=(pen) p;
+ pic.add(new void(frame f, transform3 t, picture pic, projection P) {
+ path3 G=margin(t*g,q).g;
+ if(is3D()) {
+ draw(f,G,p,light,name,render,null);
+ if(pic != null && size(G) > 0)
+ pic.addBox(min(G,P),max(G,P),min(q),max(q));
+ }
+ if(pic != null)
+ draw(pic,project(G,P),q);
+ },true);
+ Label L=L.copy();
+ L.align(align);
+ if(L.s != "") {
+ L.p(q);
+ label(pic,L,g);
+ }
+ addPath(pic,g,q);
+}
+
+include three_tube;
+
+draw=new void(frame f, path3 g, material p=currentpen,
+ light light=nolight, string name="",
+ render render=defaultrender,
+ projection P=currentprojection) {
+ pen q=(pen) p;
+ if(is3D()) {
+ p=material(p);
+ real width=linewidth(q);
+ void drawthick(path3 g) {
+ if(settings.thick) {
+ if(width > 0) {
+ bool prc=prc();
+ void cylinder(transform3) {};
+ void sphere(transform3, bool half) {};
+ void disk(transform3) {};
+ void pipe(path3, path3);
+ if(prc) {
+ cylinder=new void(transform3 t) {drawPRCcylinder(f,t,p,light);};
+ sphere=new void(transform3 t, bool half)
+ {drawPRCsphere(f,t,half,p,light,render);};
+ disk=new void(transform3 t) {draw(f,t*unitdisk,p,light,render);};
+ pipe=new void(path3 center, path3 g)
+ {drawPRCtube(f,center,g,p,light);};
+ }
+ real linecap=linecap(q);
+ real r=0.5*width;
+ bool open=!cyclic(g);
+ int L=length(g);
+ triple g0=point(g,0);
+ triple gL=point(g,L);
+ if(open && L > 0) {
+ if(linecap == 2) {
+ g0 -= r*dir(g,0);
+ gL += r*dir(g,L);
+ g=g0..g..gL;
+ L += 2;
+ }
+ }
+ tube T=tube(g,width,render,cylinder,sphere,pipe);
+ path3 c=T.center;
+ if(L >= 0) {
+ if(open) {
+ int Lc=length(c);
+ triple c0=point(c,0);
+ triple cL=point(c,Lc);
+ triple dir0=dir(g,0);
+ triple dirL=dir(g,L);
+ triple dirc0=dir(c,0);
+ triple dircL=dir(c,Lc);
+ transform3 t0=shift(g0)*align(-dir0);
+ transform3 tL=shift(gL)*align(dirL);
+ transform3 tc0=shift(c0)*align(-dirc0);
+ transform3 tcL=shift(cL)*align(dircL);
+ if(linecap == 0 || linecap == 2){
+ transform3 scale2r=scale(r,r,1);
+ T.s.append(t0*scale2r*unitdisk);
+ disk(tc0*scale2r);
+ if(L > 0) {
+ T.s.append(tL*scale2r*unitdisk);
+ disk(tcL*scale2r);
+ }
+ } else if(linecap == 1) {
+ transform3 scale3r=scale3(r);
+ T.s.append(t0*scale3r*
+ (dir0 != O ? unithemisphere : unitsphere));
+ sphere(tc0*scale3r,half=straight(c,0));
+ if(L > 0) {
+ T.s.append(tL*scale3r*
+ (dirL != O ? unithemisphere : unitsphere));
+ sphere(tcL*scale3r,half=straight(c,Lc-1));
+ }
+ }
+ }
+ if(opacity(q) == 1)
+ _draw(f,c,q);
+ }
+ for(patch s : T.s.s)
+ draw3D(f,s,p,light,prc=false);
+ } else _draw(f,g,q);
+ } else _draw(f,g,q);
+ }
+ if(q != nullpen)
+ begingroup3(f,name == "" ? "curve" : name,render);
+ if(linetype(q).length == 0) drawthick(g);
+ else {
+ real[] dash=linetype(adjust(q,arclength(g),cyclic(g)));
+ if(sum(dash) > 0) {
+ dash.cyclic=true;
+ real offset=offset(q);
+ real L=arclength(g);
+ int i=0;
+ real l=offset;
+ while(l <= L) {
+ real t1=arctime(g,l);
+ l += dash[i];
+ real t2=arctime(g,min(l,L));
+ drawthick(subpath(g,t1,t2));
+ ++i;
+ l += dash[i];
+ ++i;
+ }
+ }
+ }
+ if(q != nullpen)
+ endgroup3(f);
+ } else draw(f,project(g,P),q);
+};
+
+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);
+ for(int i=0; i < g.length; ++i)
+ draw(f,g[i],p,light,partname(i),render,P);
+ if(g.length > 1)
+ endgroup3(f);
+}
+
+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)
+ 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);
+}
+
+include three_arrows;
+
+void draw(picture pic=currentpicture, Label L="", path3 g,
+ align align=NoAlign, material p=currentpen, arrowbar3 arrow,
+ arrowbar3 bar=None, margin3 margin=NoMargin3, light light=nolight,
+ light arrowheadlight=currentlight, string name="",
+ render render=defaultrender)
+{
+ bool group=arrow != None || bar != None;
+ if(group)
+ begingroup3(pic,name,render);
+ bool drawpath=arrow(pic,g,p,margin,light,arrowheadlight);
+ if(bar(pic,g,p,margin,light,arrowheadlight) && drawpath)
+ draw(pic,L,g,align,p,margin,light,render);
+ if(group)
+ endgroup3(pic);
+ if(L.s != "")
+ label(pic,L,g,align,(pen) p);
+}
+
+void draw(frame f, path3 g, material p=currentpen, arrowbar3 arrow,
+ light light=nolight, light arrowheadlight=currentlight,
+ string name="", render render=defaultrender,
+ projection P=currentprojection)
+{
+ picture pic;
+ bool group=arrow != None;
+ if(group)
+ begingroup3(f,name,render);
+ if(arrow(pic,g,p,NoMargin3,light,arrowheadlight))
+ draw(f,g,p,light,render,P);
+ add(f,pic.fit());
+ if(group)
+ endgroup3(f);
+}
+
+void add(picture pic=currentpicture, void d(picture,transform3),
+ bool exact=false)
+{
+ pic.add(d,exact);
+}
+
+// Fit the picture src using the identity transformation (so user
+// coordinates and truesize coordinates agree) and add it about the point
+// position to picture dest.
+void add(picture dest, picture src, triple position, bool group=true,
+ bool above=true)
+{
+ dest.add(new void(picture f, transform3 t) {
+ f.add(shift(t*position)*src,group,above);
+ });
+}
+
+void add(picture src, triple position, bool group=true, bool above=true)
+{
+ add(currentpicture,src,position,group,above);
+}
+
+// Align an arrow pointing to b from the direction dir. The arrow is
+// 'length' PostScript units long.
+void arrow(picture pic=currentpicture, Label L="", triple b, triple dir,
+ real length=arrowlength, align align=NoAlign,
+ pen p=currentpen, arrowbar3 arrow=Arrow3, margin3 margin=EndMargin3,
+ light light=nolight, light arrowheadlight=currentlight,
+ string name="", render render=defaultrender)
+{
+ Label L=L.copy();
+ if(L.defaultposition) L.position(0);
+ L.align(L.align,dir);
+ L.p(p);
+ picture opic;
+ marginT3 margin=margin(b--b,p); // Extract margin.begin and margin.end
+ triple a=(margin.begin+length+margin.end)*unit(dir);
+ draw(opic,L,a--O,align,p,arrow,margin,light,arrowheadlight,name,render);
+ add(pic,opic,b);
+}
+
+void arrow(picture pic=currentpicture, Label L="", triple b, pair dir,
+ real length=arrowlength, align align=NoAlign,
+ pen p=currentpen, arrowbar3 arrow=Arrow3, margin3 margin=EndMargin3,
+ light light=nolight, light arrowheadlight=currentlight,
+ string name="", render render=defaultrender,
+ projection P=currentprojection)
+{
+ arrow(pic,L,b,invert(dir,b,P),length,align,p,arrow,margin,light,
+ arrowheadlight,name,render);
+}
+
+triple min3(picture pic, projection P=currentprojection)
+{
+ return pic.min3(P);
+}
+
+triple max3(picture pic, projection P=currentprojection)
+{
+ return pic.max3(P);
+}
+
+triple size3(picture pic, bool user=false, projection P=currentprojection)
+{
+ transform3 t=pic.calculateTransform3(P);
+ triple M=pic.max(t);
+ triple m=pic.min(t);
+ if(!user) return M-m;
+ t=inverse(t);
+ return t*M-t*m;
+}
+
+triple point(frame f, triple dir)
+{
+ triple m=min3(f);
+ triple M=max3(f);
+ return m+realmult(rectify(dir),M-m);
+}
+
+triple point(picture pic=currentpicture, triple dir, bool user=true,
+ projection P=currentprojection)
+{
+ triple min = pic.userMin(), max = pic.userMax();
+ triple v=min+realmult(rectify(dir),max-min);
+ return user ? v : pic.calculateTransform3(P)*v;
+}
+
+triple truepoint(picture pic=currentpicture, triple dir, bool user=true,
+ projection P=currentprojection)
+{
+ transform3 t=pic.calculateTransform3(P);
+ triple m=pic.min(t);
+ triple M=pic.max(t);
+ triple v=m+realmult(rectify(dir),M-m);
+ return user ? inverse(t)*v : v;
+}
+
+void add(picture dest=currentpicture, object src, pair position=0, pair align=0,
+ bool group=true, filltype filltype=NoFill, bool above=true)
+{
+ if(prc())
+ label(dest,src,position,align);
+ else if(settings.render == 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;
+ void operator init(string s) {
+ s=replace(s,'\n'," ");
+ string[] S=split(s);
+ int pos(string s, string key) {
+ int pos=find(s,key);
+ if(pos < 0) return -1;
+ pos += length(key);
+ while(substr(s,pos,1) == " ") ++pos;
+ if(substr(s,pos,1) == "=")
+ return pos+1;
+ return -1;
+ }
+ triple C2C=X;
+ real ROO=1;
+ real ROLL=0;
+ angle=30;
+ int pos;
+ for(int k=0; k < S.length; ++k) {
+ if((pos=pos(S[k],"COO")) >= 0)
+ target=((real) substr(S[k],pos),(real) S[++k],(real) S[++k]);
+ else if((pos=pos(S[k],"C2C")) >= 0)
+ C2C=((real) substr(S[k],pos),(real) S[++k],(real) S[++k]);
+ else if((pos=pos(S[k],"ROO")) >= 0)
+ ROO=(real) substr(S[k],pos);
+ else if((pos=pos(S[k],"ROLL")) >= 0)
+ ROLL=(real) substr(S[k],pos);
+ else if((pos=pos(S[k],"AAC")) >= 0)
+ angle=(real) substr(S[k],pos);
+ }
+ camera=target+ROO*C2C;
+ triple u=unit(target-camera);
+ triple w=unit(Z-u.z*u);
+ up=rotate(ROLL,O,u)*w;
+ }
+}
+
+projection perspective(string s)
+{
+ viewpoint v=viewpoint(s);
+ projection P=perspective(v.camera,v.up,v.target);
+ P.angle=v.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");
+}
+
+private string Format(triple v, string sep=" ")
+{
+ return Format(v.x)+sep+Format(v.y)+sep+Format(v.z);
+}
+
+private string Format(real[] c)
+{
+ return Format((c[0],c[1],c[2]));
+}
+
+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)
+{
+ 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;
+}
+
+string lightscript(light light) {
+ string script="for(var i=scene.lights.count-1; i >= 0; i--)
+ scene.lights.removeByIndex(i);"+'\n\n';
+ for(int i=0; i < light.position.length; ++i) {
+ string Li="L"+string(i);
+ real[] diffuse=light.diffuse[i];
+ script += Li+"=scene.createLight();"+'\n'+
+ Li+".direction.set("+format(-light.position[i],",")+");"+'\n'+
+ Li+".color.set("+format((diffuse[0],diffuse[1],diffuse[2]),",")+");"+'\n';
+ }
+ // Work around initialization bug in Adobe Reader 8.0:
+ return script +"
+scene.lightScheme=scene.LIGHT_MODE_HEADLAMP;
+scene.lightScheme=scene.LIGHT_MODE_FILE;
+";
+}
+
+void writeJavaScript(string name, string preamble, string script)
+{
+ file out=output(name);
+ write(out,preamble);
+ if(script != "") {
+ write(out,endl);
+ file in=input(script);
+ while(true) {
+ string line=in;
+ if(eof(in)) break;
+ write(out,line,endl);
+ }
+ }
+ close(out);
+ if(settings.verbose > 1) write("Wrote "+name);
+ if(!settings.inlinetex)
+ file3.push(name);
+}
+
+pair viewportmargin(pair lambda)
+{
+ return maxbound(0.5*(viewportsize-lambda),viewportmargin);
+}
+
+string embed3D(string label="", string text=label, string prefix,
+ frame f, string format="",
+ real width=0, real height=0,
+ string options="", string script="",
+ light light=currentlight, projection P=currentprojection)
+{
+ if(!prc(format) || Embed == null) return "";
+
+ if(width == 0) width=settings.paperwidth;
+ if(height == 0) height=settings.paperheight;
+
+ 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;
+ } else
+ if(!P.absolute) P.angle=2*aTan(Tan(0.5*P.angle));
+
+ int[] index;
+ triple[] center;
+ shipout3(prefix,f,index,center);
+
+ string name=prefix+".js";
+ writeJavaScript(name,lightscript+projection(P.infinity,viewplanesize)+
+ billboard(index,center),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);
+ }
+
+ 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";
+ 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))+
+ ",3Dbg="+Format(light.background());
+ if(options != "") options3 += ","+options;
+ if(settings.inlinetex)
+ prefix=jobname(prefix);
+ options3 += ",3Djscript="+prefix+".js";
+
+ return Embed(prefix+".prc",options3,width,height);
+}
+
+struct scene
+{
+ frame f;
+ transform3 t;
+ projection P;
+ bool adjusted;
+ real width,height;
+ pair viewportmargin;
+ transform3 T=identity4;
+ picture pic2;
+
+ void operator init(frame f, real width, real height,
+ projection P=currentprojection) {
+ this.f=f;
+ this.t=identity4;
+ this.P=P;
+ this.width=width;
+ this.height=height;
+ }
+
+ void operator init(picture pic, real xsize=pic.xsize, real ysize=pic.ysize,
+ bool keepAspect=pic.keepAspect, bool is3D=true,
+ projection P=currentprojection) {
+ real xsize3=pic.xsize3, ysize3=pic.ysize3, zsize3=pic.zsize3;
+ bool warn=true;
+
+ if(xsize3 == 0 && ysize3 == 0 && zsize3 == 0) {
+ xsize3=ysize3=zsize3=max(xsize,ysize);
+ warn=false;
+ }
+
+ if(P.absolute)
+ this.P=P.copy();
+ else if(P.showtarget)
+ draw(pic,P.target,nullpen);
+
+ t=pic.scaling(xsize3,ysize3,zsize3,keepAspect,warn);
+ adjusted=false;
+ triple m=pic.min(t);
+ triple M=pic.max(t);
+
+ if(!P.absolute) {
+ this.P=t*P;
+ if(this.P.center && settings.render != 0) {
+ triple target=0.5*(m+M);
+ this.P.target=target;
+ this.P.calculate();
+ }
+ if(this.P.autoadjust || this.P.infinity)
+ adjusted=adjusted | this.P.adjust(m,M);
+ }
+
+ bool scale=xsize != 0 || ysize != 0;
+ bool scaleAdjust=scale && this.P.autoadjust;
+ bool noAdjust=(this.P.absolute || !scaleAdjust);
+
+ if(pic.bounds3.exact && noAdjust)
+ this.P.bboxonly=false;
+
+ f=pic.fit3(t,pic.bounds3.exact ? pic2 : null,this.P);
+
+ if(!pic.bounds3.exact) {
+ if(noAdjust)
+ this.P.bboxonly=false;
+
+ transform3 s=pic.scale3(f,xsize3,ysize3,zsize3,keepAspect);
+ t=s*t;
+ this.P=s*this.P;
+ f=pic.fit3(t,pic2,this.P);
+ }
+
+ if(is3D || scale) {
+ pic2.bounds.exact=true;
+ transform s=pic2.scaling(xsize,ysize,keepAspect);
+
+ pair m2=pic2.min(s);
+ pair M2=pic2.max(s);
+ pair lambda=M2-m2;
+ viewportmargin=viewportmargin(lambda);
+ width=ceil(lambda.x+2*viewportmargin.x);
+ height=ceil(lambda.y+2*viewportmargin.y);
+
+ if(!this.P.absolute) {
+ if(scaleAdjust) {
+ pair v=(s.xx,s.yy);
+ transform3 T=this.P.t;
+ pair x=project(X,T);
+ pair y=project(Y,T);
+ pair z=project(Z,T);
+ real f(pair a, pair b) {
+ return b == 0 ? (0.5*(a.x+a.y)) :
+ (b.x^2*a.x+b.y^2*a.y)/(b.x^2+b.y^2);
+ }
+ transform3 s=keepAspect ? scale3(min(f(v,x),f(v,y),f(v,z))) :
+ xscale3(f(v,x))*yscale3(f(v,y))*zscale3(f(v,z));
+ s=shift(this.P.target)*s*shift(-this.P.target);
+ t=s*t;
+ this.P=s*this.P;
+ this.P.bboxonly=false;
+ if(!is3D) pic2.erase();
+ f=pic.fit3(t,is3D ? null : pic2,this.P);
+ }
+
+ if(this.P.autoadjust || this.P.infinity)
+ adjusted=adjusted | this.P.adjust(min3(f),max3(f));
+ }
+ }
+ }
+
+ // Choose the angle to be just large enough to view the entire image.
+ real angle(projection P) {
+ T=identity4;
+ real h=-0.5*P.target.z;
+ pair r,R;
+ real diff=realMax;
+ pair s;
+ int i;
+ do {
+ r=minratio(f);
+ R=maxratio(f);
+ pair lasts=s;
+ if(P.autoadjust) {
+ s=r+R;
+ if(s != 0) {
+ transform3 t=shift(h*s.x,h*s.y,0);
+ f=t*f;
+ T=t*T;
+ adjusted=true;
+ }
+ }
+ diff=abs(s-lasts);
+ ++i;
+ } while (diff > angleprecision && i < maxangleiterations);
+ real aspect=width > 0 ? height/width : 1;
+ real rx=-r.x*aspect;
+ real Rx=R.x*aspect;
+ real ry=-r.y;
+ real Ry=R.y;
+ if(!P.autoadjust) {
+ if(rx > Rx) Rx=rx;
+ else rx=Rx;
+ if(ry > Ry) Ry=ry;
+ else ry=Ry;
+ }
+ return (1+angleprecision)*max(aTan(rx)+aTan(Rx),aTan(ry)+aTan(Ry));
+ }
+}
+
+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 F;
+ transform3 modelview;
+ projection P=S.P;
+ transform3 tinv=inverse(S.t);
+
+ projection Q;
+ modelview=P.T.modelview;
+ if(P.absolute) {
+ Q=modelview*P;
+ } 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);
+ triple M=max3(S.f);
+ triple lambda=M-m;
+ 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)
+ } else {
+ if(P.angle == 0) {
+ P.angle=S.angle(P);
+ modelview=S.T*modelview;
+ if(S.viewportmargin.y != 0)
+ P.angle=2*aTan(Tan(0.5*P.angle)-S.viewportmargin.y/P.target.z);
+ }
+ 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;
+ }
+ P=S.T*P;
+ }
+ if(settings.verbose > 0) {
+ if((P.center && settings.render != 0) || (!P.infinity && P.autoadjust))
+ write("adjusting target to ",tinv*target);
+ }
+ }
+
+ 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.
+ string dir=stripfile(prefix);
+ prefix=dir+replace(stripdirectory(prefix),
+ new string[][]{{" ","_"},{".","_"}});
+ if(settings.embed || nativeformat() == "pdf")
+ prefix += "+"+(string) file3.length;
+ } else
+ preview=false;
+ if(preview || (!prc && settings.render != 0)) {
+ frame f=S.f;
+ triple m,M;
+ real zcenter;
+ real r;
+ if(P.absolute) {
+ f=modelview*f;
+ m=min3(f);
+ M=max3(f);
+ r=0.5*abs(M-m);
+ zcenter=0.5*(M.z+m.z);
+ } else {
+ m=min3(f);
+ M=max3(f);
+ zcenter=P.target.z;
+ r=P.distance(m,M);
+ }
+ M=(M.x,M.y,zcenter+r);
+ m=(m.x,m.y,zcenter-r);
+
+ if(P.infinity) {
+ triple margin=(S.viewportmargin.x,S.viewportmargin.y,0);
+ M += margin;
+ m -= margin;
+ } else if(M.z >= 0) abort("camera too close");
+
+ shipout3(prefix,f,preview ? nativeformat() : format,
+ 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);
+ if(!preview) return F;
+ }
+
+ string image;
+ if((preview || (prc && settings.render == 0)) && settings.embed) {
+ image=prefix;
+ if(settings.inlinetex) image += "_0";
+ if(!preview && !shipped && !S.pic2.empty2()) {
+ transform T=S.pic2.scaling(S.width,S.height);
+ shipout(image,S.pic2.fit(T),newframe,nativeformat(),false,false,null);
+ }
+
+ image += "."+nativeformat();
+ if(!settings.inlinetex) file3.push(image);
+ image=graphic(image,"hiresbb");
+ }
+ if(prc) {
+ if(!P.infinity && P.viewportshift != 0)
+ 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);
+ }
+ return F;
+}
+
+object embed(string label="", string text=label,
+ string prefix=defaultfilename,
+ 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,
+ projection P=currentprojection)
+{
+ 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);
+ else {
+ object F;
+ transform T=S.pic2.scaling(xsize,ysize,keepAspect);
+ F.f=pic.fit(scale(S.t[0][0])*T);
+ add(F.f,S.pic2.fit());
+ return F;
+ }
+}
+
+object embed(string label="", string text=label,
+ string prefix=defaultfilename,
+ frame f, string format="", real width=0, real height=0,
+ bool view=true, string options="", string script="",
+ light light=currentlight, projection P=currentprojection)
+{
+ if(is3D(format))
+ return embed(label,text,prefix,scene(f,width,height,P),format,view,options,
+ script,light);
+ else {
+ object F;
+ F.f=f;
+ return F;
+ }
+}
+
+embed3=new object(string prefix, frame f, string format, string options,
+ string script, light light, projection P) {
+ return embed(prefix=prefix,f,format,options,script,light,P);
+};
+
+frame embedder(object embedder(string prefix, string format),
+ string prefix, string format, bool view, light light)
+{
+ frame f;
+ bool prc=prc(format);
+ if(!prc && settings.render != 0 && !view) {
+ static int previewcount=0;
+ bool keep=prefix != "";
+ prefix=outprefix(prefix)+"+"+(string) previewcount;
+ ++previewcount;
+ format=nativeformat();
+ if(!keep) file3.push(prefix+"."+format);
+ }
+ object F=embedder(prefix,format);
+ if(prc)
+ label(f,F.L);
+ else {
+ if(settings.render == 0) {
+ add(f,F.f);
+ if(light.background != nullpen)
+ box(f,light.background,Fill,above=false);
+ } else if(!view)
+ label(f,graphic(prefix,"hiresbb"));
+ }
+ return f;
+}
+
+currentpicture.fitter=new frame(string prefix, picture pic, string format,
+ real xsize, real ysize, bool keepAspect,
+ bool view, string options, string script,
+ light light, projection P) {
+ frame f;
+ bool empty3=pic.empty3();
+ if(!empty3) f=embedder(new object(string prefix, string format) {
+ return embed(prefix=prefix,pic,format,xsize,ysize,keepAspect,view,
+ options,script,light,P);
+ },prefix,format,view,light);
+ if(is3D(format) || empty3) add(f,pic.fit2(xsize,ysize,keepAspect));
+ return f;
+};
+
+frame embedder(string prefix, frame f, string format, real width, real height,
+ bool view, string options, string script, light light,
+ projection P)
+{
+ return embedder(new object(string prefix, string format) {
+ return embed(prefix=prefix,f,format,width,height,view,options,script,
+ light,P);
+ },prefix,format,view,light);
+}
+
+projection[][] ThreeViewsUS={{TopView},
+ {FrontView,RightView}};
+
+projection[][] SixViewsUS={{null,TopView},
+ {LeftView,FrontView,RightView,BackView},
+ {null,BottomView}};
+
+projection[][] ThreeViewsFR={{RightView,FrontView},
+ {null,TopView}};
+
+projection[][] SixViewsFR={{null,BottomView},
+ {RightView,FrontView,LeftView,BackView},
+ {null,TopView}};
+
+projection[][] ThreeViews={{FrontView,TopView,RightView}};
+
+projection[][] SixViews={{FrontView,TopView,RightView},
+ {BackView,BottomView,LeftView}};
+
+void addViews(picture dest, picture src, projection[][] views=SixViewsUS,
+ bool group=true, filltype filltype=NoFill)
+{
+ frame[][] F=array(views.length,new frame[]);
+ pair[][] M=array(views.length,new pair[]);
+ pair[][] m=array(views.length,new pair[]);
+
+ for(int i=0; i < views.length; ++i) {
+ projection[] viewsi=views[i];
+ frame[] Fi=F[i];
+ pair[] Mi=M[i];
+ pair[] mi=m[i];
+ for(projection P : viewsi) {
+ if(P != null) {
+ frame f=src.fit(P);
+ mi.push(min(f));
+ Mi.push(max(f));
+ Fi.push(f);
+ } else {
+ pair Infinity=(infinity,infinity);
+ mi.push(Infinity);
+ Mi.push(-Infinity);
+ Fi.push(newframe);
+ }
+ }
+ }
+
+ real[] my=new real[views.length];
+ real[] My=new real[views.length];
+
+ int Nj=0;
+ for(int i=0; i < views.length; ++i) {
+ my[i]=minbound(m[i]).y;
+ My[i]=maxbound(M[i]).y;
+ Nj=max(Nj,views[i].length);
+ }
+
+ real[] mx=array(Nj,infinity);
+ real[] Mx=array(Nj,-infinity);
+ for(int i=0; i < views.length; ++i) {
+ pair[] mi=m[i];
+ pair[] Mi=M[i];
+ for(int j=0; j < views[i].length; ++j) {
+ mx[j]=min(mx[j],mi[j].x);
+ Mx[j]=max(Mx[j],Mi[j].x);
+ }
+ }
+
+ if(group) begingroup(dest);
+
+ real y;
+ for(int i=0; i < views.length; ++i) {
+ real x;
+ pair[] mi=m[i];
+ for(int j=0; j < views[i].length; ++j) {
+ if(size(F[i][j]) != 0)
+ add(dest,shift(x-mx[j],y+my[i])*F[i][j],filltype);
+ x += (Mx[j]-mx[j]);
+ }
+ y -= (My[i]-my[i]);
+ }
+
+ if(group) endgroup(dest);
+}
+
+void addViews(picture src, projection[][] views=SixViewsUS, bool group=true,
+ filltype filltype=NoFill)
+{
+ addViews(currentpicture,src,views,group,filltype);
+}
+
+void addStereoViews(picture dest, picture src, bool group=true,
+ filltype filltype=NoFill, real eyetoview=defaulteyetoview,
+ bool leftright=true, projection P=currentprojection)
+{
+ triple v=P.vector();
+ triple h=0.5*abs(v)*eyetoview*unit(cross(P.up,v));
+ projection leftEye=P.copy();
+ leftEye.camera -= h;
+ leftEye.calculate();
+ projection rightEye=P.copy();
+ rightEye.camera += h;
+ rightEye.calculate();
+ addViews(dest,src,leftright ?
+ new projection[][] {{leftEye,rightEye}} :
+ new projection[][] {{rightEye,leftEye}},group,filltype);
+}
+
+void addStereoViews(picture src, bool group=true,
+ filltype filltype=NoFill,
+ real eyetoview=defaulteyetoview, bool leftright=true,
+ projection P=currentprojection)
+{
+ addStereoViews(currentpicture,src,group,filltype,eyetoview,leftright,P);
+}
+
+// Fit an array of 3D pictures simultaneously using the sizing of picture all.
+frame[] fit3(string prefix="", picture[] pictures, picture all,
+ string format="", bool view=true, string options="",
+ string script="", light light=currentlight,
+ projection P=currentprojection)
+{
+ frame[] out;
+ scene S=scene(all,P);
+ triple m=all.min(S.t);
+ triple M=all.max(S.t);
+ out=new frame[pictures.length];
+ int i=0;
+ bool reverse=settings.reverse;
+ settings.animating=true;
+
+ for(picture pic : pictures) {
+ picture pic2;
+ frame f=pic.fit3(S.t,pic2,S.P);
+ if(settings.interrupt) break;
+ add(f,pic2.fit2());
+ draw(f,m,nullpen);
+ draw(f,M,nullpen);
+ out[i]=f;
+ ++i;
+ }
+
+ while(!settings.interrupt) {
+ for(int i=settings.reverse ? pictures.length-1 : 0;
+ i >= 0 && i < pictures.length && !settings.interrupt;
+ settings.reverse ? --i : ++i) {
+ frame f=embedder(prefix,out[i],format,S.width,S.height,view,options,
+ script,light,S.P);
+ if(!settings.loop) out[i]=f;
+ }
+ if(!settings.loop) break;
+ }
+
+ settings.animating=false;
+ settings.interrupt=false;
+ settings.reverse=reverse;
+
+ return out;
+}
+
+// Fit an array of pictures simultaneously using the size of the first picture.
+fit=new frame[](string prefix="", picture[] pictures, string format="",
+ bool view=true, string options="", string script="",
+ projection P=currentprojection) {
+ if(pictures.length == 0)
+ return new frame[];
+
+ picture all;
+ size(all,pictures[0]);
+ for(picture pic : pictures)
+ add(all,pic);
+
+ return all.empty3() ? fit2(pictures,all) :
+ fit3(prefix,pictures,all,format,view,options,script,P);
+};
+
+// Add frame src to picture dest about position.
+void add(picture dest=currentpicture, frame src, triple position)
+{
+ if(is3D(src)) {
+ dest.add(new void(frame f, transform3 t, picture, projection) {
+ add(f,shift(t*position)*src);
+ },true);
+ } else {
+ dest.add(new void(frame, transform3 t, picture pic, projection P) {
+ if(pic != null) {
+ pic.add(new void(frame f, transform T) {
+ add(f,T*shift(project(t*position,P))*src);
+ },true);
+ }
+ },true);
+ }
+ dest.addBox(position,position,min3(src),max3(src));
+}
+
+exitfcn currentexitfunction=atexit();
+
+void exitfunction()
+{
+ if(currentexitfunction != null) currentexitfunction();
+ if(!settings.keep)
+ for(int i=0; i < file3.length; ++i)
+ delete(file3[i]);
+ file3=new string[];
+}
+
+atexit(exitfunction);
diff --git a/Master/texmf-dist/asymptote/three_arrows.asy b/Master/texmf-dist/asymptote/three_arrows.asy
new file mode 100644
index 00000000000..8346a5bc6c1
--- /dev/null
+++ b/Master/texmf-dist/asymptote/three_arrows.asy
@@ -0,0 +1,730 @@
+// A transformation that bends points along a path
+transform3 bend(path3 g, real t)
+{
+ triple dir=dir(g,t);
+ triple a=point(g,0), b=postcontrol(g,0);
+ triple c=precontrol(g,1), d=point(g,1);
+ triple dir1=b-a;
+ triple dir2=c-b;
+ triple dir3=d-c;
+
+ triple u = unit(cross(dir1,dir3));
+ real eps=1000*realEpsilon;
+ if(abs(u) < eps) {
+ u = unit(cross(dir1,dir2));
+ if(abs(u) < eps) {
+ u = unit(cross(dir2,dir3));
+ if(abs(u) < eps)
+ // linear segment: use any direction perpendicular to initial direction
+ u = perp(dir1);
+ }
+ }
+ u = unit(perp(u,dir));
+
+ triple w=cross(dir,u);
+ triple q=point(g,t);
+ return new real[][] {
+ {u.x,w.x,dir.x,q.x},
+ {u.y,w.y,dir.y,q.y},
+ {u.z,w.z,dir.z,q.z},
+ {0,0,0,1}
+ };
+}
+
+// bend a point along a path; assumes that p.z is in [0,scale]
+triple bend(triple p, path3 g, real scale)
+{
+ return bend(g,arctime(g,arclength(g)+p.z-scale))*(p.x,p.y,0);
+}
+
+void bend(surface s, path3 g, real L)
+{
+ for(patch p : s.s) {
+ for(int i=0; i < 4; ++i) {
+ for(int j=0; j < 4; ++j) {
+ p.P[i][j]=bend(p.P[i][j],g,L);
+ }
+ }
+ }
+}
+
+// Refine a noncyclic path3 g so that it approaches its endpoint in
+// geometrically spaced steps.
+path3 approach(path3 p, int n, real radix=3)
+{
+ guide3 G;
+ real L=length(p);
+ real tlast=0;
+ real r=1/radix;
+ for(int i=1; i < n; ++i) {
+ real t=L*(1-r^i);
+ G=G&subpath(p,tlast,t);
+ tlast=t;
+ }
+ return G&subpath(p,tlast,L);
+}
+
+struct arrowhead3
+{
+ arrowhead arrowhead2=DefaultHead;
+ real size(pen p)=arrowsize;
+ real arcsize(pen p)=arcarrowsize;
+ real gap=1;
+ real size;
+ bool splitpath=true;
+
+ surface head(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0, real angle=arrowangle,
+ filltype filltype=null, bool forwards=true,
+ projection P=currentprojection);
+
+ static surface surface(path3 g, position position, real size,
+ path[] h, pen p, filltype filltype,
+ triple normal, projection P) {
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+ path3 r=subpath(g,position,0);
+ path3 s=subpath(r,arctime(r,size),0);
+ if(filltype == null) filltype=FillDraw(p);
+ bool draw=filltype.type != filltype.Fill;
+ triple v=point(s,length(s));
+ triple N=normal == O ? P.normal : normal;
+ triple w=unit(v-point(s,0));
+ transform3 t=transform3(w,unit(cross(w,N)));
+ path3[] H=t*path3(h);
+ surface s;
+ real width=linewidth(p);
+ if(filltype != NoFill && filltype.type != filltype.UnFill &&
+ filltype.type != filltype.Draw) {
+ triple n=0.5*width*unit(t*Z);
+ s=surface(shift(n)*H,planar=true);
+ s.append(surface(shift(-n)*H,planar=true));
+ if(!draw)
+ for(path g : h)
+ s.append(shift(-n)*t*extrude(g,width*Z));
+ }
+ if(draw)
+ for(path3 g : H)
+ s.append(tube(g,width).s);
+ return shift(v)*s;
+ }
+
+ static path project(path3 g, bool forwards, projection P) {
+ path h=project(forwards ? g : reverse(g),P);
+ return shift(-point(h,length(h)))*h;
+ }
+
+ 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;
+ }
+}
+
+arrowhead3 DefaultHead3;
+DefaultHead3.head=new surface(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0,
+ real angle=arrowangle, filltype filltype=null,
+ bool forwards=true,
+ projection P=currentprojection)
+{
+ if(size == 0) size=DefaultHead3.size(p);
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+
+ path3 r=subpath(g,position,0);
+ path3 s=subpath(r,arctime(r,size),0);
+ int n=length(s);
+ bool straight1=n == 1 && straight(g,0);
+ real aspect=Tan(angle);
+ real width=size*aspect;
+ surface head;
+ if(straight1) {
+ triple v=point(s,0);
+ triple u=point(s,1)-v;
+ return shift(v)*align(unit(u))*scale(width,width,size)*unitsolidcone;
+ } else {
+ real remainL=size;
+ bool first=true;
+ for(int i=0; i < n; ++i) {
+ render(subpath(s,i,i+1),new void(path3 q, real) {
+ if(remainL > 0) {
+ real l=arclength(q);
+ real w=remainL*aspect;
+ surface segment=scale(w,w,l)*unitcylinder;
+ if(first) { // add base
+ first=false;
+ segment.append(scale(w,w,1)*unitdisk);
+ }
+ for(patch p : segment.s) {
+ for(int i=0; i < 4; ++i) {
+ for(int j=0; j < 4; ++j) {
+ real k=1-p.P[i][j].z/remainL;
+ p.P[i][j]=bend((k*p.P[i][j].x,k*p.P[i][j].y,p.P[i][j].z),q,l);
+ }
+ }
+ }
+ head.append(segment);
+ remainL -= l;
+ }
+ });
+ }
+ }
+ return head;
+};
+
+arrowhead3 HookHead3(real dir=arrowdir, real barb=arrowbarb)
+{
+ arrowhead3 a;
+ a.head=new surface(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0, real angle=arrowangle,
+ filltype filltype=null, bool forwards=true,
+ projection P=currentprojection) {
+ if(size == 0) size=a.size(p);
+
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+
+ path3 r=subpath(g,position,0);
+ path3 s=subpath(r,arctime(r,size),0);
+ bool straight1=length(s) == 1 && straight(g,0);
+ path3 H=path3(HookHead(dir,barb).head((0,0)--(0,size),p,size,angle),
+ YZplane);
+ surface head=surface(O,reverse(approach(subpath(H,1,0),7,1.5))&
+ approach(subpath(H,1,2),4,2),Z);
+
+ if(straight1) {
+ triple v=point(s,0);
+ triple u=point(s,1)-v;
+ return shift(v)*align(unit(u))*head;
+ } else {
+ bend(head,s,size);
+ return head;
+ }
+ };
+ a.arrowhead2=HookHead;
+ a.gap=0.7;
+ return a;
+}
+arrowhead3 HookHead3=HookHead3();
+
+arrowhead3 TeXHead3;
+TeXHead3.size=TeXHead.size;
+TeXHead3.arcsize=TeXHead.size;
+TeXHead3.arrowhead2=TeXHead;
+TeXHead3.head=new surface(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0, real angle=arrowangle,
+ filltype filltype=null, bool forwards=true,
+ projection P=currentprojection)
+{
+ real texsize=TeXHead3.size(p);
+ if(size == 0) size=texsize;
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) position=reltime(g,position);
+
+ path3 r=subpath(g,position,0);
+ path3 s=subpath(r,arctime(r,size),0);
+ bool straight1=length(s) == 1 && straight(g,0);
+
+ surface head=surface(O,approach(subpath(path3(TeXHead.head((0,0)--(0,1),p,
+ size),
+ YZplane),5,0),8,1.5),Z);
+ if(straight1) {
+ triple v=point(s,0);
+ triple u=point(s,1)-v;
+ return shift(v)*align(unit(u))*head;
+ } else {
+ path3 s=subpath(r,arctime(r,size/texsize*arrowsize(p)),0);
+ bend(head,s,size);
+ return head;
+ }
+};
+
+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};
+}
+
+path3 arrowbase(path3 r, triple y, real t, real size)
+{
+ triple perp=2*size*perp(dir(r,t));
+ return size == 0 ? y : y+perp--y-perp;
+}
+
+arrowhead3 DefaultHead2(triple normal=O) {
+ arrowhead3 a;
+ a.head=new surface(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0,
+ real angle=arrowangle,
+ filltype filltype=null, bool forwards=true,
+ projection P=currentprojection) {
+ if(size == 0) size=a.size(p);
+ path h=a.project(g,forwards,P);
+ a.size=min(size,arclength(h));
+ path[] H=a.align(DefaultHead.head(h,p,size,angle),h);
+ H=forwards ? yscale(-1)*H : H;
+ return a.surface(g,position,size,H,p,filltype,normal,P);
+ };
+ a.gap=1.005;
+ return a;
+}
+arrowhead3 DefaultHead2=DefaultHead2();
+
+arrowhead3 HookHead2(real dir=arrowdir, real barb=arrowbarb, triple normal=O)
+{
+ arrowhead3 a;
+ a.head=new surface(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0, real angle=arrowangle,
+ filltype filltype=null, bool forwards=true,
+ projection P=currentprojection) {
+ if(size == 0) size=a.size(p);
+ path h=a.project(g,forwards,P);
+ a.size=min(size,arclength(h));
+ path[] H=a.align(HookHead.head(h,p,size,angle),h);
+ H=forwards ? yscale(-1)*H : H;
+ return a.surface(g,position,size,H,p,filltype,normal,P);
+ };
+ a.arrowhead2=HookHead;
+ a.gap=1.005;
+ return a;
+}
+arrowhead3 HookHead2=HookHead2();
+
+arrowhead3 TeXHead2(triple normal=O) {
+ arrowhead3 a;
+ a.head=new surface(path3 g, position position=EndPoint,
+ pen p=currentpen, real size=0,
+ real angle=arrowangle, filltype filltype=null,
+ bool forwards=true, projection P=currentprojection) {
+ if(size == 0) size=a.size(p);
+ path h=a.project(g,forwards,P);
+ a.size=min(size,arclength(h));
+ h=rotate(-degrees(dir(h,length(h)),warn=false))*h;
+ path[] H=TeXHead.head(h,p,size,angle);
+ H=forwards ? yscale(-1)*H : H;
+ return a.surface(g,position,size,H,p,
+ filltype == null ? TeXHead.defaultfilltype(p) : filltype,
+ normal,P);
+ };
+ a.arrowhead2=TeXHead;
+ a.size=TeXHead.size;
+ a.splitpath=false;
+ a.gap=1.005;
+ return a;
+}
+arrowhead3 TeXHead2=TeXHead2();
+
+private real position(position position, real size, path3 g, bool center)
+{
+ bool relative=position.relative;
+ real position=position.position.x;
+ if(relative) {
+ position *= arclength(g);
+ if(center) position += 0.5*size;
+ position=arctime(g,position);
+ } else if(center)
+ position=arctime(g,arclength(subpath(g,0,position))+0.5*size);
+ return position;
+}
+
+void drawarrow(picture pic, arrowhead3 arrowhead=DefaultHead3,
+ path3 g, material p=currentpen, material arrowheadpen=nullpen,
+ real size=0, real angle=arrowangle, position position=EndPoint,
+ filltype filltype=null, bool forwards=true,
+ margin3 margin=NoMargin3, bool center=false, light light=nolight,
+ light arrowheadlight=currentlight,
+ projection P=currentprojection)
+{
+ pen q=(pen) p;
+ if(filltype != null) {
+ if(arrowheadpen == nullpen && filltype != null)
+ arrowheadpen=filltype.fillpen;
+ if(arrowheadpen == nullpen && filltype != null)
+ arrowheadpen=filltype.drawpen;
+ }
+ if(arrowheadpen == nullpen) arrowheadpen=p;
+ if(size == 0) size=arrowhead.size(q);
+ size=min(arrowsizelimit*arclength(g),size);
+ real position=position(position,size,g,center);
+
+ g=margin(g,q).g;
+ int L=length(g);
+ if(!forwards) {
+ g=reverse(g);
+ position=L-position;
+ }
+ path3 r=subpath(g,position,0);
+ size=min(arrowsizelimit*arclength(r),size);
+ surface head=arrowhead.head(g,position,q,size,angle,filltype,forwards,P);
+ if(arrowhead.size > 0) size=arrowhead.size;
+ bool endpoint=position > L-sqrtEpsilon;
+ if(arrowhead.splitpath || endpoint) {
+ if(position > 0) {
+ real Size=size*arrowhead.gap;
+ draw(pic,subpath(r,arctime(r,Size),length(r)),p,light);
+ }
+ if(!endpoint)
+ draw(pic,subpath(g,position,L),p,light);
+ } else draw(pic,g,p,light);
+ draw(pic,head,arrowheadpen,arrowheadlight);
+}
+
+void drawarrow2(picture pic, arrowhead3 arrowhead=DefaultHead3,
+ path3 g, material p=currentpen, material arrowheadpen=nullpen,
+ real size=0, real angle=arrowangle, filltype filltype=null,
+ margin3 margin=NoMargin3, light light=nolight,
+ light arrowheadlight=currentlight,
+ projection P=currentprojection)
+{
+ pen q=(pen) p;
+ if(filltype != null) {
+ if(arrowheadpen == nullpen && filltype != null)
+ arrowheadpen=filltype.fillpen;
+ if(arrowheadpen == nullpen && filltype != null)
+ arrowheadpen=filltype.drawpen;
+ }
+ if(arrowheadpen == nullpen) arrowheadpen=p;
+ if(size == 0) size=arrowhead.size(q);
+ g=margin(g,q).g;
+ size=min(arrow2sizelimit*arclength(g),size);
+
+ path3 r=reverse(g);
+ int L=length(g);
+ real Size=size*arrowhead.gap;
+ draw(pic,subpath(r,arctime(r,Size),L-arctime(g,Size)),p,light);
+ draw(pic,arrowhead.head(g,L,q,size,angle,filltype,forwards=true,P),
+ arrowheadpen,arrowheadlight);
+ draw(pic,arrowhead.head(r,L,q,size,angle,filltype,forwards=false,P),
+ arrowheadpen,arrowheadlight);
+}
+
+// Add to picture an estimate of the bounding box contribution of arrowhead
+// using the local slope at endpoint.
+void addArrow(picture pic, arrowhead3 arrowhead, path3 g, pen p, real size,
+ real angle, filltype filltype, real position)
+{
+ triple v=point(g,position);
+ path3 g=v-(size+linewidth(p))*dir(g,position)--v;
+ surface s=arrowhead.head(g,position,p,size,angle);
+ if(s.s.length > 0) {
+ pic.addPoint(v,min(s)-v);
+ pic.addPoint(v,max(s)-v);
+ } else pic.addPoint(v);
+}
+
+picture arrow(arrowhead3 arrowhead=DefaultHead3,
+ path3 g, material p=currentpen, material arrowheadpen=p,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=EndPoint,
+ bool forwards=true, margin3 margin=NoMargin3,
+ bool center=false, light light=nolight,
+ light arrowheadlight=currentlight)
+{
+ pen q=(pen) p;
+ if(size == 0) size=arrowhead.size(q);
+ picture pic;
+ if(is3D())
+ pic.add(new void(frame f, transform3 t, picture pic2, projection P) {
+ picture opic;
+ drawarrow(opic,arrowhead,t*g,p,arrowheadpen,size,angle,position,
+ filltype,forwards,margin,center,light,arrowheadlight,P);
+ add(f,opic.fit3(identity4,pic2,P));
+ });
+
+ addPath(pic,g,q);
+
+ real position=position(position,size,g,center);
+ path3 G;
+ if(!forwards) {
+ G=reverse(g);
+ position=length(g)-position;
+ } else G=g;
+ addArrow(pic,arrowhead,G,q,size,angle,filltype,position);
+
+ return pic;
+}
+
+picture arrow2(arrowhead3 arrowhead=DefaultHead3,
+ path3 g, material p=currentpen, material arrowheadpen=p,
+ real size=0, real angle=arrowangle, filltype filltype=null,
+ margin3 margin=NoMargin3, light light=nolight,
+ light arrowheadlight=currentlight)
+{
+ pen q=(pen) p;
+ if(size == 0) size=arrowhead.size(q);
+ picture pic;
+
+ if(is3D())
+ pic.add(new void(frame f, transform3 t, picture pic2, projection P) {
+ picture opic;
+ drawarrow2(opic,arrowhead,t*g,p,arrowheadpen,size,angle,filltype,
+ margin,light,arrowheadlight,P);
+ add(f,opic.fit3(identity4,pic2,P));
+ });
+
+ addPath(pic,g,q);
+
+ int L=length(g);
+ addArrow(pic,arrowhead,g,q,size,angle,filltype,L);
+ addArrow(pic,arrowhead,reverse(g),q,size,angle,filltype,L);
+
+ return pic;
+}
+
+void add(picture pic, arrowhead3 arrowhead, real size, real angle,
+ filltype filltype, position position, material arrowheadpen,
+ path3 g, material p, bool forwards=true, margin3 margin,
+ bool center=false, light light, light arrowheadlight)
+{
+ add(pic,arrow(arrowhead,g,p,arrowheadpen,size,angle,filltype,position,
+ forwards,margin,center,light,arrowheadlight));
+ if(!is3D()) {
+ pic.add(new void(frame f, transform3 t, picture pic, projection P) {
+ if(pic != null) {
+ pen q=(pen) p;
+ path3 G=t*g;
+ marginT3 m=margin(G,q);
+ add(pic,arrow(arrowhead.arrowhead2,project(G,P),q,size,angle,
+ filltype == null ?
+ arrowhead.arrowhead2.defaultfilltype
+ ((pen) arrowheadpen) : filltype,position,
+ forwards,TrueMargin(m.begin,m.end),center));
+ }
+ },true);
+ }
+}
+
+void add2(picture pic, arrowhead3 arrowhead, real size, real angle,
+ filltype filltype, material arrowheadpen, path3 g, material p,
+ margin3 margin, light light, light arrowheadlight)
+{
+ add(pic,arrow2(arrowhead,g,p,arrowheadpen,size,angle,filltype,margin,light,
+ arrowheadlight));
+ if(!is3D()) {
+ pic.add(new void(frame f, transform3 t, picture pic, projection P) {
+ if(pic != null) {
+ pen q=(pen) p;
+ path3 G=t*g;
+ marginT3 m=margin(G,q);
+ add(pic,arrow2(arrowhead.arrowhead2,project(G,P),q,size,angle,
+ filltype == null ?
+ arrowhead.arrowhead2.defaultfilltype
+ ((pen) arrowheadpen) : filltype,
+ TrueMargin(m.begin,m.end)));
+ }
+ },true);
+ }
+}
+
+void bar(picture pic, triple a, triple d, triple perp=O,
+ material p=currentpen, light light=nolight)
+{
+ d *= 0.5;
+ perp *= 0.5;
+ pic.add(new void(frame f, transform3 t, picture pic2, projection P) {
+ picture opic;
+ triple A=t*a;
+ triple v=d == O ? abs(perp)*unit(cross(P.normal,perp)) : d;
+ draw(opic,A-v--A+v,p,light);
+ add(f,opic.fit3(identity4,pic2,P));
+ });
+ triple v=d == O ? cross(currentprojection.normal,perp) : d;
+ pen q=(pen) p;
+ triple m=min3(q);
+ triple M=max3(q);
+ pic.addPoint(a,-v-m);
+ pic.addPoint(a,-v+m);
+ pic.addPoint(a,v-M);
+ pic.addPoint(a,v+M);
+}
+
+picture bar(triple a, triple dir, triple perp=O, material p=currentpen)
+{
+ picture pic;
+ bar(pic,a,dir,perp,p);
+ return pic;
+}
+
+typedef bool arrowbar3(picture, path3, material, margin3, light, light);
+
+bool Blank(picture, path3, material, margin3, light, light)
+{
+ return false;
+}
+
+bool None(picture, path3, material, margin3, light, light)
+{
+ return true;
+}
+
+arrowbar3 BeginArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=BeginPoint,
+ material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p,
+ forwards=false,margin,light,arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 Arrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=EndPoint,
+ material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p,margin,
+ light,arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 EndArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, position position=EndPoint,
+ material arrowheadpen=nullpen)=Arrow3;
+
+arrowbar3 MidArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ add(pic,arrowhead,size,angle,filltype,MidPoint,
+ arrowheadpen,g,p,margin,center=true,light,arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 Arrows3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arrowangle,
+ filltype filltype=null, material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ add2(pic,arrowhead,size,angle,filltype,arrowheadpen,g,p,margin,light,
+ arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 BeginArcArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null, position position=BeginPoint,
+ material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ real size=size == 0 ? arrowhead.arcsize((pen) p) : size;
+ add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p,
+ forwards=false,margin,light,arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 ArcArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null, position position=EndPoint,
+ material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ real size=size == 0 ? arrowhead.arcsize((pen) p) : size;
+ add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p,margin,
+ light,arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 EndArcArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null,
+ position position=EndPoint,
+ material arrowheadpen=nullpen)=ArcArrow3;
+
+
+arrowbar3 MidArcArrow3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null, material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ real size=size == 0 ? arrowhead.arcsize((pen) p) : size;
+ add(pic,arrowhead,size,angle,filltype,MidPoint,arrowheadpen,g,p,margin,
+ center=true,light,arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 ArcArrows3(arrowhead3 arrowhead=DefaultHead3,
+ real size=0, real angle=arcarrowangle,
+ filltype filltype=null, material arrowheadpen=nullpen)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin,
+ light light, light arrowheadlight) {
+ real size=size == 0 ? arrowhead.arcsize((pen) p) : size;
+ add2(pic,arrowhead,size,angle,filltype,arrowheadpen,g,p,margin,light,
+ arrowheadlight);
+ return false;
+ };
+}
+
+arrowbar3 BeginBar3(real size=0, triple dir=O)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin, light light,
+ light) {
+ real size=size == 0 ? barsize((pen) p) : size;
+ bar(pic,point(g,0),size*unit(dir),size*dir(g,0),p,light);
+ return true;
+ };
+}
+
+arrowbar3 Bar3(real size=0, triple dir=O)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin, light light,
+ light) {
+ int L=length(g);
+ real size=size == 0 ? barsize((pen) p) : size;
+ bar(pic,point(g,L),size*unit(dir),size*dir(g,L),p,light);
+ return true;
+ };
+}
+
+arrowbar3 EndBar3(real size=0, triple dir=O)=Bar3;
+
+arrowbar3 Bars3(real size=0, triple dir=O)
+{
+ return new bool(picture pic, path3 g, material p, margin3 margin, light light,
+ light) {
+ real size=size == 0 ? barsize((pen) p) : size;
+ BeginBar3(size,dir)(pic,g,p,margin,light,nolight);
+ EndBar3(size,dir)(pic,g,p,margin,light,nolight);
+ return true;
+ };
+}
+
+arrowbar3 BeginArrow3=BeginArrow3(),
+MidArrow3=MidArrow3(),
+Arrow3=Arrow3(),
+EndArrow3=Arrow3(),
+Arrows3=Arrows3(),
+BeginArcArrow3=BeginArcArrow3(),
+MidArcArrow3=MidArcArrow3(),
+ArcArrow3=ArcArrow3(),
+EndArcArrow3=ArcArrow3(),
+ArcArrows3=ArcArrows3(),
+BeginBar3=BeginBar3(),
+Bar3=Bar3(),
+EndBar3=Bar3(),
+Bars3=Bars3();
diff --git a/Master/texmf-dist/asymptote/three_light.asy b/Master/texmf-dist/asymptote/three_light.asy
new file mode 100644
index 00000000000..eb85ed0c3e5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/three_light.asy
@@ -0,0 +1,117 @@
+struct material {
+ pen[] p; // diffusepen,ambientpen,emissivepen,specularpen
+ real opacity;
+ real shininess;
+ void operator init(pen diffusepen=black, pen ambientpen=black,
+ pen emissivepen=black, pen specularpen=mediumgray,
+ real opacity=opacity(diffusepen),
+ real shininess=defaultshininess) {
+ p=new pen[] {diffusepen,ambientpen,emissivepen,specularpen};
+ this.opacity=opacity;
+ this.shininess=shininess;
+ }
+ void operator init(material m) {
+ p=copy(m.p);
+ opacity=m.opacity;
+ shininess=m.shininess;
+ }
+ pen diffuse() {return p[0];}
+ pen ambient() {return p[1];}
+ pen emissive() {return p[2];}
+ pen specular() {return p[3];}
+
+ void diffuse(pen q) {p[0]=q;}
+ void ambient(pen q) {p[1]=q;}
+ void emissive(pen q) {p[2]=q;}
+ void specular(pen q) {p[3]=q;}
+}
+
+void write(file file, string s="", material x, suffix suffix=none)
+{
+ write(file,s);
+ write(file,"{");
+ write(file,"diffuse=",x.diffuse());
+ write(file,", ambient=",x.ambient());
+ write(file,", emissive=",x.emissive());
+ write(file,", specular=",x.specular());
+ write(file,", opacity=",x.opacity);
+ write(file,", shininess=",x.shininess);
+ write(file,"}",suffix);
+}
+
+void write(string s="", material x, suffix suffix=endl)
+{
+ write(stdout,s,x,suffix);
+}
+
+bool operator == (material m, material n)
+{
+ return all(m.p == n.p) && m.opacity == n.opacity &&
+ m.shininess == n.shininess;
+}
+
+material operator cast(pen p)
+{
+ return material(p);
+}
+
+material[] operator cast(pen[] p)
+{
+ return sequence(new material(int i) {return p[i];},p.length);
+}
+
+pen operator ecast(material m)
+{
+ return m.p.length > 0 ? m.diffuse() : nullpen;
+}
+
+material emissive(material m)
+{
+ return material(black+opacity(m.opacity),black,m.diffuse(),black,m.opacity,1);
+}
+
+pen color(triple normal, material m, light light, transform3 T=light.T) {
+ triple[] position=light.position;
+ if(invisible((pen) m)) return invisible;
+ if(position.length == 0) return m.diffuse();
+ normal=unit(T*normal);
+ if(settings.twosided) normal *= sgn(normal.z);
+ real s=m.shininess*128;
+ real[] Diffuse=rgba(m.diffuse());
+ real[] Ambient=rgba(m.ambient());
+ real[] Specular=rgba(m.specular());
+ real[] p=rgba(m.emissive());
+ for(int i=0; i < position.length; ++i) {
+ triple L=light.viewport ? position[i] : T*position[i];
+ real Ldotn=max(dot(normal,L),0);
+ p += light.ambient[i]*Ambient+Ldotn*light.diffuse[i]*Diffuse;
+ // Apply specularfactor to partially compensate non-pixel-based rendering.
+ if(Ldotn > 0) // Phong-Blinn model of specular reflection
+ p += dot(normal,unit(L+Z))^s*light.specularfactor*
+ light.specular[i]*Specular;
+ }
+ return rgb(p[0],p[1],p[2])+opacity(opacity(m.diffuse()));
+}
+
+light operator * (transform3 t, light light)
+{
+ light light=light(light);
+ if(!light.viewport) light.position=shiftless(t)*light.position;
+ return light;
+}
+
+light operator cast(triple v) {return light(v);}
+
+light Viewport=light(ambient=gray(0.1),specularfactor=3,viewport=true,
+ (0.25,-0.25,1));
+
+light White=light(new pen[] {rgb(0.38,0.38,0.45),rgb(0.6,0.6,0.67),
+ rgb(0.5,0.5,0.57)},specularfactor=3,
+ new triple[] {(-2,-1.5,-0.5),(2,1.1,-2.5),(-0.5,0,2)});
+
+light Headlamp=light(gray(0.8),ambient=gray(0.1),specular=gray(0.7),
+ specularfactor=3,viewport=true,dir(42,48));
+
+currentlight=Headlamp;
+
+light nolight;
diff --git a/Master/texmf-dist/asymptote/three_margins.asy b/Master/texmf-dist/asymptote/three_margins.asy
new file mode 100644
index 00000000000..b6f286ee4ff
--- /dev/null
+++ b/Master/texmf-dist/asymptote/three_margins.asy
@@ -0,0 +1,104 @@
+struct marginT3 {
+ path3 g;
+ real begin,end;
+};
+
+typedef marginT3 margin3(path3, pen);
+
+path3 trim(path3 g, real begin, real end) {
+ real a=arctime(g,begin);
+ real b=arctime(g,arclength(g)-end);
+ return a <= b ? subpath(g,a,b) : point(g,a);
+}
+
+margin3 operator +(margin3 ma, margin3 mb)
+{
+ return new marginT3(path3 g, pen p) {
+ marginT3 margin;
+ real ba=ma(g,p).begin < 0 ? 0 : ma(g,p).begin;
+ real bb=mb(g,p).begin < 0 ? 0 : mb(g,p).begin;
+ real ea=ma(g,p).end < 0 ? 0 : ma(g,p).end;
+ real eb=mb(g,p).end < 0 ? 0 : mb(g,p).end;
+ margin.begin=ba+bb;
+ margin.end=ea+eb;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin3 NoMargin3()
+{
+ return new marginT3(path3 g, pen) {
+ marginT3 margin;
+ margin.begin=margin.end=0;
+ margin.g=g;
+ return margin;
+ };
+}
+
+margin3 Margin3(real begin, real end)
+{
+ return new marginT3(path3 g, pen p) {
+ marginT3 margin;
+ real factor=labelmargin(p);
+ real w=0.5*linewidth(p);
+ margin.begin=begin*factor-w;
+ margin.end=end*factor-w;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin3 PenMargin3(real begin, real end)
+{
+ return new marginT3(path3 g, pen p) {
+ marginT3 margin;
+ real factor=linewidth(p);
+ margin.begin=begin*factor;
+ margin.end=end*factor;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin3 DotMargin3(real begin, real end)
+{
+ return new marginT3(path3 g, pen p) {
+ marginT3 margin;
+ real margindot(real x) {return x > 0 ? dotfactor*x : x;}
+ real factor=linewidth(p);
+ margin.begin=margindot(begin)*factor;
+ margin.end=margindot(end)*factor;
+ margin.g=trim(g,margin.begin,margin.end);
+ return margin;
+ };
+}
+
+margin3 TrueMargin3(real begin, real end)
+{
+ return new marginT3(path3 g, pen p) {
+ marginT3 margin;
+ margin.begin=begin;
+ margin.end=end;
+ margin.g=trim(g,begin,end);
+ return margin;
+ };
+}
+
+margin3 NoMargin3=NoMargin3(),
+ BeginMargin3=Margin3(1,0),
+ Margin3=Margin3(0,1),
+ EndMargin3=Margin3,
+ Margins3=Margin3(1,1),
+ BeginPenMargin3=PenMargin3(0.5,-0.5),
+ BeginPenMargin2=PenMargin3(1.0,-0.5),
+ PenMargin3=PenMargin3(-0.5,0.5),
+ PenMargin2=PenMargin3(-0.5,1.0),
+ EndPenMargin3=PenMargin3,
+ EndPenMargin2=PenMargin2,
+ PenMargins3=PenMargin3(0.5,0.5),
+ PenMargins2=PenMargin3(1.0,1.0),
+ BeginDotMargin3=DotMargin3(0.5,-0.5),
+ DotMargin3=DotMargin3(-0.5,0.5),
+ EndDotMargin3=DotMargin3,
+ DotMargins3=DotMargin3(0.5,0.5);
diff --git a/Master/texmf-dist/asymptote/three_surface.asy b/Master/texmf-dist/asymptote/three_surface.asy
new file mode 100644
index 00000000000..7d356e45e22
--- /dev/null
+++ b/Master/texmf-dist/asymptote/three_surface.asy
@@ -0,0 +1,1999 @@
+import bezulate;
+private import interpolate;
+
+int nslice=12;
+real camerafactor=1.2;
+
+string meshname(string name) {return name+" mesh";}
+
+private real Fuzz=10.0*realEpsilon;
+private real nineth=1/9;
+
+struct patch {
+ triple[][] P;
+ triple[] normals; // Optionally specify 4 normal vectors at the corners.
+ pen[] colors; // Optionally specify 4 corner colors.
+ bool straight; // Patch is based on a piecewise straight external path.
+ bool3 planar; // Patch is planar.
+
+ path3 external() {
+ return straight ? P[0][0]--P[3][0]--P[3][3]--P[0][3]--cycle :
+ P[0][0]..controls P[1][0] and P[2][0]..
+ P[3][0]..controls P[3][1] and P[3][2]..
+ P[3][3]..controls P[2][3] and P[1][3]..
+ P[0][3]..controls P[0][2] and P[0][1]..cycle;
+ }
+
+ triple[] internal() {
+ return new triple[] {P[1][1],P[2][1],P[2][2],P[1][2]};
+ }
+
+ triple cornermean() {
+ return 0.25*(P[0][0]+P[0][3]+P[3][0]+P[3][3]);
+ }
+
+ triple[] corners() {return new triple[] {P[0][0],P[3][0],P[3][3],P[0][3]};}
+
+ real[] map(real f(triple)) {
+ return new real[] {f(P[0][0]),f(P[3][0]),f(P[3][3]),f(P[0][3])};
+ }
+
+ triple Bu(int j, real u) {return bezier(P[0][j],P[1][j],P[2][j],P[3][j],u);}
+ triple BuP(int j, real u) {return bezierP(P[0][j],P[1][j],P[2][j],P[3][j],u);}
+ triple BuPP(int j, real u) {
+ return bezierPP(P[0][j],P[1][j],P[2][j],P[3][j],u);
+ }
+ triple BuPPP(int j) {return bezierPPP(P[0][j],P[1][j],P[2][j],P[3][j]);}
+
+ path3 uequals(real u) {
+ triple z0=Bu(0,u);
+ triple z1=Bu(3,u);
+ return path3(new triple[] {z0,Bu(2,u)},new triple[] {z0,z1},
+ new triple[] {Bu(1,u),z1},new bool[] {straight,false},false);
+ }
+
+ triple Bv(int i, real v) {return bezier(P[i][0],P[i][1],P[i][2],P[i][3],v);}
+ triple BvP(int i, real v) {return bezierP(P[i][0],P[i][1],P[i][2],P[i][3],v);}
+ triple BvPP(int i, real v) {
+ return bezierPP(P[i][0],P[i][1],P[i][2],P[i][3],v);
+ }
+ triple BvPPP(int i) {return bezierPPP(P[i][0],P[i][1],P[i][2],P[i][3]);}
+
+ path3 vequals(real v) {
+ triple z0=Bv(0,v);
+ triple z1=Bv(3,v);
+ return path3(new triple[] {z0,Bv(2,v)},new triple[] {z0,z1},
+ new triple[] {Bv(1,v),z1},new bool[] {straight,false},false);
+ }
+
+ triple point(real u, real v) {
+ return bezier(Bu(0,u),Bu(1,u),Bu(2,u),Bu(3,u),v);
+ }
+
+ // compute normal vectors for degenerate cases
+ private triple normal0(real u, real v, real epsilon) {
+ triple n=0.5*(cross(bezier(BuPP(0,u),BuPP(1,u),BuPP(2,u),BuPP(3,u),v),
+ bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u))+
+ cross(bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v),
+ bezier(BvPP(0,v),BvPP(1,v),BvPP(2,v),BvPP(3,v),u)));
+ return abs(n) > epsilon ? n :
+ 0.25*cross(bezier(BuPP(0,u),BuPP(1,u),BuPP(2,u),BuPP(3,u),v),
+ bezier(BvPP(0,v),BvPP(1,v),BvPP(2,v),BvPP(3,v),u))+
+ 1/6*(cross(bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v),
+ bezier(BvPPP(0),BvPPP(1),BvPPP(2),BvPPP(3),u))+
+ cross(bezier(BuPPP(0),BuPPP(1),BuPPP(2),BuPPP(3),v),
+ bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u)))+
+ 1/12*(cross(bezier(BuPPP(0),BuPPP(1),BuPPP(2),BuPPP(3),v),
+ bezier(BvPP(0,v),BvPP(1,v),BvPP(2,v),BvPP(3,v),u))+
+ cross(bezier(BuPP(0,u),BuPP(1,u),BuPP(2,u),BuPP(3,u),v),
+ bezier(BvPPP(0),BvPPP(1),BvPPP(2),BvPPP(3),u)))+
+ 1/36*cross(bezier(BuPPP(0),BuPPP(1),BuPPP(2),BuPPP(3),v),
+ bezier(BvPPP(0),BvPPP(1),BvPPP(2),BvPPP(3),u));
+ }
+
+ static real fuzz=1000*realEpsilon;
+
+ triple partialu(real u, real v) {
+ return bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v);
+ }
+
+ triple partialv(real u, real v) {
+ return bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u);
+ }
+
+ triple normal(real u, real v) {
+ triple n=cross(partialu(u,v),partialv(u,v));
+ real epsilon=fuzz*change2(P);
+ return (abs(n) > epsilon) ? n : normal0(u,v,epsilon);
+ }
+
+ triple normal00() {
+ triple n=9*cross(P[1][0]-P[0][0],P[0][1]-P[0][0]);
+ real epsilon=fuzz*change2(P);
+ return abs(n) > epsilon ? n : normal0(0,0,epsilon);
+ }
+
+ triple normal10() {
+ triple n=9*cross(P[3][0]-P[2][0],P[3][1]-P[3][0]);
+ real epsilon=fuzz*change2(P);
+ return abs(n) > epsilon ? n : normal0(1,0,epsilon);
+ }
+
+ triple normal11() {
+ triple n=9*cross(P[3][3]-P[2][3],P[3][3]-P[3][2]);
+ real epsilon=fuzz*change2(P);
+ return abs(n) > epsilon ? n : normal0(1,1,epsilon);
+ }
+
+ triple normal01() {
+ triple n=9*cross(P[1][3]-P[0][3],P[0][3]-P[0][2]);
+ real epsilon=fuzz*change2(P);
+ return abs(n) > epsilon ? n : normal0(0,1,epsilon);
+ }
+
+ pen[] colors(material m, light light=currentlight) {
+ bool nocolors=colors.length == 0;
+ if(normals.length > 0)
+ return new pen[] {color(normals[0],nocolors ? m : colors[0],light),
+ color(normals[1],nocolors ? m : colors[1],light),
+ color(normals[2],nocolors ? m : colors[2],light),
+ color(normals[3],nocolors ? m : colors[3],light)};
+ if(planar) {
+ triple normal=normal(0.5,0.5);
+ return new pen[] {color(normal,nocolors ? m : colors[0],light),
+ color(normal,nocolors ? m : colors[1],light),
+ color(normal,nocolors ? m : colors[2],light),
+ color(normal,nocolors ? m : colors[3],light)};
+ }
+ return new pen[] {color(normal00(),nocolors ? m : colors[0],light),
+ color(normal10(),nocolors ? m : colors[1],light),
+ color(normal11(),nocolors ? m : colors[2],light),
+ color(normal01(),nocolors ? m : colors[3],light)};
+ }
+
+ triple min3,max3;
+ bool havemin3,havemax3;
+
+ void init() {
+ havemin3=false;
+ havemax3=false;
+ }
+
+ triple min(triple bound=P[0][0]) {
+ if(havemin3) return minbound(min3,bound);
+ havemin3=true;
+ return min3=minbezier(P,bound);
+ }
+
+ triple max(triple bound=P[0][0]) {
+ if(havemax3) return maxbound(max3,bound);
+ havemax3=true;
+ return max3=maxbezier(P,bound);
+ }
+
+ triple center() {
+ return 0.5*(this.min()+this.max());
+ }
+
+ pair min(projection P, pair bound=project(this.P[0][0],P.t)) {
+ triple[][] Q=P.T.modelview*this.P;
+ if(P.infinity)
+ return xypart(minbezier(Q,(bound.x,bound.y,0)));
+ real d=P.T.projection[3][2];
+ return maxratio(Q,d*bound)/d; // d is negative
+ }
+
+ pair max(projection P, pair bound=project(this.P[0][0],P.t)) {
+ triple[][] Q=P.T.modelview*this.P;
+ if(P.infinity)
+ return xypart(maxbezier(Q,(bound.x,bound.y,0)));
+ real d=P.T.projection[3][2];
+ return minratio(Q,d*bound)/d; // d is negative
+ }
+
+ void operator init(triple[][] P, triple[] normals=new triple[],
+ pen[] colors=new pen[], bool straight=false,
+ bool3 planar=default, bool copy=true) {
+ init();
+ this.P=copy ? copy(P) : P;
+ if(normals.length != 0)
+ this.normals=copy(normals);
+ if(colors.length != 0)
+ this.colors=copy(colors);
+ this.planar=planar;
+ this.straight=straight;
+ }
+
+ void operator init(pair[][] P, triple plane(pair)=XYplane,
+ bool straight=false) {
+ triple[][] Q=new triple[4][];
+ for(int i=0; i < 4; ++i) {
+ pair[] Pi=P[i];
+ Q[i]=sequence(new triple(int j) {return plane(Pi[j]);},4);
+ }
+ operator init(Q,straight);
+ planar=true;
+ }
+
+ void operator init(patch s) {
+ operator init(s.P,s.normals,s.colors,s.straight);
+ }
+
+ // A constructor for a convex cyclic path3 of length <= 4 with optional
+ // arrays of 4 internal points, corner normals, and pens.
+ void operator init(path3 external, triple[] internal=new triple[],
+ triple[] normals=new triple[], pen[] colors=new pen[],
+ bool3 planar=default) {
+ init();
+
+ if(internal.length == 0 && planar == default)
+ this.planar=normal(external) != O;
+ else this.planar=planar;
+
+ int L=length(external);
+ if(L > 4 || !cyclic(external))
+ abort("cyclic path3 of length <= 4 expected");
+ if(L == 1) {
+ external=external--cycle--cycle--cycle;
+ if(colors.length > 0) colors.append(array(3,colors[0]));
+ if(normals.length > 0) normals.append(array(3,normals[0]));
+ } else if(L == 2) {
+ external=external--cycle--cycle;
+ if(colors.length > 0) colors.append(array(2,colors[0]));
+ if(normals.length > 0) normals.append(array(2,normals[0]));
+ } else if(L == 3) {
+ external=external--cycle;
+ if(colors.length > 0) colors.push(colors[0]);
+ if(normals.length > 0) normals.push(normals[0]);
+ }
+ if(normals.length != 0)
+ this.normals=copy(normals);
+ if(colors.length != 0)
+ this.colors=copy(colors);
+
+ if(internal.length == 0) {
+ straight=piecewisestraight(external);
+ internal=new triple[4];
+ for(int j=0; j < 4; ++j)
+ internal[j]=nineth*(-4*point(external,j)
+ +6*(precontrol(external,j)+postcontrol(external,j))
+ -2*(point(external,j-1)+point(external,j+1))
+ +3*(precontrol(external,j-1)+
+ postcontrol(external,j+1))
+ -point(external,j+2));
+ } else straight=false;
+
+ P=new triple[][] {
+ {point(external,0),precontrol(external,0),postcontrol(external,3),
+ point(external,3)},
+ {postcontrol(external,0),internal[0],internal[3],precontrol(external,3)},
+ {precontrol(external,1),internal[1],internal[2],postcontrol(external,2)},
+ {point(external,1),postcontrol(external,1),precontrol(external,2),
+ point(external,2)}
+ };
+ }
+
+ // A constructor for a convex quadrilateral.
+ void operator init(triple[] external, triple[] internal=new triple[],
+ triple[] normals=new triple[], pen[] colors=new pen[],
+ bool3 planar=default) {
+ init();
+
+ if(internal.length == 0 && planar == default)
+ this.planar=normal(external) != O;
+ else this.planar=planar;
+
+ if(normals.length != 0)
+ this.normals=copy(normals);
+ if(colors.length != 0)
+ this.colors=copy(colors);
+
+ if(internal.length == 0) {
+ internal=new triple[4];
+ for(int j=0; j < 4; ++j)
+ internal[j]=nineth*(4*external[j]+2*external[(j+1)%4]+
+ external[(j+2)%4]+2*external[(j+3)%4]);
+ }
+
+ straight=true;
+
+ triple delta[]=new triple[4];
+ for(int j=0; j < 4; ++j)
+ delta[j]=(external[(j+1)% 4]-external[j])/3;
+
+ P=new triple[][] {
+ {external[0],external[0]-delta[3],external[3]+delta[3],external[3]},
+ {external[0]+delta[0],internal[0],internal[3],external[3]-delta[2]},
+ {external[1]-delta[0],internal[1],internal[2],external[2]+delta[2]},
+ {external[1],external[1]+delta[1],external[2]-delta[1],external[2]}
+ };
+ }
+}
+
+patch operator * (transform3 t, patch s)
+{
+ patch S;
+ S.P=new triple[4][4];
+ for(int i=0; i < 4; ++i) {
+ triple[] si=s.P[i];
+ triple[] Si=S.P[i];
+ for(int j=0; j < 4; ++j)
+ Si[j]=t*si[j];
+ }
+
+ transform3 t0=shiftless(t);
+ for(int i=0; i < s.normals.length; ++i)
+ S.normals[i]=t0*s.normals[i];
+
+ S.colors=copy(s.colors);
+ S.planar=s.planar;
+ S.straight=s.straight;
+ return S;
+}
+
+patch reverse(patch s)
+{
+ patch S;
+ S.P=transpose(s.P);
+ if(s.normals.length > 0)
+ S.normals=
+ new triple[] {s.normals[0],s.normals[3],s.normals[2],s.normals[1]};
+ if(s.colors.length > 0)
+ S.colors=new pen[] {s.colors[0],s.colors[3],s.colors[2],s.colors[1]};
+ S.planar=s.planar;
+ S.straight=s.straight;
+ return S;
+}
+
+// Return the Coons patch control points corresponding to path p.
+pair[][] coons(path p)
+{
+ int L=length(p);
+ if(L == 1)
+ p=p--cycle--cycle--cycle;
+ else if(L == 2)
+ p=p--cycle--cycle;
+ else if(L == 3)
+ p=p--cycle;
+
+ pair[] internal=new pair[4];
+ for(int j=0; j < 4; ++j) {
+ internal[j]=nineth*(-4*point(p,j)
+ +6*(precontrol(p,j)+postcontrol(p,j))
+ -2*(point(p,j-1)+point(p,j+1))
+ +3*(precontrol(p,j-1)+postcontrol(p,j+1))
+ -point(p,j+2));
+ }
+
+ return new pair[][] {
+ {point(p,0),precontrol(p,0),postcontrol(p,3),point(p,3)},
+ {postcontrol(p,0),internal[0],internal[3],precontrol(p,3)},
+ {precontrol(p,1),internal[1],internal[2],postcontrol(p,2)},
+ {point(p,1),postcontrol(p,1),precontrol(p,2),point(p,2)}
+ };
+}
+
+// Decompose a possibly nonconvex cyclic path into an array of paths that
+// yield nondegenerate Coons patches.
+path[] regularize(path p, bool checkboundary=true)
+{
+ path[] s;
+
+ if(!cyclic(p))
+ abort("cyclic path expected");
+
+ int L=length(p);
+
+ if(L > 4) {
+ for(path g : bezulate(p))
+ s.append(regularize(g,checkboundary));
+ return s;
+ }
+
+ bool straight=piecewisestraight(p);
+ if(L <= 3 && straight) {
+ return new path[] {p};
+ }
+
+ // Split p along the angle bisector at t.
+ bool split(path p, real t) {
+ pair dir=dir(p,t);
+ if(dir != 0) {
+ path g=subpath(p,t,t+length(p));
+ int L=length(g);
+ pair z=point(g,0);
+ real[] T=intersections(g,z,z+I*dir);
+ for(int i=0; i < T.length; ++i) {
+ real cut=T[i];
+ if(cut > sqrtEpsilon && cut < L-sqrtEpsilon) {
+ pair w=point(g,cut);
+ if(!inside(p,0.5*(z+w),zerowinding)) continue;
+ pair delta=sqrtEpsilon*(w-z);
+ if(intersections(g,z-delta--w+delta).length != 2) continue;
+ s.append(regularize(subpath(g,0,cut)--cycle,checkboundary));
+ s.append(regularize(subpath(g,cut,L)--cycle,checkboundary));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // Ensure that all interior angles are less than 180 degrees.
+ real fuzz=1e-4;
+ int sign=sgn(windingnumber(p,inside(p,zerowinding)));
+ for(int i=0; i < L; ++i) {
+ if(sign*(conj(dir(p,i,-1))*dir(p,i,1)).y < -fuzz) {
+ if(split(p,i)) return s;
+ }
+ }
+
+ if(straight)
+ return new path[] {p};
+
+ pair[][] P=coons(p);
+
+ // Check for degeneracy.
+ pair[][] U=new pair[3][4];
+ pair[][] V=new pair[4][3];
+
+ for(int i=0; i < 3; ++i) {
+ for(int j=0; j < 4; ++j)
+ U[i][j]=P[i+1][j]-P[i][j];
+ }
+
+ for(int i=0; i < 4; ++i) {
+ for(int j=0; j < 3; ++j)
+ V[i][j]=P[i][j+1]-P[i][j];
+ }
+
+ int[] choose2={1,2,1};
+ int[] choose3={1,3,3,1};
+
+ real T[][]=new real[6][6];
+ for(int p=0; p < 6; ++p) {
+ int kstart=max(p-2,0);
+ int kstop=min(p,3);
+ real[] Tp=T[p];
+ for(int q=0; q < 6; ++q) {
+ real Tpq;
+ int jstop=min(q,3);
+ int jstart=max(q-2,0);
+ for(int k=kstart; k <= kstop; ++k) {
+ int choose3k=choose3[k];
+ for(int j=jstart; j <= jstop; ++j) {
+ int i=p-k;
+ int l=q-j;
+ Tpq += (conj(U[i][j])*V[k][l]).y*
+ choose2[i]*choose3k*choose3[j]*choose2[l];
+ }
+ }
+ Tp[q]=Tpq;
+ }
+ }
+
+ bool3 aligned=default;
+ bool degenerate=false;
+
+ for(int p=0; p < 6; ++p) {
+ for(int q=0; q < 6; ++q) {
+ if(aligned == default) {
+ if(T[p][q] > sqrtEpsilon) aligned=true;
+ if(T[p][q] < -sqrtEpsilon) aligned=false;
+ } else {
+ if((T[p][q] > sqrtEpsilon && aligned == false) ||
+ (T[p][q] < -sqrtEpsilon && aligned == true)) degenerate=true;
+ }
+ }
+ }
+
+ if(!degenerate) {
+ if(aligned == (sign >= 0))
+ return new path[] {p};
+ return s;
+ }
+
+ if(checkboundary) {
+ // Polynomial coefficients of (B_i'' B_j + B_i' B_j')/3.
+ static real[][][] fpv0={
+ {{5, -20, 30, -20, 5},
+ {-3, 24, -54, 48, -15},
+ {0, -6, 27, -36, 15},
+ {0, 0, -3, 8, -5}},
+ {{-7, 36, -66, 52, -15},
+ {3, -36, 108, -120, 45},
+ {0, 6, -45, 84, -45},
+ {0, 0, 3, -16, 15}},
+ {{2, -18, 45, -44, 15},
+ {0, 12, -63, 96, -45},
+ {0, 0, 18, -60, 45},
+ {0, 0, 0, 8, -15}},
+ {{0, 2, -9, 12, -5},
+ {0, 0, 9, -24, 15},
+ {0, 0, 0, 12, -15},
+ {0, 0, 0, 0, 5}}
+ };
+
+ // Compute one-ninth of the derivative of the Jacobian along the boundary.
+ real[][] c=array(4,array(5,0.0));
+ for(int i=0; i < 4; ++i) {
+ real[][] fpv0i=fpv0[i];
+ for(int j=0; j < 4; ++j) {
+ real[] w=fpv0i[j];
+ c[0] += w*(conj(P[i][0])*(P[j][1]-P[j][0])).y; // v=0
+ c[1] += w*(conj(P[3][j]-P[2][j])*P[3][i]).y; // u=1
+ c[2] += w*(conj(P[i][3])*(P[j][3]-P[j][2])).y; // v=1
+ c[3] += w*(conj(P[0][j]-P[1][j])*P[0][i]).y; // u=0
+ }
+ }
+
+ pair BuP(int j, real u) {
+ return bezierP(P[0][j],P[1][j],P[2][j],P[3][j],u);
+ }
+ pair BvP(int i, real v) {
+ return bezierP(P[i][0],P[i][1],P[i][2],P[i][3],v);
+ }
+ real normal(real u, real v) {
+ return (conj(bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v))*
+ bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u)).y;
+ }
+
+ // Use Rolle's theorem to check for degeneracy on the boundary.
+ real M=0;
+ real cut;
+ for(int i=0; i < 4; ++i) {
+ if(!straight(p,i)) {
+ real[] ci=c[i];
+ pair[] R=quarticroots(ci[4],ci[3],ci[2],ci[1],ci[0]);
+ for(pair r : R) {
+ if(fabs(r.y) < sqrtEpsilon) {
+ real t=r.x;
+ if(0 <= t && t <= 1) {
+ real[] U={t,1,t,0};
+ real[] V={0,t,1,t};
+ real[] T={t,t,1-t,1-t};
+ real N=sign*normal(U[i],V[i]);
+ if(N < M) {
+ M=N; cut=i+T[i];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Split at the worst boundary degeneracy.
+ if(M < 0 && split(p,cut)) return s;
+ }
+
+ // Split arbitrarily to resolve any remaining (internal) degeneracy.
+ checkboundary=false;
+ for(int i=0; i < L; ++i)
+ if(!straight(p,i) && split(p,i+0.5)) return s;
+
+ while(true)
+ for(int i=0; i < L; ++i)
+ if(!straight(p,i) && split(p,i+unitrand())) return s;
+
+ return s;
+}
+
+struct surface {
+ patch[] s;
+ int index[][];
+ bool vcyclic;
+
+ bool empty() {
+ return s.length == 0;
+ }
+
+ void operator init(int n) {
+ s=new patch[n];
+ }
+
+ void operator init(... patch[] s) {
+ this.s=s;
+ }
+
+ void operator init(surface s) {
+ this.s=new patch[s.s.length];
+ for(int i=0; i < s.s.length; ++i)
+ this.s[i]=patch(s.s[i]);
+ this.index=copy(s.index);
+ this.vcyclic=s.vcyclic;
+ }
+
+ void operator init(triple[][][] P, triple[][] normals=new triple[][],
+ pen[][] colors=new pen[][], bool3 planar=default) {
+ s=sequence(new patch(int i) {
+ return patch(P[i],normals.length == 0 ? new triple[] : normals[i],
+ colors.length == 0 ? new pen[] : colors[i],planar);
+ },P.length);
+ }
+
+ void colors(pen[][] palette) {
+ for(int i=0; i < s.length; ++i)
+ s[i].colors=copy(palette[i]);
+ }
+
+ triple[][] corners() {
+ triple[][] a=new triple[s.length][];
+ for(int i=0; i < s.length; ++i)
+ a[i]=s[i].corners();
+ return a;
+ }
+
+ real[][] map(real f(triple)) {
+ real[][] a=new real[s.length][];
+ for(int i=0; i < s.length; ++i)
+ a[i]=s[i].map(f);
+ return a;
+ }
+
+ triple[] cornermean() {
+ return sequence(new triple(int i) {return s[i].cornermean();},s.length);
+ }
+
+ triple point(real u, real v) {
+ int U=floor(u);
+ int V=floor(v);
+ int index=index.length == 0 ? U+V : index[U][V];
+ return s[index].point(u-U,v-V);
+ }
+
+ triple normal(real u, real v) {
+ int U=floor(u);
+ int V=floor(v);
+ int index=index.length == 0 ? U+V : index[U][V];
+ return s[index].normal(u-U,v-V);
+ }
+
+ void ucyclic(bool f)
+ {
+ index.cyclic=f;
+ }
+
+ void vcyclic(bool f)
+ {
+ for(int[] i : index)
+ i.cyclic=f;
+ vcyclic=f;
+ }
+
+ bool ucyclic()
+ {
+ return index.cyclic;
+ }
+
+ bool vcyclic()
+ {
+ return vcyclic;
+ }
+
+ path3 uequals(real u) {
+ if(index.length == 0) return nullpath3;
+ int U=floor(u);
+ int[] index=index[U];
+ path3 g;
+ for(int i : index)
+ g=g&s[i].uequals(u-U);
+ return vcyclic() ? g&cycle : g;
+ }
+
+ path3 vequals(real v) {
+ if(index.length == 0) return nullpath3;
+ int V=floor(v);
+ path3 g;
+ for(int[] i : index)
+ g=g&s[i[V]].vequals(v-V);
+ return ucyclic() ? g&cycle : g;
+ }
+
+ // A constructor for a possibly nonconvex cyclic path in a given plane.
+ void operator init(path p, triple plane(pair)=XYplane) {
+ bool straight=piecewisestraight(p);
+ for(path g : regularize(p))
+ s.push(patch(coons(g),plane,straight));
+ }
+
+ void operator init(explicit path[] g, triple plane(pair)=XYplane) {
+ for(path p : bezulate(g))
+ s.append(surface(p,plane).s);
+ }
+
+ // A general surface constructor for both planar and nonplanar 3D paths.
+ void construct(path3 external, triple[] internal=new triple[],
+ triple[] normals=new triple[], pen[] colors=new pen[],
+ bool3 planar=default) {
+ int L=length(external);
+ if(!cyclic(external)) abort("cyclic path expected");
+
+ if(L <= 3 && piecewisestraight(external)) {
+ s.push(patch(external,internal,normals,colors,planar=true));
+ return;
+ }
+
+ // Construct a surface from a possibly nonconvex planar cyclic path3.
+ if(planar != false && internal.length == 0 && normals.length == 0 &&
+ colors.length == 0) {
+ triple n=normal(external);
+ if(n != O) {
+ transform3 T=align(n);
+ external=transpose(T)*external;
+ T *= shift(0,0,point(external,0).z);
+ for(patch p : surface(path(external)).s)
+ s.push(T*p);
+ return;
+ }
+ }
+
+ if(L <= 4 || internal.length > 0) {
+ s.push(patch(external,internal,normals,colors,planar));
+ return;
+ }
+
+ // Path is not planar; split into patches.
+ real factor=1/L;
+ pen[] p;
+ triple[] n;
+ bool nocolors=colors.length == 0;
+ bool nonormals=normals.length == 0;
+ triple center;
+ for(int i=0; i < L; ++i)
+ center += point(external,i);
+ center *= factor;
+ if(!nocolors)
+ p=new pen[] {mean(colors)};
+ if(!nonormals)
+ n=new triple[] {factor*sum(normals)};
+ // Use triangles for nonplanar surfaces.
+ int step=normal(external) == O ? 1 : 2;
+ int i=0;
+ int end;
+ while((end=i+step) < L) {
+ s.push(patch(subpath(external,i,end)--center--cycle,
+ nonormals ? n : concat(normals[i:end+1],n),
+ nocolors ? p : concat(colors[i:end+1],p),planar));
+ i=end;
+ }
+ s.push(patch(subpath(external,i,L)--center--cycle,
+ nonormals ? n : concat(normals[i:],normals[0:1],n),
+ nocolors ? p : concat(colors[i:],colors[0:1],p),planar));
+ }
+
+ void operator init(path3 external, triple[] internal=new triple[],
+ triple[] normals=new triple[], pen[] colors=new pen[],
+ bool3 planar=default) {
+ s=new patch[];
+ construct(external,internal,normals,colors,planar);
+ }
+
+ void operator init(explicit path3[] external,
+ triple[][] internal=new triple[][],
+ triple[][] normals=new triple[][],
+ pen[][] colors=new pen[][], bool3 planar=default) {
+ s=new patch[];
+ if(planar == true) {// Assume all path3 elements share a common normal.
+ if(external.length != 0) {
+ triple n=normal(external[0]);
+ if(n != O) {
+ transform3 T=align(n);
+ external=transpose(T)*external;
+ T *= shift(0,0,point(external[0],0).z);
+ path[] g=sequence(new path(int i) {return path(external[i]);},
+ external.length);
+ for(patch p : surface(g).s)
+ s.push(T*p);
+ return;
+ }
+ }
+ }
+
+ for(int i=0; i < external.length; ++i)
+ construct(external[i],
+ internal.length == 0 ? new triple[] : internal[i],
+ normals.length == 0 ? new triple[] : normals[i],
+ colors.length == 0 ? new pen[] : colors[i],planar);
+ }
+
+ void push(path3 external, triple[] internal=new triple[],
+ triple[] normals=new triple[] ,pen[] colors=new pen[],
+ bool3 planar=default) {
+ s.push(patch(external,internal,normals,colors,planar));
+ }
+
+ // Construct the surface of rotation generated by rotating g
+ // from angle1 to angle2 sampled n times about the line c--c+axis.
+ // An optional surface pen color(int i, real j) may be specified
+ // to override the color at vertex(i,j).
+ void operator init(triple c, path3 g, triple axis, int n=nslice,
+ real angle1=0, real angle2=360,
+ pen color(int i, real j)=null) {
+ axis=unit(axis);
+ real w=(angle2-angle1)/n;
+ int L=length(g);
+ s=new patch[L*n];
+ index=new int[n][L];
+ int m=-1;
+ transform3[] T=new transform3[n+1];
+ transform3 t=rotate(w,c,c+axis);
+ T[0]=rotate(angle1,c,c+axis);
+ for(int k=1; k <= n; ++k)
+ T[k]=T[k-1]*t;
+
+ typedef pen colorfcn(int i, real j);
+ bool defaultcolors=(colorfcn) color == null;
+
+ for(int i=0; i < L; ++i) {
+ path3 h=subpath(g,i,i+1);
+ path3 r=reverse(h);
+ path3 H=shift(-c)*h;
+ real M=0;
+ triple perp;
+ void test(real[] t) {
+ for(int i=0; i < 3; ++i) {
+ triple v=point(H,t[i]);
+ triple V=v-dot(v,axis)*axis;
+ real a=abs(V);
+ if(a > M) {M=a; perp=V;}
+ }
+ }
+ test(maxtimes(H));
+ test(mintimes(H));
+
+ perp=unit(perp);
+ triple normal=unit(cross(axis,perp));
+ triple dir(real j) {return Cos(j)*normal-Sin(j)*perp;}
+ real j=angle1;
+ transform3 Tk=T[0];
+ triple dirj=dir(j);
+ for(int k=0; k < n; ++k, j += w) {
+ transform3 Tp=T[k+1];
+ triple dirp=dir(j+w);
+ path3 G=reverse(Tk*h{dirj}..{dirp}Tp*r{-dirp}..{-dirj}cycle);
+ Tk=Tp;
+ dirj=dirp;
+ s[++m]=defaultcolors ? patch(G) :
+ patch(G,new pen[] {color(i,j),color(i,j+w),color(i+1,j+w),
+ color(i+1,j)});
+ index[k][i]=m;
+ }
+ ucyclic((angle2-angle1) % 360 == 0);
+ vcyclic(cyclic(g));
+ }
+ }
+
+ void push(patch s) {
+ this.s.push(s);
+ }
+
+ void append(surface s) {
+ this.s.append(s.s);
+ }
+
+ void operator init(... surface[] s) {
+ for(surface S : s)
+ this.s.append(S.s);
+ }
+}
+
+surface operator * (transform3 t, surface s)
+{
+ surface S;
+ S.s=new patch[s.s.length];
+ for(int i=0; i < s.s.length; ++i)
+ S.s[i]=t*s.s[i];
+ S.index=copy(s.index);
+ S.vcyclic=(bool) s.vcyclic;
+
+ return S;
+}
+
+private string nullsurface="null surface";
+
+triple min(surface s)
+{
+ if(s.s.length == 0)
+ abort(nullsurface);
+ triple bound=s.s[0].min();
+ for(int i=1; i < s.s.length; ++i)
+ bound=s.s[i].min(bound);
+ return bound;
+}
+
+triple max(surface s)
+{
+ if(s.s.length == 0)
+ abort(nullsurface);
+ triple bound=s.s[0].max();
+ for(int i=1; i < s.s.length; ++i)
+ bound=s.s[i].max(bound);
+ return bound;
+}
+
+pair min(surface s, projection P)
+{
+ if(s.s.length == 0)
+ abort(nullsurface);
+ pair bound=s.s[0].min(P);
+ for(int i=1; i < s.s.length; ++i)
+ bound=s.s[i].min(P,bound);
+ return bound;
+}
+
+pair max(surface s, projection P)
+{
+ if(s.s.length == 0)
+ abort(nullsurface);
+ pair bound=s.s[0].max(P);
+ for(int i=1; i < s.s.length; ++i)
+ bound=s.s[i].max(P,bound);
+ return bound;
+}
+
+private triple[] split(triple z0, triple c0, triple c1, triple z1, real t=0.5)
+{
+ triple m0=interp(z0,c0,t);
+ triple m1=interp(c0,c1,t);
+ triple m2=interp(c1,z1,t);
+ triple m3=interp(m0,m1,t);
+ triple m4=interp(m1,m2,t);
+ triple m5=interp(m3,m4,t);
+
+ return new triple[] {m0,m3,m5,m4,m2};
+}
+
+// Return the control points of the subpatches
+// 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[][][] {
+ {{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]}}
+ };
+}
+
+// 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]}}
+ };
+}
+
+// Return a 2D array of the control point arrays of the subpatches
+// produced by horizontal and vertical splits of P at u and v
+triple[][][][] split(triple[][] P, real u=1/2, real v=1/2)
+{
+ triple[] P0=P[0];
+ triple[] P1=P[1];
+ triple[] P2=P[2];
+ triple[] P3=P[3];
+
+ // slice horizontally
+ triple[] c0=split(P0[0],P0[1],P0[2],P0[3],v);
+ triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v);
+ triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v);
+ triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v);
+
+ // bottom patch
+ triple[] c4=split(P0[0],P1[0],P2[0],P3[0],u);
+ triple[] c5=split(c0[0],c1[0],c2[0],c3[0],u);
+ triple[] c6=split(c0[1],c1[1],c2[1],c3[1],u);
+ triple[] c7=split(c0[2],c1[2],c2[2],c3[2],u);
+
+ // top patch
+ triple[] c8=split(c0[3],c1[3],c2[3],c3[3],u);
+ 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}}
+ return new triple[][][][] {
+ {{{P0[0],c0[0],c0[1],c0[2]},
+ {c4[0],c5[0],c6[0],c7[0]},
+ {c4[1],c5[1],c6[1],c7[1]},
+ {c4[2],c5[2],c6[2],c7[2]}},
+ {{c0[2],c0[3],c0[4],P0[3]},
+ {c7[0],c8[0],c9[0],cA[0]},
+ {c7[1],c8[1],c9[1],cA[1]},
+ {c7[2],c8[2],c9[2],cA[2]}}},
+ {{{c4[2],c5[2],c6[2],c7[2]},
+ {c4[3],c5[3],c6[3],c7[3]},
+ {c4[4],c5[4],c6[4],c7[4]},
+ {P3[0],c3[0],c3[1],c3[2]}},
+ {{c7[2],c8[2],c9[2],cA[2]},
+ {c7[3],c8[3],c9[3],cA[3]},
+ {c7[4],c8[4],c9[4],cA[4]},
+ {c3[2],c3[3],c3[4],P3[3]}}}
+ };
+}
+
+// Return the control points for a subpatch of P on [u,1] x [v,1].
+triple[][] subpatchend(triple[][] P, real u, real v)
+{
+ 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],v);
+ triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v);
+ triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v);
+ triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v);
+
+ triple[] c7=split(c0[2],c1[2],c2[2],c3[2],u);
+ triple[] c8=split(c0[3],c1[3],c2[3],c3[3],u);
+ triple[] c9=split(c0[4],c1[4],c2[4],c3[4],u);
+ triple[] cA=split(P0[3],P1[3],P2[3],P3[3],u);
+
+ return new triple[][] {
+ {c7[2],c8[2],c9[2],cA[2]},
+ {c7[3],c8[3],c9[3],cA[3]},
+ {c7[4],c8[4],c9[4],cA[4]},
+ {c3[2],c3[3],c3[4],P3[3]}};
+}
+
+// Return the control points for a subpatch of P on [0,u] x [0,v].
+triple[][] subpatchbegin(triple[][] P, real u, real v)
+{
+ 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],v);
+ triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v);
+ triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v);
+ triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v);
+
+ triple[] c4=split(P0[0],P1[0],P2[0],P3[0],u);
+ triple[] c5=split(c0[0],c1[0],c2[0],c3[0],u);
+ triple[] c6=split(c0[1],c1[1],c2[1],c3[1],u);
+ triple[] c7=split(c0[2],c1[2],c2[2],c3[2],u);
+
+ return new triple[][] {
+ {P0[0],c0[0],c0[1],c0[2]},
+ {c4[0],c5[0],c6[0],c7[0]},
+ {c4[1],c5[1],c6[1],c7[1]},
+ {c4[2],c5[2],c6[2],c7[2]}};
+}
+
+triple[][] subpatch(triple[][] P, pair a, pair b)
+{
+ return subpatchend(subpatchbegin(P,b.x,b.y),a.x/b.x,a.y/b.y);
+}
+
+patch subpatch(patch s, pair a, pair b)
+{
+ assert(a.x >= 0 && a.y >= 0 && b.x <= 1 && b.y <= 1 &&
+ a.x < b.x && a.y < b.y);
+ return patch(subpatch(s.P,a,b),s.straight,s.planar);
+}
+
+
+// return an array containing all intersection times of path p and patch s.
+real[][] intersections(path3 p, patch s, real fuzz=-1)
+{
+ return sort(intersections(p,s.P,fuzz));
+}
+
+// return an array containing all intersection times of path p and surface s.
+real[][] intersections(path3 p, surface s, real fuzz=-1)
+{
+ real[][] T;
+ if(length(p) < 0) return T;
+ for(int i=0; i < s.s.length; ++i)
+ for(real[] s: intersections(p,s.s[i].P,fuzz))
+ T.push(s);
+
+ static real Fuzz=1000*realEpsilon;
+ real fuzz=max(10*fuzz,Fuzz*max(abs(min(s)),abs(max(s))));
+
+ // Remove intrapatch duplicate points.
+ for(int i=0; i < T.length; ++i) {
+ triple v=point(p,T[i][0]);
+ for(int j=i+1; j < T.length;) {
+ if(abs(v-point(p,T[j][0])) < fuzz)
+ T.delete(j);
+ else ++j;
+ }
+ }
+ return sort(T);
+}
+
+// return an array containing all intersection points of path p and surface s.
+triple[] intersectionpoints(path3 p, patch s, real fuzz=-1)
+{
+ real[][] t=intersections(p,s,fuzz);
+ return sequence(new triple(int i) {return point(p,t[i][0]);},t.length);
+}
+
+// return an array containing all intersection points of path p and surface s.
+triple[] intersectionpoints(path3 p, surface s, real fuzz=-1)
+{
+ real[][] t=intersections(p,s,fuzz);
+ return sequence(new triple(int i) {return point(p,t[i][0]);},t.length);
+}
+
+// Return true iff the bounding boxes of patch p and q overlap.
+bool overlap(triple[][] p, triple[][] q, real fuzz=-1)
+{
+ triple p0=p[0][0];
+ triple q0=q[0][0];
+ triple pmin=minbezier(p,p0);
+ triple pmax=maxbezier(p,p0);
+ triple qmin=minbezier(q,q0);
+ triple qmax=maxbezier(q,q0);
+
+ static real Fuzz=1000*realEpsilon;
+ real fuzz=max(10*fuzz,Fuzz*max(abs(pmin),abs(pmax)));
+
+ return
+ pmax.x+fuzz >= qmin.x &&
+ pmax.y+fuzz >= qmin.y &&
+ pmax.z+fuzz >= qmin.z &&
+ qmax.x+fuzz >= pmin.x &&
+ qmax.y+fuzz >= pmin.y &&
+ qmax.z+fuzz >= pmin.z; // Overlapping bounding boxes?
+}
+
+triple point(patch s, real u, real v)
+{
+ return s.point(u,v);
+}
+
+real PRCshininess(real shininess)
+{
+ // Empirical translation table from Phong-Blinn to PRC shininess model:
+ static real[] x={0.015,0.025,0.05,0.07,0.1,0.14,0.23,0.5,0.65,0.75,0.85,
+ 0.875,0.9,1};
+ static real[] y={0.05,0.1,0.15,0.2,0.25,0.3,0.4,0.5,0.55,0.6,0.7,0.8,0.9,1};
+ static realfunction s=fspline(x,y,monotonic);
+ return s(shininess);
+}
+
+struct interaction
+{
+ int type;
+ bool targetsize;
+ void operator init(int type, bool targetsize=false) {
+ this.type=type;
+ this.targetsize=targetsize;
+ }
+}
+
+restricted interaction Embedded=interaction(0);
+restricted interaction Billboard=interaction(1);
+
+interaction LabelInteraction()
+{
+ return settings.autobillboard ? Billboard : Embedded;
+}
+
+private material material(material m, light light)
+{
+ return light.on() || invisible((pen) m) ? m : emissive(m);
+}
+
+void draw3D(frame f, int type=0, patch s, triple center=O, material m,
+ light light=currentlight, interaction interaction=Embedded,
+ bool prc=true)
+{
+ if(s.colors.length > 0)
+ m=mean(s.colors);
+ m=material(m,light);
+ real PRCshininess;
+ if(prc())
+ 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);
+}
+
+void drawPRCsphere(frame f, transform3 t=identity4, bool half=false, material m,
+ light light=currentlight, render render=defaultrender)
+{
+ m=material(m,light);
+ drawPRCsphere(f,t,half,m.p,m.opacity,PRCshininess(m.shininess),render.sphere);
+}
+
+void drawPRCcylinder(frame f, transform3 t=identity4, material m,
+ light light=currentlight)
+{
+ m=material(m,light);
+ drawPRCcylinder(f,t,m.p,m.opacity,PRCshininess(m.shininess));
+}
+
+void drawPRCdisk(frame f, transform3 t=identity4, material m,
+ light light=currentlight)
+{
+ m=material(m,light);
+ drawPRCdisk(f,t,m.p,m.opacity,PRCshininess(m.shininess));
+}
+
+void drawPRCtube(frame f, path3 center, path3 g, material m,
+ light light=currentlight)
+{
+ m=material(m,light);
+ drawPRCtube(f,center,g,m.p,m.opacity,PRCshininess(m.shininess));
+}
+
+void tensorshade(transform t=identity(), frame f, patch s,
+ material m, light light=currentlight, projection P)
+{
+ tensorshade(f,box(t*s.min(P),t*s.max(P)),m.diffuse(),
+ s.colors(m,light),t*project(s.external(),P,1),
+ t*project(s.internal(),P));
+}
+
+restricted pen[] nullpens={nullpen};
+nullpens.cyclic=true;
+
+void draw(transform t=identity(), frame f, surface s, int nu=1, int nv=1,
+ material[] surfacepen, pen[] meshpen=nullpens,
+ light light=currentlight, light meshlight=light, string name="",
+ render render=defaultrender, projection P=currentprojection)
+{
+ bool is3D=is3D();
+ if(is3D) {
+ 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);
+ 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);
+ 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),
+ 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),
+ render);
+ endgroup3(f);
+ }
+ }
+ }
+ if(!is3D || settings.render == 0) {
+ begingroup(f);
+ // Sort patches by mean distance from camera
+ triple camera=P.camera;
+ if(P.infinity) {
+ triple m=min(s);
+ triple M=max(s);
+ camera=P.target+camerafactor*(abs(M-m)+abs(m-P.target))*unit(P.vector());
+ }
+
+ real[][] depth=new real[s.s.length][];
+ for(int i=0; i < depth.length; ++i)
+ depth[i]=new real[] {abs(camera-s.s[i].cornermean()),i};
+
+ depth=sort(depth);
+
+ light.T=shiftless(P.T.modelview);
+
+ // Draw from farthest to nearest
+ while(depth.length > 0) {
+ real[] a=depth.pop();
+ int i=round(a[1]);
+ tensorshade(t,f,s.s[i],surfacepen[i],light,P);
+ pen meshpen=meshpen[i];
+ if(!invisible(meshpen))
+ draw(f,t*project(s.s[i].external(),P),meshpen);
+ }
+ endgroup(f);
+ }
+}
+
+void draw(transform t=identity(), frame f, surface s, int nu=1, int nv=1,
+ material surfacepen=currentpen, pen meshpen=nullpen,
+ light light=currentlight, light meshlight=light, string name="",
+ render render=defaultrender, projection P=currentprojection)
+{
+ material[] surfacepen={surfacepen};
+ pen[] meshpen={meshpen};
+ surfacepen.cyclic=true;
+ meshpen.cyclic=true;
+ draw(t,f,s,nu,nv,surfacepen,meshpen,light,meshlight,name,render,P);
+}
+
+void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1,
+ material[] surfacepen, pen[] meshpen=nullpens,
+ light light=currentlight, light meshlight=light, string name="",
+ render render=defaultrender)
+{
+ if(s.empty()) return;
+
+ bool cyclic=surfacepen.cyclic;
+ surfacepen=copy(surfacepen);
+ surfacepen.cyclic=cyclic;
+ cyclic=meshpen.cyclic;
+ meshpen=copy(meshpen);
+ meshpen.cyclic=cyclic;
+
+ pic.add(new void(frame f, transform3 t, picture pic, projection P) {
+ surface S=t*s;
+ if(is3D())
+ draw(f,S,nu,nv,surfacepen,meshpen,light,meshlight,name,render);
+ if(pic != null) {
+ pic.add(new void(frame f, transform T) {
+ draw(T,f,S,nu,nv,surfacepen,meshpen,light,meshlight,P);
+ },true);
+ pic.addPoint(min(S,P));
+ pic.addPoint(max(S,P));
+ }
+ },true);
+ pic.addPoint(min(s));
+ pic.addPoint(max(s));
+
+ pen modifiers;
+ if(is3D()) modifiers=thin()+squarecap;
+ for(int k=0; k < s.s.length; ++k) {
+ pen meshpen=meshpen[k];
+ if(!invisible(meshpen)) {
+ meshpen=modifiers+meshpen;
+ real step=nu == 0 ? 0 : 1/nu;
+ for(int i=0; i <= nu; ++i)
+ addPath(pic,s.s[k].uequals(i*step),meshpen);
+ step=nv == 0 ? 0 : 1/nv;
+ for(int j=0; j <= nv; ++j)
+ addPath(pic,s.s[k].vequals(j*step),meshpen);
+ }
+ }
+}
+
+void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1,
+ material surfacepen=currentpen, pen meshpen=nullpen,
+ light light=currentlight, light meshlight=light, string name="",
+ render render=defaultrender)
+{
+ material[] surfacepen={surfacepen};
+ pen[] meshpen={meshpen};
+ surfacepen.cyclic=true;
+ meshpen.cyclic=true;
+ draw(pic,s,nu,nv,surfacepen,meshpen,light,meshlight,name,render);
+}
+
+void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1,
+ material[] surfacepen, pen meshpen,
+ light light=currentlight, light meshlight=light, string name="",
+ render render=defaultrender)
+{
+ pen[] meshpen={meshpen};
+ meshpen.cyclic=true;
+ draw(pic,s,nu,nv,surfacepen,meshpen,light,meshlight,name,render);
+}
+
+surface extrude(path3 p, path3 q)
+{
+ static patch[] allocate;
+ return surface(...sequence(new patch(int i) {
+ return patch(subpath(p,i,i+1)--subpath(q,i+1,i)--cycle);
+ },length(p)));
+}
+
+surface extrude(path3 p, triple axis=Z)
+{
+ return extrude(p,shift(axis)*p);
+}
+
+surface extrude(path p, triple plane(pair)=XYplane, triple axis=Z)
+{
+ return extrude(path3(p,plane),axis);
+}
+
+surface extrude(explicit path[] p, triple axis=Z)
+{
+ surface s;
+ for(path g:p)
+ s.append(extrude(g,axis));
+ return s;
+}
+
+triple rectify(triple dir)
+{
+ real scale=max(abs(dir.x),abs(dir.y),abs(dir.z));
+ if(scale != 0) dir *= 0.5/scale;
+ dir += (0.5,0.5,0.5);
+ return dir;
+}
+
+path3[] align(path3[] g, transform3 t=identity4, triple position,
+ triple align, pen p=currentpen)
+{
+ if(determinant(t) == 0 || g.length == 0) return g;
+ 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)*g;
+}
+
+surface align(surface s, transform3 t=identity4, triple position,
+ triple align, pen p=currentpen)
+{
+ if(determinant(t) == 0 || s.s.length == 0) return s;
+ 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)*s;
+}
+
+surface surface(Label L, triple position=O, bool bbox=false)
+{
+ surface s=surface(texpath(L,bbox=bbox));
+ return L.align.is3D ? align(s,L.T3,position,L.align.dir3,L.p) :
+ shift(position)*L.T3*s;
+}
+
+private path[] path(Label L, pair z=0, projection P)
+{
+ path[] g=texpath(L,bbox=P.bboxonly);
+ return L.align.is3D ? align(g,z,project(L.align.dir3,P)-project(O,P),L.p) :
+ shift(z)*g;
+}
+
+void label(frame f, Label L, triple position, align align=NoAlign,
+ pen p=currentpen, light light=nolight,
+ string name="", render render=defaultrender,
+ interaction interaction=LabelInteraction(),
+ projection P=currentprojection)
+{
+ Label L=L.copy();
+ L.align(align);
+ L.p(p);
+ if(interaction.targetsize && settings.render != 0)
+ L.T=L.T*scale(abs(P.camera-position)/abs(P.vector()));
+ if(L.defaulttransform3)
+ L.T3=transform3(P);
+ begingroup3(f,name == "" ? L.s : name,render);
+ 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);
+ }
+ } else {
+ pen p=color(L.T3*Z,L.p,light,shiftless(P.T.modelview));
+ if(L.defaulttransform3) {
+ if(L.filltype == NoFill)
+ fill(f,path(L,project(position,P.t),P),p);
+ else {
+ frame d;
+ fill(d,path(L,project(position,P.t),P),p);
+ add(f,d,L.filltype);
+ }
+ } else
+ 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,
+ align align=NoAlign, pen p=currentpen,
+ light light=nolight, string name="",
+ render render=defaultrender,
+ interaction interaction=LabelInteraction())
+{
+ Label L=L.copy();
+ L.align(align);
+ L.p(p);
+ L.position(0);
+
+ pic.add(new void(frame f, transform3 t, picture pic2, projection P) {
+ // Handle relative projected 3D alignments.
+ Label L=L.copy();
+ triple v=t*position;
+ if(!align.is3D && L.align.relative && L.align.dir3 != O &&
+ determinant(P.t) != 0)
+ L.align(L.align.dir*unit(project(v+L.align.dir3,P.t)-project(v,P.t)));
+
+ if(interaction.targetsize && settings.render != 0)
+ L.T=L.T*scale(abs(P.camera-v)/abs(P.vector()));
+ if(L.defaulttransform3)
+ L.T3=transform3(P);
+
+ 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);
+ }
+ }
+
+ if(pic2 != null) {
+ pen p=color(L.T3*Z,L.p,light,shiftless(P.T.modelview));
+ if(L.defaulttransform3) {
+ if(L.filltype == NoFill)
+ fill(project(v,P.t),pic2,path(L,P),p);
+ else {
+ picture d;
+ fill(project(v,P.t),d,path(L,P),p);
+ add(pic2,d,L.filltype);
+ }
+ } else
+ pic2.add(new void(frame f, transform T) {
+ for(patch S : surface(L,v).s)
+ fill(f,T*project(S.external(),P,1),p);
+ });
+ }
+ endgroup3(f);
+
+ },!L.defaulttransform3);
+
+ Label L=L.copy();
+
+ if(interaction.targetsize && settings.render != 0)
+ L.T=L.T*scale(abs(currentprojection.camera-position)/
+ abs(currentprojection.vector()));
+ path[] g=texpath(L,bbox=true);
+ if(g.length == 0 || (g.length == 1 && size(g[0]) == 0)) return;
+ if(L.defaulttransform3)
+ L.T3=transform3(currentprojection);
+ path3[] G=path3(g);
+ G=L.align.is3D ? align(G,L.T3,O,L.align.dir3,L.p) : L.T3*G;
+ pic.addBox(position,position,min(G),max(G));
+}
+
+void label(picture pic=currentpicture, Label L, path3 g, align align=NoAlign,
+ pen p=currentpen, light light=nolight, string name="",
+ interaction interaction=LabelInteraction())
+{
+ Label L=L.copy();
+ L.align(align);
+ L.p(p);
+ bool relative=L.position.relative;
+ real position=L.position.position.x;
+ if(L.defaultposition) {relative=true; position=0.5;}
+ if(relative) position=reltime(g,position);
+ if(L.align.default) {
+ align a;
+ a.init(-I*(position <= sqrtEpsilon ? S :
+ position >= length(g)-sqrtEpsilon ? N : E),relative=true);
+ a.dir3=dir(g,position); // Pass 3D direction via unused field.
+ L.align(a);
+ }
+ label(pic,L,point(g,position),light,name,interaction);
+}
+
+surface extrude(Label L, triple axis=Z)
+{
+ Label L=L.copy();
+ path[] g=texpath(L);
+ surface S=extrude(g,axis);
+ surface s=surface(g);
+ S.append(s);
+ S.append(shift(axis)*s);
+ return S;
+}
+
+restricted surface nullsurface;
+
+// Embed a Label onto a surface.
+surface surface(Label L, surface s, real uoffset, real voffset,
+ real height=0, bool bottom=true, bool top=true)
+{
+ int nu=s.index.length;
+ int nv;
+ if(nu == 0) nu=nv=1;
+ else {
+ nv=s.index[0].length;
+ if(nv == 0) nv=1;
+ }
+
+ path[] g=texpath(L);
+ pair m=min(g);
+ pair M=max(g);
+ pair lambda=inverse(L.T*scale(nu-epsilon,nv-epsilon))*(M-m);
+ lambda=(abs(lambda.x),abs(lambda.y));
+ path[] G=bezulate(g);
+
+ path3 transpath(path p, real height) {
+ return path3(unstraighten(p),new triple(pair z) {
+ real u=uoffset+(z.x-m.x)/lambda.x;
+ real v=voffset+(z.y-m.y)/lambda.y;
+ if(((u < 0 || u >= nu) && !s.ucyclic()) ||
+ ((v < 0 || v >= nv) && !s.vcyclic())) {
+ warning("cannotfit","cannot fit string to surface");
+ u=v=0;
+ }
+ return s.point(u,v)+height*unit(s.normal(u,v));
+ });
+ }
+
+ surface s;
+ for(path p : G) {
+ for(path g : regularize(p)) {
+ path3 b;
+ bool extrude=height > 0;
+ if(bottom || extrude)
+ b=transpath(g,0);
+ if(bottom) s.s.push(patch(b));
+ if(top || extrude) {
+ path3 h=transpath(g,height);
+ if(top) s.s.push(patch(h));
+ if(extrude) s.append(extrude(b,h));
+ }
+ }
+ }
+ return s;
+}
+
+private real a=4/3*(sqrt(2)-1);
+private transform3 t1=rotate(90,O,Z);
+private transform3 t2=t1*t1;
+private transform3 t3=t2*t1;
+private transform3 i=xscale3(-1)*zscale3(-1);
+
+restricted patch octant1=patch(X{Y}..{-X}Y{Z}..{-Y}Z..Z{X}..{-Z}cycle,
+ new triple[] {(1,a,a),(a,1,a),(a^2,a,1),
+ (a,a^2,1)});
+
+restricted surface unithemisphere=surface(octant1,t1*octant1,t2*octant1,
+ t3*octant1);
+restricted surface unitsphere=surface(octant1,t1*octant1,t2*octant1,t3*octant1,
+ i*octant1,i*t1*octant1,i*t2*octant1,
+ i*t3*octant1);
+
+restricted patch unitfrustum(real t1, real t2)
+{
+ real s1=interp(t1,t2,1/3);
+ real s2=interp(t1,t2,2/3);
+ return patch(interp(Z,X,t2){Y}..{-X}interp(Z,Y,t2)--interp(Z,Y,t1){X}..{-Y}
+ interp(Z,X,t1)--cycle,
+ new triple[] {(s2,s2*a,1-s2),(s2*a,s2,1-s2),(s1*a,s1,1-s1),
+ (s1,s1*a,1-s1)});
+}
+
+// Return a unitcone constructed from n frusta (the final one being degenerate)
+surface unitcone(int n=6)
+{
+ surface unitcone;
+ unitcone.s=new patch[4*n];
+ real r=1/3;
+ for(int i=0; i < n; ++i) {
+ patch s=unitfrustum(i < n-1 ? r^(i+1) : 0,r^i);
+ unitcone.s[i]=s;
+ unitcone.s[n+i]=t1*s;
+ unitcone.s[2n+i]=t2*s;
+ unitcone.s[3n+i]=t3*s;
+ }
+ return unitcone;
+}
+
+restricted surface unitcone=unitcone();
+restricted surface unitsolidcone=surface(patch(unitcircle3)...unitcone.s);
+
+private patch unitcylinder1=patch(X{Y}..{-X}Y--Y+Z{X}..{-Y}X+Z--cycle);
+
+restricted surface unitcylinder=surface(unitcylinder1,t1*unitcylinder1,
+ t2*unitcylinder1,t3*unitcylinder1);
+
+private patch unitplane=patch(new triple[] {O,X,X+Y,Y});
+restricted surface unitcube=surface(reverse(unitplane),
+ rotate(90,O,X)*unitplane,
+ rotate(-90,O,Y)*unitplane,
+ shift(Z)*unitplane,
+ rotate(90,X,X+Y)*unitplane,
+ rotate(-90,Y,X+Y)*unitplane);
+restricted surface unitplane=surface(unitplane);
+restricted surface unitdisk=surface(unitcircle3);
+
+void dot(frame f, triple v, material p=currentpen,
+ light light=nolight, string name="",
+ render render=defaultrender, projection P=currentprojection)
+{
+ pen q=(pen) p;
+ if(is3D()) {
+ 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);
+ } else dot(f,project(v,P.t),q);
+}
+
+void dot(frame f, triple[] v, material p=currentpen, light light=nolight,
+ string name="", render render=defaultrender,
+ projection P=currentprojection)
+{
+ if(v.length > 0) {
+ // Remove duplicate points.
+ v=sort(v,lexorder);
+
+ triple last=v[0];
+ dot(f,last,p,light,name,P);
+ for(int i=1; i < v.length; ++i) {
+ triple V=v[i];
+ if(V != last) {
+ dot(f,V,p,light,name,render,P);
+ last=V;
+ }
+ }
+ }
+}
+
+void dot(frame f, path3 g, material p=currentpen, light light=nolight,
+ string name="", render render=defaultrender,
+ projection P=currentprojection)
+{
+ dot(f,sequence(new triple(int i) {return point(g,i);},size(g)),
+ p,light,name,render,P);
+}
+
+void dot(frame f, path3[] g, material p=currentpen, light light=nolight,
+ string name="", render render=defaultrender,
+ projection P=currentprojection)
+{
+ int sum;
+ for(path3 G : g)
+ sum += size(G);
+ int i,j;
+ dot(f,sequence(new triple(int) {
+ while(j >= size(g[i])) {
+ ++i;
+ j=0;
+ }
+ triple v=point(g[i],j);
+ ++j;
+ return v;
+ },sum),p,light,name,render,P);
+}
+
+void dot(picture pic=currentpicture, triple v, material p=currentpen,
+ light light=nolight, string name="", render render=defaultrender)
+{
+ pen q=(pen) p;
+ real size=0.5*linewidth(dotsize(q)+q);
+ pic.add(new void(frame f, transform3 t, picture pic, projection P) {
+ triple V=t*v;
+ if(is3D()) {
+ 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(pic != null)
+ dot(pic,project(V,P.t),q);
+ },true);
+ triple R=size*(1,1,1);
+ pic.addBox(v,v,-R,R);
+}
+
+void dot(picture pic=currentpicture, triple[] v, material p=currentpen,
+ light light=nolight, string name="", render render=defaultrender)
+{
+ if(v.length > 0) {
+ // Remove duplicate points.
+ v=sort(v,lexorder);
+
+ triple last=v[0];
+ begingroup3(pic,name == "" ? "dots" : name,render);
+ dot(pic,last,p,light,partname(0),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);
+ last=V;
+ }
+ }
+ endgroup3(pic);
+ }
+}
+
+void dot(picture pic=currentpicture, explicit path3 g, material p=currentpen,
+ light light=nolight, string name="",
+ render render=defaultrender)
+{
+ dot(pic,sequence(new triple(int i) {return point(g,i);},size(g)),
+ p,light,name,render);
+}
+
+void dot(picture pic=currentpicture, path3[] g, material p=currentpen,
+ light light=nolight, string name="", render render=defaultrender)
+{
+ int sum;
+ for(path3 G : g)
+ sum += size(G);
+ int i,j;
+ dot(pic,sequence(new triple(int) {
+ while(j >= size(g[i])) {
+ ++i;
+ j=0;
+ }
+ triple v=point(g[i],j);
+ ++j;
+ return v;
+ },sum),p,light,name,render);
+}
+
+void dot(picture pic=currentpicture, Label L, triple v, align align=NoAlign,
+ string format=defaultformat, material p=currentpen,
+ light light=nolight, string name="", render render=defaultrender)
+{
+ Label L=L.copy();
+ if(L.s == "") {
+ if(format == "") format=defaultformat;
+ L.s="("+format(format,v.x)+","+format(format,v.y)+","+
+ format(format,v.z)+")";
+ }
+ L.align(align,E);
+ L.p((pen) p);
+ dot(pic,v,p,light,name,render);
+ label(pic,L,v,render);
+}
+
+void pixel(picture pic=currentpicture, triple v, pen p=currentpen,
+ real width=1)
+{
+ real h=0.5*width;
+ pic.add(new void(frame f, transform3 t, picture pic, projection P) {
+ triple V=t*v;
+ if(is3D())
+ drawpixel(f,V,p,width);
+ if(pic != null) {
+ triple R=h*unit(cross(unit(P.vector()),P.up));
+ pair z=project(V,P.t);
+ real h=0.5*abs(project(V+R,P.t)-project(V-R,P.t));
+ pair r=h*(1,1)/mm;
+ fill(pic,box(z-r,z+r),p,false);
+ }
+ },true);
+ triple R=h*(1,1,1);
+ pic.addBox(v,v,-R,R);
+}
+
+pair minbound(triple[] A, projection P)
+{
+ pair b=project(A[0],P);
+ for(triple v : A)
+ b=minbound(b,project(v,P.t));
+ return b;
+}
+
+pair maxbound(triple[] A, projection P)
+{
+ pair b=project(A[0],P);
+ for(triple v : A)
+ b=maxbound(b,project(v,P.t));
+ return b;
+}
+
+pair minbound(triple[][] A, projection P)
+{
+ pair b=project(A[0][0],P);
+ for(triple[] a : A) {
+ for(triple v : a) {
+ b=minbound(b,project(v,P.t));
+ }
+ }
+ return b;
+}
+
+pair maxbound(triple[][] A, projection P)
+{
+ pair b=project(A[0][0],P);
+ for(triple[] a : A) {
+ for(triple v : a) {
+ b=maxbound(b,project(v,P.t));
+ }
+ }
+ return b;
+}
+
+triple[][] operator / (triple[][] a, real[][] b)
+{
+ triple[][] A=new triple[a.length][];
+ for(int i=0; i < a.length; ++i) {
+ triple[] ai=a[i];
+ real[] bi=b[i];
+ A[i]=sequence(new triple(int j) {return ai[j]/bi[j];},ai.length);
+ }
+ return A;
+}
+
+// Draw a NURBS curve.
+void draw(picture pic=currentpicture, triple[] P, real[] knot,
+ real[] weights=new real[], pen p=currentpen, string name="",
+ render render=defaultrender)
+{
+ P=copy(P);
+ knot=copy(knot);
+ weights=copy(weights);
+ pic.add(new void(frame f, transform3 t, picture pic, projection Q) {
+ if(is3D()) {
+ triple[] P=t*P;
+ begingroup3(f,name == "" ? "curve" : name,render);
+ draw(f,P,knot,weights,p);
+ endgroup3(f);
+ if(pic != null)
+ pic.addBox(minbound(P,Q),maxbound(P,Q));
+ }
+ },true);
+ pic.addBox(minbound(P),maxbound(P));
+}
+
+// Draw a NURBS surface.
+void draw(picture pic=currentpicture, triple[][] P, real[] uknot, real[] vknot,
+ real[][] weights=new real[][], material m=currentpen,
+ pen[] colors=new pen[], light light=currentlight, string name="",
+ render render=defaultrender)
+{
+ if(colors.length > 0)
+ m=mean(colors);
+ bool lighton=light.on();
+ P=copy(P);
+ uknot=copy(uknot);
+ vknot=copy(vknot);
+ weights=copy(weights);
+ colors=copy(colors);
+ pic.add(new void(frame f, transform3 t, picture pic, projection Q) {
+ if(is3D()) {
+ 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);
+ if(pic != null)
+ pic.addBox(minbound(P,Q),maxbound(P,Q));
+ }
+ },true);
+ pic.addBox(minbound(P),maxbound(P));
+}
diff --git a/Master/texmf-dist/asymptote/three_tube.asy b/Master/texmf-dist/asymptote/three_tube.asy
new file mode 100644
index 00000000000..aa5688f081e
--- /dev/null
+++ b/Master/texmf-dist/asymptote/three_tube.asy
@@ -0,0 +1,417 @@
+void render(path3 s, void f(path3, real), render render=defaultrender)
+{
+ real granularity=render.tubegranularity;
+ void Split(triple z0, triple c0, triple c1, triple z1, real t0=0, real t1=1,
+ real depth=mantissaBits) {
+ if(depth > 0) {
+ real S=straightness(z0,c0,c1,z1);
+ if(S > 0) {
+ --depth;
+ if(S > max(granularity*max(abs(z0),abs(c0),abs(c1),abs(z1)))) {
+ triple m0=0.5*(z0+c0);
+ triple m1=0.5*(c0+c1);
+ triple m2=0.5*(c1+z1);
+ triple m3=0.5*(m0+m1);
+ triple m4=0.5*(m1+m2);
+ triple m5=0.5*(m3+m4);
+ real tm=0.5*(t0+t1);
+ Split(z0,m0,m3,m5,t0,tm,depth);
+ Split(m5,m4,m2,z1,tm,t1,depth);
+ return;
+ }
+ }
+ }
+ f(z0..controls c0 and c1..z1,t0);
+ }
+ Split(point(s,0),postcontrol(s,0),precontrol(s,1),point(s,1));
+}
+
+struct rmf
+{
+ triple p,r,t,s;
+ void operator init(triple p, triple r, triple t)
+ {
+ this.p=p;
+ this.r=r;
+ this.t=t;
+ s=cross(t,r);
+ }
+}
+
+// Rotation minimizing frame
+// http://www.cs.hku.hk/research/techreps/document/TR-2007-07.pdf
+rmf[] rmf(path3 g, real[] t)
+{
+ rmf[] R=new rmf[t.length];
+ triple d=dir(g,0);
+ R[0]=rmf(point(g,0),perp(d),d);
+ for(int i=1; i < t.length; ++i) {
+ rmf Ri=R[i-1];
+ real t=t[i];
+ triple p=point(g,t);
+ triple v1=p-Ri.p;
+ if(v1 != O) {
+ triple r=Ri.r;
+ triple u1=unit(v1);
+ triple ti=Ri.t;
+ triple tp=ti-2*dot(u1,ti)*u1;
+ ti=dir(g,t);
+ triple rp=r-2*dot(u1,r)*u1;
+ triple u2=unit(ti-tp);
+ rp=rp-2*dot(u2,rp)*u2;
+ R[i]=rmf(p,unit(rp),unit(ti));
+ } else
+ R[i]=R[i-1];
+ }
+ return R;
+}
+
+private real[][][] bispline0(real[][] z, real[][] p, real[][] q, real[][] r,
+ real[] x, real[] y, bool[][] cond={})
+{ // z[i][j] is the value at (x[i],y[j])
+ // p and q are the first derivatives with respect to x and y, respectively
+ // r is the second derivative ddu/dxdy
+ int n=x.length-1;
+ int m=y.length-1;
+
+ bool all=cond.length == 0;
+
+ int count;
+ if(all)
+ count=n*m;
+ else {
+ count=0;
+ for(int i=0; i < n; ++i) {
+ bool[] condi=cond[i];
+ for(int j=0; j < m; ++j)
+ if(condi[j]) ++count;
+ }
+ }
+
+ real[][][] s=new real[count][][];
+ int k=0;
+ for(int i=0; i < n; ++i) {
+ int ip=i+1;
+ real xi=x[i];
+ real xp=x[ip];
+ real hx=(xp-xi)/3;
+ real[] zi=z[i];
+ real[] zp=z[ip];
+ real[] ri=r[i];
+ real[] rp=r[ip];
+ real[] pi=p[i];
+ real[] pp=p[ip];
+ real[] qi=q[i];
+ real[] qp=q[ip];
+ bool[] condi=all ? null : cond[i];
+ for(int j=0; j < m; ++j) {
+ if(all || condi[j]) {
+ real yj=y[j];
+ int jp=j+1;
+ real yp=y[jp];
+ real hy=(yp-yj)/3;
+ real hxy=hx*hy;
+ real zij=zi[j];
+ real zip=zi[jp];
+ real zpj=zp[j];
+ real zpp=zp[jp];
+ real pij=hx*pi[j];
+ real ppj=hx*pp[j];
+ real qip=hy*qi[jp];
+ real qpp=hy*qp[jp];
+ real zippip=zip+hx*pi[jp];
+ real zppmppp=zpp-hx*pp[jp];
+ real zijqij=zij+hy*qi[j];
+ real zpjqpj=zpj+hy*qp[j];
+
+ s[k]=new real[][] {{zij,zijqij,zip-qip,zip},
+ {zij+pij,zijqij+pij+hxy*ri[j],
+ zippip-qip-hxy*ri[jp],zippip},
+ {zpj-ppj,zpjqpj-ppj-hxy*rp[j],
+ zppmppp-qpp+hxy*rp[jp],zppmppp},
+ {zpj,zpjqpj,zpp-qpp,zpp}};
+ ++k;
+ }
+ }
+ }
+
+ return s;
+}
+
+// return the surface values described by a real matrix f, interpolated with
+// xsplinetype and ysplinetype.
+real[][][] bispline(real[][] f, real[] x, real[] y,
+ splinetype xsplinetype=null,
+ splinetype ysplinetype=xsplinetype, bool[][] cond={})
+{
+ real epsilon=sqrtEpsilon*norm(y);
+ if(xsplinetype == null)
+ xsplinetype=(abs(x[0]-x[x.length-1]) <= epsilon) ? periodic : notaknot;
+ if(ysplinetype == null)
+ ysplinetype=(abs(y[0]-y[y.length-1]) <= epsilon) ? periodic : notaknot;
+ int n=x.length; int m=y.length;
+ real[][] ft=transpose(f);
+ real[][] tp=new real[m][];
+ for(int j=0; j < m; ++j)
+ tp[j]=xsplinetype(x,ft[j]);
+ real[][] q=new real[n][];
+ for(int i=0; i < n; ++i)
+ q[i]=ysplinetype(y,f[i]);
+ real[][] qt=transpose(q);
+ real[] d1=xsplinetype(x,qt[0]);
+ real[] d2=xsplinetype(x,qt[m-1]);
+ real[][] r=new real[n][];
+ real[][] p=transpose(tp);
+ for(int i=0; i < n; ++i)
+ r[i]=clamped(d1[i],d2[i])(y,p[i]);
+ return bispline0(f,p,q,r,x,y,cond);
+}
+
+bool uperiodic(real[][] a) {
+ int n=a.length;
+ if(n == 0) return false;
+ int m=a[0].length;
+ real[] a0=a[0];
+ real[] a1=a[n-1];
+ real epsilon=sqrtEpsilon*norm(a);
+ for(int j=0; j < m; ++j)
+ if(abs(a0[j]-a1[j]) > epsilon) return false;
+ return true;
+}
+bool vperiodic(real[][] a) {
+ int n=a.length;
+ if(n == 0) return false;
+ int m=a[0].length-1;
+ real epsilon=sqrtEpsilon*norm(a);
+ for(int i=0; i < n; ++i)
+ if(abs(a[i][0]-a[i][m]) > epsilon) return false;
+ return true;
+}
+
+// return the surface described by a parametric function f evaluated at u and v
+// and interpolated with usplinetype and vsplinetype.
+surface surface(triple f(pair z), real[] u, real[] v,
+ splinetype[] usplinetype, splinetype[] vsplinetype=Spline,
+ bool cond(pair z)=null)
+{
+ int nu=u.length-1;
+ int nv=v.length-1;
+ real[] ipt=sequence(u.length);
+ real[] jpt=sequence(v.length);
+ real[][] fx=new real[u.length][v.length];
+ real[][] fy=new real[u.length][v.length];
+ real[][] fz=new real[u.length][v.length];
+
+ bool[][] active;
+ bool all=cond == null;
+ if(!all) active=new bool[u.length][v.length];
+
+ for(int i=0; i <= nu; ++i) {
+ real ui=u[i];
+ real[] fxi=fx[i];
+ real[] fyi=fy[i];
+ real[] fzi=fz[i];
+ bool[] activei=all ? null : active[i];
+ for(int j=0; j <= nv; ++j) {
+ pair z=(ui,v[j]);
+ triple f=(all || (activei[j]=cond(z))) ? f(z) : O;
+ fxi[j]=f.x;
+ fyi[j]=f.y;
+ fzi[j]=f.z;
+ }
+ }
+
+ if(usplinetype.length == 0) {
+ usplinetype=new splinetype[] {uperiodic(fx) ? periodic : notaknot,
+ uperiodic(fy) ? periodic : notaknot,
+ uperiodic(fz) ? periodic : notaknot};
+ } else if(usplinetype.length != 3) abort("usplinetype must have length 3");
+
+ if(vsplinetype.length == 0) {
+ vsplinetype=new splinetype[] {vperiodic(fx) ? periodic : notaknot,
+ vperiodic(fy) ? periodic : notaknot,
+ vperiodic(fz) ? periodic : notaknot};
+ } else if(vsplinetype.length != 3) abort("vsplinetype must have length 3");
+
+ real[][][] sx=bispline(fx,ipt,jpt,usplinetype[0],vsplinetype[0],active);
+ real[][][] sy=bispline(fy,ipt,jpt,usplinetype[1],vsplinetype[1],active);
+ real[][][] sz=bispline(fz,ipt,jpt,usplinetype[2],vsplinetype[2],active);
+
+ surface s=surface(sx.length);
+ s.index=new int[nu][nv];
+ int k=-1;
+ for(int i=0; i < nu; ++i) {
+ int[] indexi=s.index[i];
+ for(int j=0; j < nv; ++j)
+ indexi[j]=++k;
+ }
+
+ for(int k=0; k < sx.length; ++k) {
+ triple[][] Q=new triple[4][];
+ real[][] Px=sx[k];
+ real[][] Py=sy[k];
+ real[][] Pz=sz[k];
+ for(int i=0; i < 4 ; ++i) {
+ real[] Pxi=Px[i];
+ real[] Pyi=Py[i];
+ real[] Pzi=Pz[i];
+ Q[i]=new triple[] {(Pxi[0],Pyi[0],Pzi[0]),
+ (Pxi[1],Pyi[1],Pzi[1]),
+ (Pxi[2],Pyi[2],Pzi[2]),
+ (Pxi[3],Pyi[3],Pzi[3])};
+ }
+ s.s[k]=patch(Q);
+ }
+
+ if(usplinetype[0] == periodic && usplinetype[1] == periodic &&
+ usplinetype[1] == periodic) s.ucyclic(true);
+
+ if(vsplinetype[0] == periodic && vsplinetype[1] == periodic &&
+ vsplinetype[1] == periodic) s.vcyclic(true);
+
+ return s;
+}
+
+path3 interp(path3 a, path3 b, real t)
+{
+ int n=size(a);
+ return path3(sequence(new triple(int i) {
+ return interp(precontrol(a,i),precontrol(b,i),t);},n),
+ sequence(new triple(int i) {return interp(point(a,i),point(b,i),t);},n),
+ sequence(new triple(int i) {return interp(postcontrol(a,i),
+ postcontrol(b,i),t);},n),
+ sequence(new bool(int i) {return straight(a,i) && straight(b,i);},n),
+ cyclic(a) && cyclic(b));
+}
+
+struct tube
+{
+ surface s;
+ path3 center; // tube axis
+
+ void Null(transform3) {}
+ void Null(transform3, bool) {}
+
+ void operator init(path3 p, real width, render render=defaultrender,
+ void cylinder(transform3)=Null,
+ void sphere(transform3, bool half)=Null,
+ void pipe(path3, path3)=null) {
+ real r=0.5*width;
+
+ void generate(path3 p) {
+ int n=length(p);
+ if(piecewisestraight(p)) {
+ for(int i=0; i < n; ++i) {
+ triple v=point(p,i);
+ triple u=point(p,i+1)-v;
+ transform3 t=shift(v)*align(unit(u))*scale(r,r,abs(u));
+ s.append(t*unitcylinder);
+ cylinder(t);
+ }
+ center=center&p;
+ } else {
+ real[] T;
+ path3 G;
+ for(int i=0; i < n; ++i)
+ render(subpath(p,i,i+1),
+ new void(path3 g, real s) {
+ G=G&g;
+ T.push(i+s);
+ },render);
+ T.push(n);
+ T.cyclic=cyclic(p);
+ rmf[] rmf=rmf(p,T);
+ triple f(pair t) {
+ rmf R=rmf[round(t.x)];
+ int n=round(t.y);
+ static real[] x={1,0,-1,0};
+ static real[] y={0,1,0,-1};
+ return point(G,t.x)+r*(R.r*x[n]-R.s*y[n]);
+ }
+
+ static real[] v={0,1,2,3,0};
+ static real[] circular(real[] x, real[] y) {
+ static real a=8/3*(sqrt(2)-1);
+ return a*periodic(x,y);
+ }
+
+ static splinetype[] Monotonic={monotonic,monotonic,monotonic};
+ static splinetype[] Circular={circular,circular,circular};
+ if(T.length > 0) {
+ surface S=surface(f,sequence(T.length),v,Monotonic,Circular);
+ s.append(S);
+
+ // Compute center of tube:
+ int n=S.index.length;
+ if(T.cyclic) --n;
+ triple[] pre=new triple[n+1];
+ triple[] point=new triple[n+1];
+ triple[] post=new triple[n+1];
+
+ int[] index=S.index[0];
+ triple Point;
+ for(int m=0; m < 4; ++m)
+ Point += S.s[index[m]].P[0][0];
+ pre[0]=point[0]=0.25*Point;
+
+ for(int i=0; i < n; ++i) {
+ index=S.index[i];
+ triple Pre,Point,Post;
+ for(int m=0; m < 4; ++m) {
+ triple [][] P=S.s[index[m]].P;
+ Post += P[1][0];
+ Pre += P[2][0];
+ Point += P[3][0];
+ }
+ post[i]=0.25*Post;
+ pre[i+1]=0.25*Pre;
+ point[i+1]=0.25*Point;
+
+ }
+
+ index=S.index[n-1];
+ triple Post;
+ for(int m=0; m < 4; ++m)
+ Post += S.s[index[m]].P[3][0];
+ post[n]=0.25*Post;
+
+ bool[] b=array(n+1,false);
+ path3 Center=path3(pre,point,post,b,T.cyclic);
+ center=center&Center;
+
+ if(pipe != null) { // Compute path along tube
+ triple[] pre=new triple[n+1];
+ triple[] point=new triple[n+1];
+ triple[] post=new triple[n+1];
+ pre[0]=point[0]=S.s[S.index[0][0]].P[0][0];
+ for(int i=0; i < n; ++i) {
+ triple [][] P=S.s[S.index[i][0]].P;
+ post[i]=P[1][0];
+ pre[i+1]=P[2][0];
+ point[i+1]=P[3][0];
+ }
+ post[n]=S.s[S.index[n-1][0]].P[3][0];
+ pipe(Center,path3(pre,point,post,b,T.cyclic));
+ }
+ }
+ }
+ }
+
+ transform3 t=scale3(r);
+ bool cyclic=cyclic(p);
+ int begin=0;
+ int n=length(p);
+ for(int i=cyclic ? 0 : 1; i < n; ++i)
+ 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)),
+ half=straight(p,i-1) && straight(p,i));
+ begin=i;
+ }
+ generate(subpath(p,begin,n));
+ }
+}
diff --git a/Master/texmf-dist/asymptote/tree.asy b/Master/texmf-dist/asymptote/tree.asy
new file mode 100644
index 00000000000..1e603ec4b6b
--- /dev/null
+++ b/Master/texmf-dist/asymptote/tree.asy
@@ -0,0 +1,86 @@
+/*****
+ * treedef.asy
+ * Andy Hammerlindl 2003/10/25
+ *
+ * Implements a dynamic binary search tree.
+ *****/
+
+struct tree
+{
+ tree left;
+ tree right;
+ int key = 0;
+ int value = 0;
+}
+
+tree newtree()
+{
+ return null;
+}
+
+tree add(tree t, int key, int value)
+{
+ if (t == null) {
+ tree tt;
+ tt.key = key; tt.value = value;
+ return tt;
+ }
+ else if (key == t.key) {
+ return t;
+ }
+ else if (key < t.key) {
+ tree tt;
+ tt.left = add(t.left, key, value);
+ tt.key = t.key;
+ tt.value = t.value;
+ tt.right = t.right;
+ return tt;
+ }
+ else {
+ tree tt;
+ tt.left = t.left;
+ tt.key = t.key;
+ tt.value = t.value;
+ tt.right = add(t.right, key, value);
+ return tt;
+ }
+}
+
+bool contains(tree t, int key)
+{
+ if (t == null)
+ return false;
+ else if (key == t.key)
+ return true;
+ else if (key < t.key)
+ return contains(t.left, key);
+ else
+ return contains(t.right, key);
+}
+
+int lookup(tree t, int key)
+{
+ if (t == null)
+ return 0;
+ else if (key == t.key)
+ return t.value;
+ else if (key < t.key)
+ return lookup(t.left, key);
+ else
+ return lookup(t.right, key);
+}
+
+void write(file out=stdout, tree t)
+{
+ if (t != null) {
+ if(t.left != null) {
+ write(out,t.left);
+ }
+ write(out,t.key);
+ write(out,"->");
+ write(out,t.value,endl);
+ if (t.right != null) {
+ write(out,t.right);
+ }
+ }
+}
diff --git a/Master/texmf-dist/asymptote/trembling.asy b/Master/texmf-dist/asymptote/trembling.asy
new file mode 100644
index 00000000000..6f6f5973332
--- /dev/null
+++ b/Master/texmf-dist/asymptote/trembling.asy
@@ -0,0 +1,216 @@
+// Copyright(c) 2008, Philippe Ivaldi.
+// Simplified by John Bowman 02Feb2011
+// http: //www.piprime.fr/
+// trembling.asy: handwriting package for the software Asymptote.
+
+// This program is free software; you can redistribute it and/or modify
+// 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.
+
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// COMMENTARY:
+
+// THANKS:
+
+// BUGS:
+// magnetic points are experimental...
+
+// CODE:
+
+real magneticRadius=1; // unit is bp in postscript coordinates.
+real trembleFuzz(){return min(1e-3,magneticRadius/10);}
+
+/* <asyxml> <variable type="real" signature="trembleAngle"> <code> </asyxml> */
+real trembleAngle=4, trembleFrequency=0.5, trembleRandom=2;
+/* <asyxml> </code> <documentation> Variables used by the routine 'tremble'.
+ </documentation> </variable> </asyxml> */
+
+struct tremble
+{
+ static real test=5;
+
+ real angle,frequency,random,fuzz;
+
+ pair[] single(pair[] P)
+ {
+ pair[] op;
+ bool allow;
+ for(int i=0; i < P.length-1; ++i) {
+ allow=true;
+ for(int j=i+1; j < P.length; ++j) {
+ if(abs(P[i]-P[j]) < magneticRadius) {
+ allow=false;
+ break;
+ }
+ }
+ if(allow) op.push(P[i]);
+ }
+ if(P.length > 0) op.push(P[P.length-1]);
+ return op;
+ }
+
+ /* <asyxml> <function type="pair" signature="attract(pair,path,real)"> <code>
+ </asyxml> */
+ real atime(pair m, path g, real fuzz=trembleFuzz())
+ {/* <asyxml> </code> <documentation> Return the time of the point on path g
+ nearest to 'm'.
+ 'fuzz' is the argument 'fuzz' of 'intersect'. </documentation> </function>
+ </asyxml> */
+ if(length(g) == 0) return 0.0;
+ real[] t=intersect(m,g,fuzz);
+ if(t.length > 0) return t[1];
+ real ot;
+ static real eps=sqrt(realEpsilon);
+ real abmax=abs(max(g)-m), abmin=abs(min(g)-m);
+ real initr=abs(m-midpoint(g));
+ real maxR=2*max(abmax,abmin), step=eps, r=initr;
+ real shx=1e-4;
+ transform T=shift(m);
+ path ig;
+ if(t.length > 0) ot=t[1];
+ real rm=0, rM=r;
+ while(rM-rm > eps) {
+ r=(rm+rM)/2;
+ t=intersect(T*scale(r)*unitcircle,g,fuzz);
+ if(t.length <= 0) {
+ rm=r;
+ } else {
+ rM=r;
+ ot=t[1];
+ }
+ }
+ return ot;
+ }
+
+ /* <asyxml> <function type="path" signature="addnode(path,real)"> <code>
+ </asyxml> */
+ path addnode(path g, real t)
+ {/* <asyxml> </code> <documentation> Add a node to 'g' at point(g,t).
+ </documentation> </function> </asyxml> */
+ real l=length(g);
+ real rt=t % 1;
+ if(l == 0 || (t > l && !cyclic(g)) || rt == 0) return g;
+ if(cyclic(g)) t=t % l;
+ int t0=floor(t);
+ int t1=t0+1;
+ pair z0=point(g,t0), z1=point(g,t1),
+ c0=postcontrol(g,t0), c1=precontrol(g,t1),
+ m0=(1-rt)*z0+rt*c0, m1=(1-rt)*c0+rt*c1,
+ m2=(1-rt)*c1+rt*z1, m3=(1-rt)*m0+rt*m1,
+ m4=(1-rt)*m1+rt*m2;
+ guide og=subpath(g,0,t0)..controls m0 and m3..point(g,t);
+ if(cyclic(g)) {
+ if(t1 < l)
+ og=og..controls m4 and m2..subpath(g,t1,l)&cycle;
+ else og=og..controls m4 and m2..cycle;
+ } else og=og..controls m4 and m2..subpath(g,t1,l);
+ return og;
+ }
+
+ path addnodes(path g, real fuzz=trembleFuzz()...pair[] P)
+ {
+ pair[] P=single(P);
+ if(length(g) == 0 || P.length == 0 || magneticRadius <= 0) return g;
+ path og=g;
+ for(pair tp: P) {
+ real t=atime(tp,og,fuzz);
+ real d=abs(tp-point(og,t));
+ if(d < magneticRadius) og=addnode(og,t);
+ }
+ return og;
+ }
+
+ /* <asyxml> <function type="path" signature="addnodes(path,int)"> <code>
+ </asyxml> */
+ path addnodes(path g, int n)
+ {/* <asyxml> </code> <documentation> Add 'n' nodes between each node of 'g'.
+ </documentation> </function> </asyxml> */
+ real l=length(g);
+ if(n == 0 || l == 0) return g;
+ path og=g;
+ int np=0;
+ for(int i=0; i < l; ++i) {
+ real step=1/(n+1);
+ for(int j=0; j < n; ++j) {
+ og=addnode(og,i*(n+1)+j+step);
+ step=1/(n-j);
+ }
+ }
+ return og;
+ }
+
+ /* <asyxml> <function type="path"
+ signature="tremble(path,real,real,real,real,pair[] )"> <code> </asyxml> */
+ void operator init(real angle=trembleAngle, real frequency=trembleFrequency,
+ real random=trembleRandom, real fuzz=trembleFuzz()) {
+ this.angle=angle;
+ this.frequency=frequency;
+ this.random=random;
+ this.fuzz=fuzz;
+ }
+
+ path deform(path g...pair[] magneticPoints) {
+ /* <asyxml> </code> <documentation> Return g as it was handwriting.
+ The postcontrols and precontrols of the nodes of g will be rotated
+ by an angle proportional to 'angle'(in degrees).
+ If frequency < 1, floor(1/frequency) nodes will be added to g to
+ increase the control points.
+ If frequency>= 1, one point for floor(frequency) will be used to deform
+ the path.
+ 'random' controls the randomized coefficient which will be multiplied
+ by 'angle'.
+ random is 0 means don't use randomized coefficient;
+ The higher 'random' is, the more the trembling is randomized.
+ </documentation> </function> </asyxml> */
+ if(length(g) == 0) return g;
+ g=addnodes(g,fuzz*abs(max(g)-min(g))...magneticPoints);
+ path tg=g;
+ frequency=abs(frequency);
+ int f=abs(floor(1/frequency)-1);
+ tg=addnodes(tg,f);
+ int frequency=floor(frequency);
+ int tf=(frequency == 0) ? 1 : frequency;
+ int l=length(tg);
+ guide og=point(tg,0);
+ random=abs(random);
+ int rsgn(real x){
+ int d2=floor(100*x)-10*floor(10*x);
+ if(d2 == 0) return 1;
+ return 2 % d2 == 0 ? 1 : -1;
+ }
+ real randf()
+ {
+ real or;
+ if(random != 0) {
+ if(1 % tf != 0) or=0;
+ else {
+ real ur=unitrand();
+ or=rsgn(ur)*angle*(1+ur^(1/random));
+ }
+ } else or=rsgn(unitrand())*1.5*angle;
+ return or;
+ }
+
+ real first=randf();
+ for(int i=1; i <= l; ++i) {
+ pair P=point(tg,i);
+ real a=randf();
+ pair post=rotate(a,point(tg,i-1))*postcontrol(tg,i-1);
+ pair pre=rotate((a+randf())/2,P)*precontrol(tg,i);
+ if(i == l && (cyclic(tg)))
+ og=og..controls post and pre..cycle;
+ else
+ og=og..controls post and pre..P;
+ }
+ return og;
+ }
+}
diff --git a/Master/texmf-dist/asymptote/tube.asy b/Master/texmf-dist/asymptote/tube.asy
new file mode 100644
index 00000000000..33e2ea3c868
--- /dev/null
+++ b/Master/texmf-dist/asymptote/tube.asy
@@ -0,0 +1,167 @@
+// Author: Philippe Ivaldi
+// http://www.piprime.fr/
+// Based on this paper:
+// http://www.cs.hku.hk/research/techreps/document/TR-2007-07.pdf
+// Note: the additional rotation for a cyclic smooth spine curve is not
+// yet properly determined.
+// TODO: Implement variational principles for RMF with boundary conditions:
+// minimum total angular speed OR minimum total squared angular speed
+
+import three;
+
+// A 3D version of roundedpath(path, real).
+path3 roundedpath(path3 A, real r)
+{
+ // Author of this routine: Jens Schwaiger
+ guide3 rounded;
+ triple before, after, indir, outdir;
+ int len=length(A);
+ bool cyclic=cyclic(A);
+ if(len < 2) {return A;};
+ if(cyclic) {rounded=point(point(A,0)--point(A,1),r);}
+ else {rounded=point(A,0);}
+ for(int i=1; i < len; i=i+1) {
+ before=point(point(A,i)--point(A,i-1),r);
+ after=point(point(A,i)--point(A,i+1),r);
+ indir=dir(point(A,i-1)--point(A,i),1);
+ outdir=dir(point(A,i)--point(A,i+1),1);
+ rounded=rounded--before{indir}..{outdir}after;
+ }
+ if(cyclic) {
+ before=point(point(A,0)--point(A,len-1),r);
+ indir=dir(point(A,len-1)--point(A,0),1);
+ outdir=dir(point(A,0)--point(A,1),1);
+ rounded=rounded--before{indir}..{outdir}cycle;
+ } else rounded=rounded--point(A,len);
+
+ return rounded;
+}
+
+real[] sample(path3 g, real r, real relstep=0)
+{
+ real[] t;
+ int n=length(g);
+ if(relstep <= 0) {
+ for(int i=0; i < n; ++i) {
+ real S=straightness(g,i);
+ if(S < sqrtEpsilon*r)
+ t.push(i);
+ else
+ render(subpath(g,i,i+1),new void(path3, real s) {t.push(i+s);});
+ }
+ t.push(n);
+ } else {
+ int nb=ceil(1/relstep);
+ relstep=n/nb;
+ for(int i=0; i <= nb; ++i)
+ t.push(i*relstep);
+ }
+ return t;
+}
+
+real degrees(rmf a, rmf b)
+{
+ real d=degrees(acos1(dot(a.r,b.r)));
+ real dt=dot(cross(a.r,b.r),a.t);
+ d=dt > 0 ? d : 360-d;
+ return d%360;
+}
+
+restricted int coloredNodes=1;
+restricted int coloredSegments=2;
+
+struct coloredpath
+{
+ path p;
+ pen[] pens(real);
+ bool usepens=false;
+ int colortype=coloredSegments;
+
+ void operator init(path p, pen[] pens=new pen[] {currentpen},
+ int colortype=coloredSegments)
+ {
+ this.p=p;
+ this.pens=new pen[] (real t) {return pens;};
+ this.usepens=true;
+ this.colortype=colortype;
+ }
+
+ void operator init(path p, pen[] pens(real), int colortype=coloredSegments)
+ {
+ this.p=p;
+ this.pens=pens;
+ this.usepens=true;
+ this.colortype=colortype;
+ }
+
+ void operator init(path p, pen pen(real))
+ {
+ this.p=p;
+ this.pens=new pen[] (real t) {return new pen[] {pen(t)};};
+ this.usepens=true;
+ this.colortype=coloredSegments;
+ }
+}
+
+coloredpath operator cast(path p)
+{
+ coloredpath cp=coloredpath(p);
+ cp.usepens=false;
+ return cp;
+}
+
+coloredpath operator cast(guide p)
+{
+ return coloredpath(p);
+}
+
+private surface surface(rmf[] R, real[] t, coloredpath cp, transform T(real),
+ bool cyclic)
+{
+ path g=cp.p;
+ int l=length(g);
+ bool[] planar;
+ for(int i=0; i < l; ++i)
+ planar[i]=straight(g,i);
+
+ surface s;
+ path3 sec=path3(T(t[0]/l)*g);
+ real adjust=0;
+ if(cyclic) adjust=-degrees(R[0],R[R.length-1])/(R.length-1);
+ path3 sec1=shift(R[0].p)*transform3(R[0].r,R[0].s,R[0].t)*sec,
+ sec2;
+
+ for(int i=1; i < R.length; ++i) {
+ sec=path3(T(t[i]/l)*g);
+ sec2=shift(R[i].p)*transform3(R[i].r,cross(R[i].t,R[i].r),R[i].t)*
+ rotate(i*adjust,Z)*sec;
+ for(int j=0; j < l; ++j) {
+ surface st=surface(subpath(sec1,j,j+1)--subpath(sec2,j+1,j)--cycle,
+ planar=planar[j]);
+ if(cp.usepens) {
+ pen[] tp1=cp.pens(t[i-1]/l), tp2=cp.pens(t[i]/l);
+ tp1.cyclic=true; tp2.cyclic=true;
+ if(cp.colortype == coloredSegments) {
+ st.colors(new pen[][] {{tp1[j],tp1[j],tp2[j],tp2[j]}});
+ } else {
+ st.colors(new pen[][] {{tp1[j],tp1[j+1],tp2[j+1],tp2[j]}});
+ }
+ }
+ s.append(st);
+ }
+ sec1=sec2;
+ }
+ return s;
+}
+
+surface tube(path3 g, coloredpath section,
+ transform T(real)=new transform(real t) {return identity();},
+ real corner=1, real relstep=0)
+{
+ pair M=max(section.p), m=min(section.p);
+ real[] t=sample(g,max(M.x-m.x,M.y-m.y)/max(realEpsilon,abs(corner)),
+ min(abs(relstep),1));
+ bool cyclic=cyclic(g);
+ t.cyclic=cyclic;
+ return surface(rmf(g,t),t,section,T,cyclic);
+}
diff --git a/Master/texmf-dist/asymptote/unicode.asy b/Master/texmf-dist/asymptote/unicode.asy
new file mode 100644
index 00000000000..33571e8341a
--- /dev/null
+++ b/Master/texmf-dist/asymptote/unicode.asy
@@ -0,0 +1,2 @@
+usepackage("ucs");
+usepackage("inputenc","utf8x");
diff --git a/Master/texmf-dist/asymptote/version.asy b/Master/texmf-dist/asymptote/version.asy
new file mode 100644
index 00000000000..2912aa57db5
--- /dev/null
+++ b/Master/texmf-dist/asymptote/version.asy
@@ -0,0 +1 @@
+string VERSION="2.16";
diff --git a/Master/texmf-dist/asymptote/x11colors.asy b/Master/texmf-dist/asymptote/x11colors.asy
new file mode 100644
index 00000000000..77e0401f06e
--- /dev/null
+++ b/Master/texmf-dist/asymptote/x11colors.asy
@@ -0,0 +1,145 @@
+pen rgbint(int r, int g, int b)
+{
+ return rgb(r/255,g/255,b/255);
+}
+
+pen AliceBlue=rgbint(240,248,255);
+pen AntiqueWhite=rgbint(250,235,215);
+pen Aqua=rgbint(0,255,255);
+pen Aquamarine=rgbint(127,255,212);
+pen Azure=rgbint(240,255,255);
+pen Beige=rgbint(245,245,220);
+pen Bisque=rgbint(255,228,196);
+pen Black=rgbint(0,0,0);
+pen BlanchedAlmond=rgbint(255,235,205);
+pen Blue=rgbint(0,0,255);
+pen BlueViolet=rgbint(138,43,226);
+pen Brown=rgbint(165,42,42);
+pen BurlyWood=rgbint(222,184,135);
+pen CadetBlue=rgbint(95,158,160);
+pen Chartreuse=rgbint(127,255,0);
+pen Chocolate=rgbint(210,105,30);
+pen Coral=rgbint(255,127,80);
+pen CornflowerBlue=rgbint(100,149,237);
+pen Cornsilk=rgbint(255,248,220);
+pen Crimson=rgbint(220,20,60);
+pen Cyan=rgbint(0,255,255);
+pen DarkBlue=rgbint(0,0,139);
+pen DarkCyan=rgbint(0,139,139);
+pen DarkGoldenrod=rgbint(184,134,11);
+pen DarkGray=rgbint(169,169,169);
+pen DarkGreen=rgbint(0,100,0);
+pen DarkKhaki=rgbint(189,183,107);
+pen DarkMagenta=rgbint(139,0,139);
+pen DarkOliveGreen=rgbint(85,107,47);
+pen DarkOrange=rgbint(255,140,0);
+pen DarkOrchid=rgbint(153,50,204);
+pen DarkRed=rgbint(139,0,0);
+pen DarkSalmon=rgbint(233,150,122);
+pen DarkSeaGreen=rgbint(143,188,143);
+pen DarkSlateBlue=rgbint(72,61,139);
+pen DarkSlateGray=rgbint(47,79,79);
+pen DarkTurquoise=rgbint(0,206,209);
+pen DarkViolet=rgbint(148,0,211);
+pen DeepPink=rgbint(255,20,147);
+pen DeepSkyBlue=rgbint(0,191,255);
+pen DimGray=rgbint(105,105,105);
+pen DodgerBlue=rgbint(30,144,255);
+pen FireBrick=rgbint(178,34,34);
+pen FloralWhite=rgbint(255,250,240);
+pen ForestGreen=rgbint(34,139,34);
+pen Fuchsia=rgbint(255,0,255);
+pen Gainsboro=rgbint(220,220,220);
+pen GhostWhite=rgbint(248,248,255);
+pen Gold=rgbint(255,215,0);
+pen Goldenrod=rgbint(218,165,32);
+pen Gray=rgbint(128,128,128);
+pen Green=rgbint(0,128,0);
+pen GreenYellow=rgbint(173,255,47);
+pen Honeydew=rgbint(240,255,240);
+pen HotPink=rgbint(255,105,180);
+pen IndianRed=rgbint(205,92,92);
+pen Indigo=rgbint(75,0,130);
+pen Ivory=rgbint(255,255,240);
+pen Khaki=rgbint(240,230,140);
+pen Lavender=rgbint(230,230,250);
+pen LavenderBlush=rgbint(255,240,245);
+pen LawnGreen=rgbint(124,252,0);
+pen LemonChiffon=rgbint(255,250,205);
+pen LightBlue=rgbint(173,216,230);
+pen LightCoral=rgbint(240,128,128);
+pen LightCyan=rgbint(224,255,255);
+pen LightGoldenrodYellow=rgbint(250,250,210);
+pen LightGreen=rgbint(144,238,144);
+pen LightGrey=rgbint(211,211,211);
+pen LightPink=rgbint(255,182,193);
+pen LightSalmon=rgbint(255,160,122);
+pen LightSeaGreen=rgbint(32,178,170);
+pen LightSkyBlue=rgbint(135,206,250);
+pen LightSlateGray=rgbint(119,136,153);
+pen LightSteelBlue=rgbint(176,196,222);
+pen LightYellow=rgbint(255,255,224);
+pen Lime=rgbint(0,255,0);
+pen LimeGreen=rgbint(50,205,50);
+pen Linen=rgbint(250,240,230);
+pen Magenta=rgbint(255,0,255);
+pen Maroon=rgbint(128,0,0);
+pen MediumAquamarine=rgbint(102,205,170);
+pen MediumBlue=rgbint(0,0,205);
+pen MediumOrchid=rgbint(186,85,211);
+pen MediumPurple=rgbint(147,112,219);
+pen MediumSeaGreen=rgbint(60,179,113);
+pen MediumSlateBlue=rgbint(123,104,238);
+pen MediumSpringGreen=rgbint(0,250,154);
+pen MediumTurquoise=rgbint(72,209,204);
+pen MediumVioletRed=rgbint(199,21,133);
+pen MidnightBlue=rgbint(25,25,112);
+pen MintCream=rgbint(245,255,250);
+pen MistyRose=rgbint(255,228,225);
+pen Moccasin=rgbint(255,228,181);
+pen NavajoWhite=rgbint(255,222,173);
+pen Navy=rgbint(0,0,128);
+pen OldLace=rgbint(253,245,230);
+pen Olive=rgbint(128,128,0);
+pen OliveDrab=rgbint(107,142,35);
+pen Orange=rgbint(255,165,0);
+pen OrangeRed=rgbint(255,69,0);
+pen Orchid=rgbint(218,112,214);
+pen PaleGoldenrod=rgbint(238,232,170);
+pen PaleGreen=rgbint(152,251,152);
+pen PaleTurquoise=rgbint(175,238,238);
+pen PaleVioletRed=rgbint(219,112,147);
+pen PapayaWhip=rgbint(255,239,213);
+pen PeachPuff=rgbint(255,218,185);
+pen Peru=rgbint(205,133,63);
+pen Pink=rgbint(255,192,203);
+pen Plum=rgbint(221,160,221);
+pen PowderBlue=rgbint(176,224,230);
+pen Purple=rgbint(128,0,128);
+pen Red=rgbint(255,0,0);
+pen RosyBrown=rgbint(188,143,143);
+pen RoyalBlue=rgbint(65,105,225);
+pen SaddleBrown=rgbint(139,69,19);
+pen Salmon=rgbint(250,128,114);
+pen SandyBrown=rgbint(244,164,96);
+pen SeaGreen=rgbint(46,139,87);
+pen Seashell=rgbint(255,245,238);
+pen Sienna=rgbint(160,82,45);
+pen Silver=rgbint(192,192,192);
+pen SkyBlue=rgbint(135,206,235);
+pen SlateBlue=rgbint(106,90,205);
+pen SlateGray=rgbint(112,128,144);
+pen Snow=rgbint(255,250,250);
+pen SpringGreen=rgbint(0,255,127);
+pen SteelBlue=rgbint(70,130,180);
+pen Tan=rgbint(210,180,140);
+pen Teal=rgbint(0,128,128);
+pen Thistle=rgbint(216,191,216);
+pen Tomato=rgbint(255,99,71);
+pen Turquoise=rgbint(64,224,208);
+pen Violet=rgbint(238,130,238);
+pen Wheat=rgbint(245,222,179);
+pen White=rgbint(255,255,255);
+pen WhiteSmoke=rgbint(245,245,245);
+pen Yellow=rgbint(255,255,0);
+pen YellowGreen=rgbint(154,205,50);