diff options
105 files changed, 39964 insertions, 52 deletions
diff --git a/Master/bin/i386-linux/asy b/Master/bin/i386-linux/asy Binary files differnew file mode 100755 index 00000000000..4aeedc65b29 --- /dev/null +++ b/Master/bin/i386-linux/asy diff --git a/Master/bin/i386-linux/xasy b/Master/bin/i386-linux/xasy new file mode 120000 index 00000000000..7e22760c64a --- /dev/null +++ b/Master/bin/i386-linux/xasy @@ -0,0 +1 @@ +/tmp/ainst/share/asymptote/GUI/xasy.py
\ No newline at end of file diff --git a/Master/bin/win32/asy.exe b/Master/bin/win32/asy.exe Binary files differnew file mode 100755 index 00000000000..cab0870d477 --- /dev/null +++ b/Master/bin/win32/asy.exe diff --git a/Master/bin/win32/cygwin1.dll b/Master/bin/win32/cygwin1.dll Binary files differnew file mode 100644 index 00000000000..0296b95d7fb --- /dev/null +++ b/Master/bin/win32/cygwin1.dll diff --git a/Master/bin/win32/freeglut.dll b/Master/bin/win32/freeglut.dll Binary files differnew file mode 100644 index 00000000000..ad642580b00 --- /dev/null +++ b/Master/bin/win32/freeglut.dll diff --git a/Master/bin/win32/glu32.dll b/Master/bin/win32/glu32.dll Binary files differnew file mode 100644 index 00000000000..a27605870f0 --- /dev/null +++ b/Master/bin/win32/glu32.dll diff --git a/Master/texmf/asymptote/CAD.asy b/Master/texmf/asymptote/CAD.asy new file mode 100644 index 00000000000..7c333318f5c --- /dev/null +++ b/Master/texmf/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("10 2.5");
+ // F
+ cad.pF =
+ cad.pInvisibleEdge =
+ cad.pInvisibleContour =
+ pHalfWidth + linetype("20 5");
+ // G
+ cad.pG =
+ cad.pMiddleLine =
+ cad.pSymmetryLine =
+ cad.pPartialCircle =
+ cad.pCircularHole =
+ cad.pDivisionPlane =
+ cad.pTransferLine =
+ pHalfWidth + linetype("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("20 2.5 2.5 2.5");
+ // K
+ cad.pK =
+ cad.pContourBeforeDeformation =
+ cad.pAdjacentPartContour =
+ cad.pEndShapeRawMaterial =
+ cad.pContourEligibleType =
+ cad.pPartInFrontOfCuttingPlane =
+ pHalfWidth + linetype("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/asymptote/GUI/CubicBezier.py b/Master/texmf/asymptote/GUI/CubicBezier.py new file mode 100755 index 00000000000..6455b700a79 --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/UndoRedoStack.py b/Master/texmf/asymptote/GUI/UndoRedoStack.py new file mode 100755 index 00000000000..f4a247a411d --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasy.py b/Master/texmf/asymptote/GUI/xasy.py new file mode 100755 index 00000000000..ffca16a1e2d --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasy2asy.py b/Master/texmf/asymptote/GUI/xasy2asy.py new file mode 100755 index 00000000000..23bd5a67c38 --- /dev/null +++ b/Master/texmf/asymptote/GUI/xasy2asy.py @@ -0,0 +1,785 @@ +#!/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 + +class asyProcessFailure(Exception): + """asy could not be invoked to execute a command because the process has failed.""" + pass + +idCounter = 0; +randString = 'wGd3I26kOcu4ZI4arZZMqoJufO2h1QE2D728f1Lai3aqeTQC9' + +quickAsyFailed = True +global AsyTempDir + +console=None + +def startQuickAsy(): + global quickAsy + global quickAsyFailed + global AsyTempDir + if quickAsyRunning(): + return + try: + quickAsy.stdin.close() + quickAsy.wait() + except: + pass + try: + quickAsyFailed = False + if os.name == "nt": + AsyTempDir=mkdtemp(prefix="asy_", dir="./") + else: + AsyTempDir=mkdtemp(prefix="asy_")+os.sep + quickAsy = Popen([xasyOptions.options['asyPath']]+ + split("-noV -multiline -interactive -o"+AsyTempDir), + stdin=PIPE,stdout=PIPE,stderr=STDOUT) + quickAsy.stdin.write("settings.xformat;\n") + 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() + syncQuickAsyOutput() + quickAsy.stdin.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() + +def syncQuickAsyOutput(verbose=False,queue=None): + global idCounter + idStr = randString+"-id "+str(idCounter) + idCounter += 1 + quickAsy.stdin.write("\nwrite(\""+idStr+"\");\n") + quickAsy.stdin.flush() + line = quickAsy.stdout.readline() + while not line.endswith(idStr+'\n'): + if verbose: + queue.put(("OUTPUT",line)) + line = quickAsy.stdout.readline() + quickAsy.stdin.flush() + +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((s*self.t[0],s*self.t[1],s*self.t[2],s*self.t[3],s*self.t[4],s*self.t[5])) + 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.""" + syncQuickAsyOutput() + quickAsy.stdin.write("pen p="+self.getCode()+';\n') + quickAsy.stdin.write("write(\";\n\");write(colorspace(p));\n") + quickAsy.stdin.write("write(colors(p));\n") + quickAsy.stdin.flush() + testline = quickAsy.stdout.readline() + if testline.startswith("> -:"): + raise Exception,"Invalid pen options" + quickAsy.stdout.readline() + colorspace = quickAsy.stdout.readline() + if colorspace.find("cmyk") != -1: + lines = quickAsy.stdout.readline()+quickAsy.stdout.readline()+quickAsy.stdout.readline()+quickAsy.stdout.readline() + parts = lines.split() + c,m,y,k = eval(parts[2]),eval(parts[4]),eval(parts[6]),eval(parts[8]) + k = 1-k + r,g,b = ((1-c)*k,(1-m)*k,(1-y)*k) + elif colorspace.find("rgb") != -1: + lines = quickAsy.stdout.readline()+quickAsy.stdout.readline()+quickAsy.stdout.readline() + parts = lines.split() + r,g,b = eval(parts[2]),eval(parts[4]),eval(parts[6]) + elif colorspace.find("gray") != -1: + lines = quickAsy.stdout.readline() + parts = lines.split() + r = g = b = eval(parts[2]) + 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""" + syncQuickAsyOutput() + quickAsy.stdin.write("path p="+self.getCode()+';\n') + quickAsy.stdin.write("write(length(p));\n") + quickAsy.stdin.write("write(unstraighten(p));write(\"\");\n") + quickAsy.stdin.flush() + lengthStr = quickAsy.stdout.readline() + pathSegments = eval(lengthStr.split()[-1]) + pathStrLines = [] + for i in range(pathSegments+1): + pathStrLines.append(quickAsy.stdout.readline()) + oneLiner = "".join(split(join(pathStrLines))) + oneLiner = oneLiner.replace(">","") + 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""" + quickAsy.stdin.write("\nreset;\n") + quickAsy.stdin.write("initXasyMode();\n") + quickAsy.stdin.write("atexit(null);\n") + syncQuickAsyOutput() + global console + for line in self.getCode().splitlines(): + quickAsy.stdin.write(line+"\n"); + quickAsy.stdin.flush() + syncQuickAsyOutput(verbose=True,queue=self.imageHandleQueue); + quickAsy.stdin.write("deconstruct(%f);\n"%mag) + quickAsy.stdin.flush() + format = split(quickAsy.stdout.readline())[1] + maxargs = int(split(quickAsy.stdout.readline())[0]) + boxes=[] + batch=0 + n=0 + text = quickAsy.stdout.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 = quickAsy.stdout.readline() + n += 1 + if n >= maxargs: + render() + boxes=[] + batch += 1 + n=0 + if text == "Error\n": + self.imageHandleQueue.put(("ERROR",quickAsy.stdout.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/asymptote/GUI/xasyActions.py b/Master/texmf/asymptote/GUI/xasyActions.py new file mode 100755 index 00000000000..38aae1d5c7e --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyBezierEditor.py b/Master/texmf/asymptote/GUI/xasyBezierEditor.py new file mode 100755 index 00000000000..998ee4c7c25 --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyCodeEditor.py b/Master/texmf/asymptote/GUI/xasyCodeEditor.py new file mode 100755 index 00000000000..93e06e447ee --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyColorPicker.py b/Master/texmf/asymptote/GUI/xasyColorPicker.py new file mode 100755 index 00000000000..7415be205c4 --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyFile.py b/Master/texmf/asymptote/GUI/xasyFile.py new file mode 100755 index 00000000000..890f1ad3eef --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyGUIIcons.py b/Master/texmf/asymptote/GUI/xasyGUIIcons.py new file mode 100755 index 00000000000..3afecfd349c --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyMainWin.py b/Master/texmf/asymptote/GUI/xasyMainWin.py new file mode 100755 index 00000000000..865e057accb --- /dev/null +++ b/Master/texmf/asymptote/GUI/xasyMainWin.py @@ -0,0 +1,1742 @@ +#!/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.6.win32-py2.5.exe" + if sys.version_info[0] != 2 or sys.version_info[1] != 5: + tkMessageBox.showerror("Failed Dependencies","Xasy requires Python 2.5 or 2.5.1. Please install http://www.python.org/ftp/python/2.5.1/python-2.5.1.msi") + self.parent.destroy() + sys.exit(1) + else: + site="http://effbot.org/downloads/Imaging-1.1.6.tar.gz after applying the patch http://asymptote.svn.sourceforge.net/viewvc/asymptote/trunk/asymptote/patches/TkAlpha-Imaging-1.1.6.patch" + 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.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 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) + dirname = os.path.dirname(fullname) + command = xasyOptions.options['asyPath']+" -f %s -o %s %s"%(outFormat,fullname,inFile) + saver = subprocess.Popen(split(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("\nhelp;\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/asymptote/GUI/xasyOptions.py b/Master/texmf/asymptote/GUI/xasyOptions.py new file mode 100755 index 00000000000..4406687cbf4 --- /dev/null +++ b/Master/texmf/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/asymptote/GUI/xasyOptionsDialog.py b/Master/texmf/asymptote/GUI/xasyOptionsDialog.py new file mode 100755 index 00000000000..b93b2a7fed9 --- /dev/null +++ b/Master/texmf/asymptote/GUI/xasyOptionsDialog.py @@ -0,0 +1,245 @@ +#!/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 subprocess +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 path + pathOK = True + try: + test = subprocess.Popen([self.ap.get(),"-interactive"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + test.stdin.close() + test.wait() + idString = test.stdout.read() + except: + pathOK = False + if not pathOK or not idString.startswith("Welcome to Asymptote"): + tkMessageBox.showerror("xasy Options","Specified file does not exist or is not an Asymptote executable.\r\n"+self.ap.get(),parent=self) + return False + + #validate the external editor + # how can I validate the editor? + + #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/asymptote/GUI/xasyVersion.py b/Master/texmf/asymptote/GUI/xasyVersion.py new file mode 100755 index 00000000000..d392ca51509 --- /dev/null +++ b/Master/texmf/asymptote/GUI/xasyVersion.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +xasyVersion = "1.75" diff --git a/Master/texmf/asymptote/animate.asy b/Master/texmf/asymptote/animate.asy new file mode 100644 index 00000000000..586e71041b6 --- /dev/null +++ b/Master/texmf/asymptote/animate.asy @@ -0,0 +1,3 @@ +usepackage("animate"); +import animation; + diff --git a/Master/texmf/asymptote/animation.asy b/Master/texmf/asymptote/animation.asy new file mode 100644 index 00000000000..2b7519c700a --- /dev/null +++ b/Master/texmf/asymptote/animation.asy @@ -0,0 +1,179 @@ +/***** + * 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 fit(string prefix="",picture); + +frame NoBox(string prefix="", picture pic) { + return pic.fit(prefix); +} + +fit BBox(string prefix="", real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill) { + return new frame(string prefix, picture pic) { + return bbox(prefix,pic,xmargin,ymargin,p,filltype); + }; +} + +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((prefix == "") ? outprefix() : stripdirectory(prefix), + " ","_"); + this.prefix=prefix; + this.global=global; + } + + string basename(string prefix=prefix) { + return "_"+stripextension(prefix); + } + + string name(string prefix, int index) { + return stripextension(prefix)+"+"+string(index); + } + + private string nextname() { + string name=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, fit fit=NoBox) { + if(global) { + pictures.push(pic.copy()); + } else this.shipout(nextname(),fit(prefix,pic)); + } + + 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; + } + + // Export all frames with the same scaling. + void export(string prefix=prefix, fit fit=NoBox, + bool multipage=false, bool view=false) { + if(pictures.length == 0) return; + rescale(pictures); + frame multi; + bool inlinetex=settings.inlinetex; + if(multipage) + settings.inlinetex=false; + for(int i=0; i < pictures.length; ++i) { + if(multipage) { + add(multi,fit(prefix,pictures[i])); + newpage(multi); + } else { + if(pictures[i].empty3() || settings.render <= 0) { + real render=settings.render; + settings.render=0; + this.shipout(name(prefix,i),fit(prefix,pictures[i])); + settings.render=render; + } else { // Render 3D frames + files.push(name(prefix,i)+"."+nativeformat()); + fit(prefix,pictures[i]); + } + } + } + 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) { + string s="\animategraphics["+options+"]{"+format("%.18f",1000/delay,"C")+ + "}{"+basename(); + if(!multipage) s += "+"; + s += "}{0}{"+string(frames-1)+"}"; + return s; + } + + string pdf(fit fit=NoBox, real delay=animationdelay, string options="", + bool keep=settings.keep, bool multipage=true) { + if(settings.inlinetex) multipage=true; + if(settings.tex != "pdflatex") + abort("inline pdf animations require -tex pdflatex"); + if(settings.outformat != "") settings.outformat="pdf"; + + string filename=basename(); + string pdfname=filename+".pdf"; + bool single=global && multipage; + + if(global) + export(filename,fit,multipage=multipage); + + shipped=false; + + if(!keep && !settings.inlinetex) { + exitfcn currentexitfunction=atexit(); + void exitfunction() { + if(currentexitfunction != null) currentexitfunction(); + this.purge(); + if(single) + delete(pdfname); + } + atexit(exitfunction); + } + + if(!single) + delete(pdfname); + + return load(pictures.length,delay,options,multipage); + } + + int movie(fit fit=NoBox, int loops=0, real delay=animationdelay, + string format=settings.outformat == "" ? "gif" : settings.outformat, + string options="", bool keep=settings.keep) { + if(global && format == "pdf") { + export(fit,multipage=true,view=true); + return 0; + } + + if(global) + export(fit); + return merge(loops,delay,format,options,keep); + } +} + +animation operator init() { + animation a=animation(); + return a; +} diff --git a/Master/texmf/asymptote/annotate.asy b/Master/texmf/asymptote/annotate.asy new file mode 100644 index 00000000000..aba9309cb65 --- /dev/null +++ b/Master/texmf/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/asymptote/asy-init.el b/Master/texmf/asymptote/asy-init.el new file mode 100644 index 00000000000..0e3f178a228 --- /dev/null +++ b/Master/texmf/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/asymptote/asy-keywords.el b/Master/texmf/asymptote/asy-keywords.el new file mode 100644 index 00000000000..e29bceb2937 --- /dev/null +++ b/Master/texmf/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 "1.75") + +(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 Label Legend Rmf 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 line linefit marginT marker mass object pair parabola path path3 pen picture point position projection real revolution scaleT scientific segment side slice 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 Ceil Circle CircleBarIntervalMarker Cos Courier CrossIntervalMarker 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 J 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 OutTicks Ox Oy Palatino PaletteTicks Pen PenMargin PenMargins Pentype Portrait RadialShade Rainbow Range Relative Right RightTicks Rotate Round Scale ScaleX ScaleY ScaleZ Seascape 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 Z ZX ZXgrid ZYgrid ZapfChancery ZapfDingbats _cputime _draw _eval _image _labelpath _strokepath _texpath aCos aSin aTan abort abs accel acos acosh acot acsc add addArrow addMargins addSaveFunction addnode addnodes addpenarc addpenline addseg 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 asec asin asinh ask assert asy asycode asydir asyfigure asyfilecode asyinclude asywrite atan atan2 atanh atbreakpoint atexit atime attach attract atupdate autoformat autoscale autoscale3 axes axes3 axialshade axis axiscoverage azimuth babel background bangles bar barmarksize barsize basealign baseline bbox beep beginclip begingroup beginpoint between bevel bezier bezierP bezierPP bezierPPP bezulate bibliography bibliographystyle binarytree binarytreeNode binomial binput bins bisector bisectorpoint bispline blend boutput box bqe breakpoint breakpoints brick buildRestoreDefaults buildRestoreThunk buildcycle bulletcolor canonical canonicalcartesiansystem cartesiansystem case1 case2 case3 cbrt cd ceil center centerToFocus centroid cevian change2 changecoordsys checkSegment checkconditionlength checker 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 containmentTree contains contour contour3 controlSpecifier convert coordinates coordsys copy cos cosh cot countIntersections cputime crop cropcode cross crossframe crosshatch crossmarksize csc csv cubicroots curabscissa curlSpecifier curpoint currentarrow currentexitfunction currentmomarrow currentpolarconicroutine curve cut cutafter cutbefore cyclic cylinder debugger deconstruct defaultdir defaultformat defaultpen defaultspline defined degenerate degrees delete deletepreamble determinant diagonal diamond diffdiv dimension dir dirSpecifier dirtime display distance divisors do_overpaint dot dotframe dotsize downcase draw drawAll drawDoubleLine drawFermion drawGhost drawGluon drawMomArrow drawPhoton drawScalar drawVertex drawVertexBox drawVertexBoxO drawVertexBoxX drawVertexO drawVertexOX drawVertexTriangle drawVertexTriangleO drawVertexX drawarrow drawarrow2 drawline drawtick duplicate elle ellipse ellipsenodesnumber embed embed3 empty enclose endScript endclip endgroup endl endpoint endpoints eof eol equation equations erase erf erfc error errorbar errorbars euler eval excenter excircle exit exitXasyMode exitfunction exp expi expm1 exradius extend extension extouch fabs factorial fermat fhorner figure file filecode fill filldraw filloutside fillrule filltype find finite firstcut firstframe fit 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 initXasyMode initdefaults input inradius insert inside interactive interior interp interpolate intersect intersection intersectionpoint intersectionpoints intersections intouch inverse inversion invisible is3D isDuplicate isogonal isogonalconjugate isotomic isotomicconjugate isparabola italic item 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 lift limits line linear linecap linejoin linemargin lineskip linetype linewidth link list locale locate locatefile location log log10 log1p logaxiscoverage longitude lookup magnetize makeNode makedraw makepen map margin markangle markangleradius markanglespace markarc marker markinterval marknodes markrightangle markuniform mass masscenter massformat math max max3 maxbound maxcoords maxlength maxratio maxtimes mean medial median midpoint min min3 minbound minipage minratio mintimes miterlimit momArrowPath momarrowsize monotonic nativeformat natural needshipout newl newpage newslide newton newtree nextframe nextnormal nextpage nib nodabscissa none normalvideo notaknot numberpage nurb object offset onpath opacity opposite orientation orig_circlenodesnumber orig_circlenodesnumber1 orig_draw orig_ellipsenodesnumber orig_ellipsenodesnumber1 orig_hyperbolanodesnumber orig_parabolanodesnumber origin orthic orthocentercenter outformat outline outprefix output overwrite pack pairs palette parabola parabolanodesnumber parallel partialsum path path3 pattern pause pdf pedal periodic perp perpendicular perpendicularmark phantom photon piecewisestraight point polar polarconicroutine polargraph polygon postcontrol postscript pow10 ppoint prc prc0 precision precontrol prepend print_random_addresses project projection purge pwhermite quadrant quadraticroots quantize quarticroots quotient radialshade radians radicalcenter radicalline radius rand randompath rd read1 read2 read3 readline realmult realquarticroots rectangle rectangular rectify reflect relabscissa relative relativedistance reldir relpoint reltime remainder remark removeDuplicates rename replace report rescale rescale2 resetdefaultpen restore restoredefaults reverse reversevideo rf rfind rgb rgba rgbint rmf rms rotate rotateO rotation round roundbox roundedpath roundrectangle samecoordsys sameside sample save savedefaults saveline scale scale3 scaleO scaleT scaleless scientific search searchtree sec secondaryX secondaryY seconds section sector seek seekeof segment sequence setpens sgn sgnd sharpangle sharpdegrees shift shiftless shipout shipout3 show side simeq simpson sin single sinh size size3 skewness skip slant sleep slope slopefield solve sort sourceline sphere split sqrt square srand standardizecoordsys startScript startTrembling stdev step stickframe stickmarksize stickmarkspace stop straight straightness string stripdirectory stripextension stripfile 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 textpath thick thin tick tickMax tickMax3 tickMin tickMin3 ticklabelshift ticklocate tildeframe tildemarksize tile tiling time times title titlepage topbox transform transformation transpose tremble trembleFuzz tremble_circlenodesnumber tremble_circlenodesnumber1 tremble_draw tremble_ellipsenodesnumber tremble_ellipsenodesnumber1 tremble_hyperbolanodesnumber tremble_marknodes tremble_markuniform tremble_parabolanodesnumber triangle triangleAbc triangleabc triangulate tricoef tridiagonal trilinear trim trueMagnetize truepoint tube uncycle unfill uniform unit unitrand unitsize unityroot unstraighten upcase updatefunction upscale uptodate usepackage usersetting usetypescript usleep value variance variancebiased vbox vector vectorfield verbatim view vline vprojection windingnumber word write xaxis xaxis3 xaxis3At xaxisAt xequals xinput xlimits xoutput 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 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 Draw E ENE EPS ESE Emerald EndPoint 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 Label Lavender LavenderBlush LawnGreen 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 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 PaleGoldenrod PaleGreen PaleTurquoise PaleVioletRed PapayaWhip Peach PeachPuff Periwinkle Peru PineGreen Pink Plum PowderBlue ProcessBlue Purple RawSienna Red RedOrange RedViolet Rhodamine 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 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 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 defaultS defaultbackpen defaultcoordsys defaultfilename defaultformat defaultmassformat defaultpen diagnostics differentlengths dot dotfactor dotframe dotted doublelinepen doublelinespacing down duplicateFuzz ellipsenodesnumberfactor eps epsgeo epsilon evenodd extendcap fermionpen figureborder figuremattpen firstnode firststep foregroundcolor fuchsia fuzz gapfactor ghostpen gluonamplitude gluonpen gluonratio gray green grey hatchepsilon havepagenumber heavyblue heavycyan heavygray heavygreen heavygrey heavymagenta heavyred hline hwratio hyperbolanodesnumberfactor identity4 ignore inXasyMode inch inches includegraphicscommand incommensurate inf infinity institutionpen intMax intMin invert invisible itempen itemskip itemstep labelmargin landscape lastnode left legendhskip legendlinelength legendmargin legendmarkersize legendmaxrelativewidth legendvskip lightblue lightcyan lightgray lightgreen lightgrey lightmagenta lightolive lightred lightyellow linemargin longdashdotted longdashed magenta magneticPoints 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 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 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 stepfraction steppagenumberpen stepping stickframe stickmarksizefactor stickmarkspacefactor textpen ticksize tildeframe tildemarksizefactor tinv titlealign titlepagepen titlepageposition titlepen titleskip top trailingzero treeLevelStep treeMinNodeWidth treeNodeStep trembleAngle trembleFrequency trembleRandom tremblingMode undefined unitcircle unitsquare up urlpen urlskip version vertexpen vertexsize viewportsize vline white wye xformStack yellow ylabelwidth zerotickfuzz zerowinding )) diff --git a/Master/texmf/asymptote/asy-mode.el b/Master/texmf/asymptote/asy-mode.el new file mode 100644 index 00000000000..ac31d8555eb --- /dev/null +++ b/Master/texmf/asymptote/asy-mode.el @@ -0,0 +1,1545 @@ +;;; asy-mode.el + +;; Copyright (C) 2006-8 +;; Author: Philippe IVALDI 20 August 2006 +;; Modified by: John Bowman +;; Last modification: 05 December 2008 (P. Ivaldi) +;; +;; 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.5") + +;;;###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) + (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)) + )))) + + ;; 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)) + (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} passing by 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-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)) + (let* + ((b-b-n (if Filename Filename (file-name-sans-extension buffer-file-name))) + (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 (or (file-newer-than-file-p + (concat b-b-n ".tex") + (concat b-b-n ".ps")) + Force) + (progn + (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 (file-name-sans-extension buffer-file-name))) + (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 (or (file-newer-than-file-p + (concat b-b-n ".tex") + (concat b-b-n ".pdf")) + Force) + (progn + (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 (file-name-sans-extension buffer-file-name))) + (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 (or (file-newer-than-file-p + (concat b-b-n ".tex") + (concat b-b-n ".pdf")) + Force) + (progn + (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/asymptote/asy.vim b/Master/texmf/asymptote/asy.vim new file mode 100644 index 00000000000..4cb897f1316 --- /dev/null +++ b/Master/texmf/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/asymptote/asymptote.py b/Master/texmf/asymptote/asymptote.py new file mode 100644 index 00000000000..721c464d36b --- /dev/null +++ b/Master/texmf/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/asymptote/babel.asy b/Master/texmf/asymptote/babel.asy new file mode 100644 index 00000000000..e93dec2a5cb --- /dev/null +++ b/Master/texmf/asymptote/babel.asy @@ -0,0 +1,4 @@ +void babel(string s) +{ + usepackage("babel",s); +} diff --git a/Master/texmf/asymptote/bezulate.asy b/Master/texmf/asymptote/bezulate.asy new file mode 100644 index 00000000000..01b3024f7b6 --- /dev/null +++ b/Master/texmf/asymptote/bezulate.asy @@ -0,0 +1,261 @@ +// Bezier triangulation routines written by Orest Shardt, 2008. + +private real fuzz=1e-6; +real duplicateFuzz=1e-3; // Work around font errors. + +int countIntersections(path[] p, pair start, pair end) +{ + int intersects=0; + for(path q : p) + intersects += intersections(q,start--end,fuzz).length; + return intersects; +} + +path[][] containmentTree(path[] paths) +{ + path[][] result; + for(int i=0; i < paths.length; ++i) { + bool classified=false; + // check if current curve contains or is contained in a group of curves + for(int j=0; !classified && j < result.length; ++j) + { + int test = inside(paths[i],result[j][0],zerowinding); + if(test == 1) // current curve contains group's toplevel curve + { + // replace toplevel curve with current curve + result[j].insert(0,paths[i]); + classified = true; + } + else if(test == -1) // current curve contained in group's toplevel curve + { + result[j].push(paths[i]); + classified = true; + } + } + // create a new group if this curve does not belong to another group + if(!classified) + result.push(new path[] {paths[i]}); + } + + // sort group so that later paths in the array are contained in previous paths + bool comparepaths(path i, path j) {return inside(i,j,zerowinding)==1;} + for(int i=0; i < result.length; ++i) + result[i] = sort(result[i],comparepaths); + + 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 + path line = start--start+d*direction; + real[][] ints=intersections(line,outer,fuzz); + assert(ints.length != 0); + real endtime=ints[0][1]; // endtime is time on outer + pair end = point(outer,endtime); + line = start--end; + path rline = reverse(line); + + // find first intersection of rline segment 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(rline,inners[j],fuzz); + if(ints.length > 0 && ints[0][0] < earliestTime) { + earliestTime=ints[0][0]; // time on rline + starttime=ints[0][1]; // time on inner curve + curveIndex=j; + } + } + start=point(inners[curveIndex],starttime); + line = start--end; + + 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); + } +} + +int countIntersections(path g, pair p, pair q) +{ + int ints=0; + int l=length(g); + for(int i=1; i <= l; ++i) + ints += intersections(subpath(g,i-1,i),p--q,fuzz).length; + return ints; +} + +bool checkSegment(path g, pair p, pair q) +{ + pair mid=0.5*(p+q); + return countIntersections(g,p,q) == 4 && 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) { + write("warning: too many subdivisions"); + } else { + p=subdivide(p); + i=-1; + } + } + } + } + } + if(length(p) <= 4) + patch.append(p); + } + return patch; +} diff --git a/Master/texmf/asymptote/binarytree.asy b/Master/texmf/asymptote/binarytree.asy new file mode 100644 index 00000000000..7bbd1de2148 --- /dev/null +++ b/Master/texmf/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,arr,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/asymptote/bsp.asy b/Master/texmf/asymptote/bsp.asy new file mode 100644 index 00000000000..a744da522cc --- /dev/null +++ b/Master/texmf/asymptote/bsp.asy @@ -0,0 +1,207 @@ +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.userBox(F.userMin,F.userMax); + pic.append(pic.bounds.point,pic.bounds.min,pic.bounds.max,F.T,F.bounds); + } +} diff --git a/Master/texmf/asymptote/contour.asy b/Master/texmf/asymptote/contour.asy new file mode 100644 index 00000000000..fbb4cd1c71a --- /dev/null +++ b/Master/texmf/asymptote/contour.asy @@ -0,0 +1,682 @@ +// Contour routines written by Radoslav Marinov and John Bowman. + +import graph_settings; + +real eps=10000*realEpsilon; + +// 1 +// 6 +-------------------+ 5 +// | \ / | +// | \ / | +// | \ / | +// | \ / | +// 2 | X | 0 +// | / \ | +// | / \ | +// | / \ | +// | / \ | +// 7 +-------------------+ 4 or 8 +// 3 + +private struct segment +{ + bool active; + pair a,b; // Endpoints; a is always an edge point if one exists. + int c; // Contour value. + int edge; // -1: interior, 0 to 3: edge, + // 4-8: single-vertex edge, 9: double-vertex edge. +} + +// Case 1: line passes through two vertices of a triangle +private segment case1(pair p0, pair p1, int edge) +{ + // Will cause a duplicate guide; luckily case1 is rare + segment rtrn; + rtrn.active=true; + rtrn.a=p0; + rtrn.b=p1; + rtrn.edge=edge; + return rtrn; +} + +// Case 2: 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, int edge) +{ + segment rtrn; + pair val=interp(p1,p2,abs(v1/(v2-v1))); + rtrn.active=true; + if(edge < 4) { + rtrn.a=val; + rtrn.b=p0; + } else { + rtrn.a=p0; + rtrn.b=val; + } + rtrn.edge=edge; + return rtrn; +} + +// Case 3: line passes through two sides of a triangle +// (through the sides formed by the first & second, and second & third +// vertices) +private segment case3(pair p0, pair p1, pair p2, + real v0, real v1, real v2, int edge=-1) +{ + segment rtrn; + rtrn.active=true; + rtrn.a=interp(p1,p0,abs(v1/(v0-v1))); + rtrn.b=interp(p1,p2,abs(v1/(v2-v1))); + rtrn.edge=edge; + 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, int edge=-1) +{ + // default null return + static segment dflt; + + real eps=eps*max(abs(v0),abs(v1),abs(v2)); + + 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 case3(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,5+edge); + else return case2(p1,p0,p2,v1,v0,v2,5+edge); + } else { + if(v2 < -eps) return case3(p0,p1,p2,v0,v1,v2,edge); + else if(v2 <= eps) + return case2(p2,p0,p1,v2,v0,v1,edge); + else return case3(p1,p0,p2,v1,v0,v2,edge); + } + } else if(v0 <= eps) { + if(v1 < -eps) { + if(v2 < -eps) return dflt; // nothing to do + else if(v2 <= eps) return case1(p0,p2,4+edge); + else return case2(p0,p1,p2,v0,v1,v2,4+edge); + } else if(v1 <= eps) { + if(v2 < -eps) return case1(p0,p1,9); + else if(v2 <= eps) return dflt; // use finer partitioning. + else return case1(p0,p1,9); + } else { + if(v2 < -eps) return case2(p0,p1,p2,v0,v1,v2,4+edge); + else if(v2 <= eps) return case1(p0,p2,4+edge); + else return dflt; // nothing to do + } + } else { + if(v1 < -eps) { + if(v2 < -eps) return case3(p1,p0,p2,v1,v0,v2,edge); + else if(v2 <= eps) + return case2(p2,p0,p1,v2,v0,v1,edge); + else return case3(p0,p1,p2,v0,v1,v2,edge); + } else if(v1 <= eps) { + if(v2 < -eps) return case2(p1,p0,p2,v1,v0,v2,5+edge); + else if(v2 <= eps) return case1(p1,p2,5+edge); + else return dflt; // nothing to do + } else { + if(v2 < -eps) return case3(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) +{ + // use to reverse an array, omitting the first point + int[] reverseF(int n) {return sequence(new int(int x){return n-1-x;},n-1);} + // use to reverse an array, omitting the last point + int[] reverseL(int n) {return sequence(new int(int x){return n-2-x;},n-1);} + + 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[0]) < eps) { + gdscnt[j]=gjg[reverseF(Lj)]; + gdscnt[j].append(gig); + gdscnt.delete(i); + --i; + break; + } else 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; + } else if(abs(gig[Li-1]-gjg[Lj-1]) < eps) { + gig.append(gjg[reverseL(Lj)]); + 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; +} + + +// 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 +// midpoint: optional array containing values of f at cell midpoints +// c: array of contour values +// join: interpolation operator (e.g. operator -- or operator ..) +guide[][] contour(pair[][] z, real[][] f, + real[][] midpoint=new real[][], real[] c, + interpolate join=operator --) +{ + int nx=z.length-1; + if(nx == 0) + abort("array z must have length >= 2"); + int ny=z[0].length-1; + if(ny == 0) + abort("array z[0] must have length >= 2"); + + c=sort(c); + bool midpoints=midpoint.length > 0; + + segment segments[][][]=new segment[nx][ny][]; + + // go over region a rectangle at a time + for(int i=0; i < nx; ++i) { + pair[] zi=z[i]; + pair[] zp=z[i+1]; + real[] fi=f[i]; + real[] fp=f[i+1]; + real[] midpointi; + if(midpoints) midpointi=midpoint[i]; + segment[][] segmentsi=segments[i]; + for(int j=0; j < ny; ++j) { + segment[] segmentsij=segmentsi[j]; + + // define points + pair bleft=zi[j]; + pair bright=zp[j]; + pair tleft=zi[j+1]; + pair tright=zp[j+1]; + pair middle=0.25*(bleft+bright+tleft+tright); + + real f00=fi[j]; + real f01=fi[j+1]; + real f10=fp[j]; + real f11=fp[j+1]; + real fmm=midpoints ? midpoint[i][j] : 0.25*(f00+f01+f10+f11); + + // optimization: we make sure we don't work with empty rectangles + int checkcell(int cnt) { + real C=c[cnt]; + real vertdat0=f00-C; // bottom-left vertex + real vertdat1=f10-C; // bottom-right vertex + real vertdat2=f01-C; // top-left vertex + real vertdat3=f11-C; // top-right vertex + + // optimization: we make sure we don't work with empty rectangles + 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(vertdat0); + check(vertdat1); + check(vertdat2); + check(vertdat3); + + 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; + + // go through the triangles + + void addseg(segment seg) { + if(seg.active) { + seg.c=cnt; + segmentsij.push(seg); + } + } + real vertdat4=fmm-C; + addseg(checktriangle(bright,tright,middle, + vertdat1,vertdat3,vertdat4,0)); + addseg(checktriangle(tright,tleft,middle, + vertdat3,vertdat2,vertdat4,1)); + addseg(checktriangle(tleft,bleft,middle, + vertdat2,vertdat0,vertdat4,2)); + addseg(checktriangle(bleft,bright,middle, + vertdat0,vertdat1,vertdat4,3)); + 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); + } + } + + // set up return value + pair[][][] points=new pair[c.length][][]; + + for(int i=0; i < nx; ++i) { + segment[][] segmentsi=segments[i]; + for(int j=0; j < ny; ++j) { + segment[] segmentsij=segmentsi[j]; + for(int k=0; k < segmentsij.length; ++k) { + segment C=segmentsij[k]; + + if(!C.active) continue; + + pair[] g=new pair[] {C.a,C.b}; + segmentsij[k].active=false; + + int forward(int I, int J, bool first=true) { + if(I >= 0 && I < nx && J >= 0 && J < ny) { + segment[] segmentsIJ=segments[I][J]; + for(int l=0; l < segmentsIJ.length; ++l) { + segment D=segmentsIJ[l]; + if(!D.active) continue; + if(abs(D.a-g[g.length-1]) < eps) { + g.push(D.b); + segmentsIJ[l].active=false; + if(D.edge >= 0 && !first) return D.edge; + first=false; + l=-1; + } else if(abs(D.b-g[g.length-1]) < eps) { + g.push(D.a); + segmentsIJ[l].active=false; + if(D.edge >= 0 && !first) return D.edge; + first=false; + l=-1; + } + } + } + return -1; + } + + int backward(int I, int J, bool first=true) { + if(I >= 0 && I < nx && J >= 0 && J < ny) { + segment[] segmentsIJ=segments[I][J]; + for(int l=0; l < segmentsIJ.length; ++l) { + segment D=segmentsIJ[l]; + if(!D.active) continue; + if(abs(D.a-g[0]) < eps) { + g.insert(0,D.b); + segmentsIJ[l].active=false; + if(D.edge >= 0 && !first) return D.edge; + first=false; + l=-1; + } else if(abs(D.b-g[0]) < eps) { + g.insert(0,D.a); + segmentsIJ[l].active=false; + if(D.edge >= 0 && !first) return D.edge; + first=false; + l=-1; + } + } + } + return -1; + } + + void follow(int f(int, int, bool first=true), int edge) { + int I=i; + int J=j; + while(true) { + static int ix[]={1,0,-1,0}; + static int iy[]={0,1,0,-1}; + if(edge >= 0 && edge < 4) { + I += ix[edge]; + J += iy[edge]; + edge=f(I,J); + } else { + if(edge == -1) break; + if(edge < 9) { + int edge0=(edge-5) % 4; + int edge1=(edge-4) % 4; + int ix0=ix[edge0]; + int iy0=iy[edge0]; + I += ix0; + J += iy0; + // Search all 3 corner cells + if((edge=f(I,J)) == -1) { + I += ix[edge1]; + J += iy[edge1]; + if((edge=f(I,J)) == -1) { + I -= ix0; + J -= iy0; + edge=f(I,J); + } + } + } else { + // Double-vertex edge: search all 8 surrounding cells + void search() { + for(int i=-1; i <= 1; ++i) { + for(int j=-1; j <= 1; ++j) { + if((edge=f(I+i,J+j,false)) >= 0) { + I += i; + J += j; + return; + } + } + } + } + search(); + } + } + } + } + + // Follow contour in cell + int edge=forward(i,j,first=false); + + // Follow contour forward outside of cell + follow(forward,edge); + + // Follow contour backward outside of cell + follow(backward,C.edge); + + points[C.c].push(g); + } + } + } + + collect(points,c); // Required to join remaining case1 cycles. + + return connect(points,c,join); +} + +// Return contour guides for a 2D data array on a uniform lattice +// f: two-dimensional array of real data values +// midpoint: optional array containing data values at cell midpoints +// a,b: diagonally opposite vertices of rectangular domain +// c: array of contour values +// join: interpolation operator (e.g. operator -- or operator ..) +guide[][] contour(real[][] f, real[][] midpoint=new real[][], + pair a, pair b, real[] c, + interpolate join=operator --) +{ + 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,midpoint,c,join); +} + +// 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 ..) +guide[][] contour(real f(real, real), pair a, pair b, + real[] c, int nx=ngraph, int ny=nx, + interpolate join=operator --) +{ + // evaluate function at points and midpoints + real[][] dat=new real[nx+1][ny+1]; + real[][] midpoint=new real[nx+1][ny+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[] midpointi=midpoint[i]; + for(int j=0; j <= ny; ++j) { + dati[j]=f(x,interp(a.y,b.y,j/ny)); + midpointi[j]=f(x2,interp(a.y,b.y,(j+0.5)/ny)); + } + } + + return contour(dat,midpoint,a,b,c,join); +} + +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)); +} + +// 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); + } + } + } +} + +// routines for irregularly spaced points: + +// check existing guides and adds new segment to them if possible, +// or otherwise store segment as a new guide +private void addseg(pair[][] gds, segment seg) +{ + if(!seg.active) return; + // search for a path to extend + for(int i=0; i < gds.length; ++i) { + pair[] gd=gds[i]; + if(abs(gd[0]-seg.b) < eps) { + gd.insert(0,seg.a); + return; + } else if(abs(gd[gd.length-1]-seg.b) < eps) { + gd.push(seg.a); + return; + } else if(abs(gd[0]-seg.a) < eps) { + gd.insert(0,seg.b); + return; + } else if(abs(gd[gd.length-1]-seg.a) < eps) { + gd.push(seg.b); + return; + } + } + + // in case nothing is found + pair[] segm; + segm=new pair[] {seg.a,seg.b}; + gds.push(segm); + + return; +} + +guide[][] contour(real f(pair), pair a, pair b, + real[] c, int nx=ngraph, int ny=nx, + interpolate join=operator --) +{ + return contour(new real(real x, real y) {return f((x,y));},a,b,c,nx,ny,join); +} + +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]; + addseg(pointscnt,checktriangle(z[i0],z[i1],z[i2], + f[i0]-C,f[i1]-C,f[i2]-C)); + } + } + + collect(points,c); + + return connect(points,c,join); +} diff --git a/Master/texmf/asymptote/contour3.asy b/Master/texmf/asymptote/contour3.asy new file mode 100644 index 00000000000..e9d8d928991 --- /dev/null +++ b/Master/texmf/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.vector(); + + 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/asymptote/drawtree.asy b/Master/texmf/asymptote/drawtree.asy new file mode 100644 index 00000000000..832dfc658a1 --- /dev/null +++ b/Master/texmf/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/asymptote/embed.asy b/Master/texmf/asymptote/embed.asy new file mode 100644 index 00000000000..5695eadab56 --- /dev/null +++ b/Master/texmf/asymptote/embed.asy @@ -0,0 +1,36 @@ +if(latex()) { + texpreamble(" +\ifx\pdfhorigin\undefined% +\usepackage[3D,dvipdfmx]{movie15} +\else% +\usepackage[3D]{movie15} +\fi% +"); + usepackage("hyperref","setpagesize=false,unicode"); +} + +// 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/asymptote/external.asy b/Master/texmf/asymptote/external.asy new file mode 100644 index 00000000000..5573561fc36 --- /dev/null +++ b/Master/texmf/asymptote/external.asy @@ -0,0 +1,36 @@ +usepackage("hyperref","setpagesize=false,unicode"); + +// 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/asymptote/feynman.asy b/Master/texmf/asymptote/feynman.asy new file mode 100644 index 00000000000..4360ee9f524 --- /dev/null +++ b/Master/texmf/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/asymptote/flowchart.asy b/Master/texmf/asymptote/flowchart.asy new file mode 100644 index 00000000000..5fe96ae8508 --- /dev/null +++ b/Master/texmf/asymptote/flowchart.asy @@ -0,0 +1,356 @@ +// 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; + +struct block { + // The absolute center of the block in user coordinates. + pair center; + + // The relative center of the block. + pair f_center; + + // The size of the block + pair size; + + // Returns the relative position along the boundary of the block. + pair f_position(real x); + + pair shift(transform t=identity()) { + return t*center-f_center; + } + + // 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 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; + + // 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); +}; + +// 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 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(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)); +} diff --git a/Master/texmf/asymptote/fontsize.asy b/Master/texmf/asymptote/fontsize.asy new file mode 100644 index 00000000000..0c0c6a0dcca --- /dev/null +++ b/Master/texmf/asymptote/fontsize.asy @@ -0,0 +1 @@ +usepackage("fix-cm"); diff --git a/Master/texmf/asymptote/geometry.asy b/Master/texmf/asymptote/geometry.asy new file mode 100644 index 00000000000..12e35339dcc --- /dev/null +++ b/Master/texmf/asymptote/geometry.asy @@ -0,0 +1,7153 @@ +// geometry.asy + +// Copyright (C) 2007 +// Author: Philippe IVALDI 2007/09/01 + +// 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; +// *=======================================================* +// *........................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......................* +// Copyright (c) 2007, Philippe Ivaldi. +// Version: $Id: coordinates.asy,v 0.0 2007/02/03 16:06:23 Philippe Ivaldi Exp$ +// Last modified: Wed Aug 15 15:53:01 CEST 2007 + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// 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 +// General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +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; +} +point point(explicit pair p, int m) +{// Handle ambiguity + return point(p,(real)m); +} + +/*<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) + write("Warning, the coordinate system of two objects are not the same. +The operation will be done relatively 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 'maths'. + 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, 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. + pair z=t*P; + pair q=t*Q; + pair v=t*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,(z.x,m.y)) < 0) { + ptp=(z.x,z.y+cp*(m.y-z.y)); + ptq=(z.x,q.y+cq*(M.y-q.y)); + } else { + ptp=(z.x,q.y+cq*(m.y-q.y)); + ptq=(z.x,z.y+cp*(M.y-z.y)); + } + } else if(v.y == 0) { + if (dot(v,(m.x,z.y)) < 0) { + ptp=(z.x+cp*(m.x-z.x),z.y); + ptq=(q.x+cq*(M.x-q.x),z.y); + } else { + ptp=(q.x+cq*(m.x-q.x),z.y); + ptq=(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) == ""){ + 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, 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*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,line,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, +// line l1, 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,line,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, +// line l1, 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, + line l1, 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) + write("Warning, 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) write("Warning: variable 'circlenodesnumberfactor' maybe 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("Warning: 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(explicit pair,real,real)"><code></asyxml>*/ +ellipse ellipse(explicit pair C, real a, real b)= + new ellipse(explicit pair C, real a, real b) +{/*<asyxml></code><documentation>Overwrite the default routine.</documentation></function></asyxml>*/ + return ellipse((point)C,a,b,0); +}; + +/*<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; +} +circle circle(explicit point C, int r) +{ + return circle(C,(real) r); +} + +/*<asyxml><function type="circle" signature="circle(pair,real)"><code></asyxml>*/ +circle circle(pair c, real r)=new circle(pair c, real r) +{/*<asyxml></code><documentation>Overwrite 'circle(pair,real)'</documentation></function></asyxml>*/ + return circle(locate(c),r); +}; + +/*<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)); + return parabola(P[0],P[1],P[2],t*p.D); +} + +/*<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>*/ + 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]) return hyperbola(oe.F2,oe.F1,oe.a); + 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."); + guide op; + coordsys Rp=coordsys(el); + if (n < 1) return op; + if (angle1 > angle2) + return reverse(arcfromcenter(el,angle2,angle1,!direction,n)); + 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=op..Rp*Rp.polar(r,a+da); + } + 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, 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; + draw(tmp,L,t*(path) parabola,align,p,arrow,bar,NoMargin,legend,marker); + add(f,tmp.fit()); + }); + if(pic.userMin.x != pic.userMax.x & pic.userMin.y != pic.userMax.y & + !finite(abs(pic.userMin)) & !finite(abs(pic.userMax))) + 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, 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; + picture tmp; + draw(tmp,L,t*(path) h,align,p,arrow,bar,NoMargin,legend,marker); + hyperbola ht=hyperbola(h.F2,h.F1,h.a); + ht.bmin=inverse(t)*m; ht.bmax=inverse(t)*M; + draw(tmp,"",t*(path) ht,align,p,arrow,bar,NoMargin,marker); + add(f,tmp.fit()); + }); + if(pic.userMin.x != pic.userMax.x & pic.userMin.y != pic.userMax.y & + !finite(abs(pic.userMin)) & !finite(abs(pic.userMax))) + 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; +} +abscissa operator +(explicit abscissa a, int x) +{ + 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; +} +abscissa operator -(explicit abscissa a, int x) +{ + return a-((real)x); +} + +/*<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 *(int x, explicit abscissa a) +{ + return ((real)x)*a; +} +abscissa operator *(explicit abscissa a, int x) +{ + return ((real)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; +} +abscissa operator /(explicit abscissa a, int x) +{ + return a/((real)x); +} + +/*<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>*/ + write("PASS"); + 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>*/ + 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)); + draw(pic,La,b--c, align=rotate(dot(a-mBC,aligna) > 0 ? 180 :0)*aligna,p); + draw(pic,Lb,a--c, align=rotate(dot(b-mAC,alignb) > 0 ? 180 :0)*alignb,p); + draw(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); +} + +/*<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) { + write("Warning: the inversion of the line is not a circle."); + write("The returned circle has an infinite radius, cirlce.l have been set."); + 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) { + write("Warning: the inversion of the circle is not a circle."); + write("The returned circle has an infinite radius, cirlce.l have been set."); + 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); + arc oa=arc(tc,a,b); + 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,b,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=arrowfactor) +{ + return arc(A,r,degrees(B-A),degrees(C-A)); +} + +// *.......End of compatibility routines........* +// *=======================================================* + +// *........................FOOTER.........................* +// *=======================================================* diff --git a/Master/texmf/asymptote/graph.asy b/Master/texmf/asymptote/graph.asy new file mode 100644 index 00000000000..805cf15f612 --- /dev/null +++ b/Master/texmf/asymptote/graph.asy @@ -0,0 +1,2214 @@ +private import math; + +import 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-1; ++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-1; ++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; + + real tickmin=finite(locate.S.tickMin) && (Step == 0 || locate.S.automin) ? + locate.S.Tinv(locate.S.tickMin) : a; + real tickmax=finite(locate.S.tickMax) && (Step == 0 || 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(Step == 0 && N == 0) { + N=1; + if(divisor.length > 0) { + bool autoscale=locate.S.automin && locate.S.automax; + real h=0.5*(b-a); + 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 OmitTick(... real[] x) { + return new tickvalues(tickvalues v) { + if(v.major.length == 0) return v; + real norm=max(abs(v.major)); + for(int i=0; i < x.length; ++i) { + int j=find(abs(v.major-x[i]) < zerotickfuzz*norm); + if(j >= 0) v.major.delete(j); + } + return v; + }; +} + +tickmodifier NoZero=OmitTick(0); + +// Tickmodifier that removes all major ticks in the interval [a,b]. +tickmodifier Break(real a, real b) { + return new tickvalues(tickvalues v) { + real[] V=v.major; + real[] major; + for(int i=0; i < V.length; ++i) + if(V[i] < a-epsilon || V[i] > b+epsilon) major.push(V[i]); + v.major=major; + return v; + }; +} + +// 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 == 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; + else 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); + } + + 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 == 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; + else 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); + } + + 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(pic.scale.x.T(min)); + + if(pic.scale.x.automax) { + if(pic.scale.x.automax()) pic.userMaxx(mx.max); + } else pic.userMaxx(pic.scale.x.T(max)); + + if(crop) { + pair userMin=pic.userMin(); + pair userMax=pic.userMax(); + pic.bounds.xclip(userMin.x,userMax.x); + pic.clip(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(pic.scale.y.T(min)); + + if(pic.scale.y.automax) { + if(pic.scale.y.automax()) pic.userMaxy(my.max); + } else pic.userMaxy(pic.scale.y.T(max)); + + if(crop) { + pair userMin=pic.userMin(); + pair userMax=pic.userMax(); + pic.bounds.yclip(userMin.y,userMax.y); + pic.clip(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) +{ + 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(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) +{ + 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,Scale(pic,z),dir,size,p); +} + +void xtick(picture pic=currentpicture, real x, pair dir=N, + real size=Ticksize, pen p=currentpen) +{ + xtick(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) +{ + xtick(pic,z,dir,size,p); +} + +void ytick(picture pic=currentpicture, real y, pair dir=E, + real size=Ticksize, pen p=currentpen) +{ + xtick(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(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); + xtick(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) +{ + ytick(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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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(defaultspline); + +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 differentlengths="attempt to graph arrays of different lengths"; +string conditionlength="condition array has different length than data"; + +void checklengths(int x, int y, string text=differentlengths) +{ + if(x != y) + abort(text+": "+string(x)+" != "+string(y)); +} + +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); +} + +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((0,0)); + 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(angle1 >= angle2 && direction) angle1 -= 2pi; + if(angle2 >= angle1 && !direction) 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/asymptote/graph3.asy b/Master/texmf/asymptote/graph3.asy new file mode 100644 index 00000000000..a1c8258638f --- /dev/null +++ b/Master/texmf/asymptote/graph3.asy @@ -0,0 +1,1942 @@ +// 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); +} + +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.vector()); + 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)); + 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); + } + + begingroup3(pic); + 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); + } + endgroup3(pic); + + 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(L.s != "" && primary) + labelaxis(pic,t,L,G,locate,Sign,ticklabels); + }; +} + +// 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.vector(),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); + triple a2=xmin == -infinity ? tinv*(lb.x-min3(p).x,ytrans(t,y2), + ztrans(t,z2)) : (xmin,y2,z2); + triple b2=xmax == infinity ? tinv*(rt.x-max3(p).x,ytrans(t,y2), + ztrans(t,z2)) : (xmax,y2,z2); + + 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(y2) ? 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); + triple a2=ymin == -infinity ? tinv*(xtrans(t,x2),lb.y-min3(p).y, + ztrans(t,z2)) : (x2,ymin,z2); + triple b2=ymax == infinity ? tinv*(xtrans(t,x2),rt.y-max3(p).y, + ztrans(t,z2)) : (x2,ymax,z2); + + 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(x2) ? 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); + triple a2=zmin == -infinity ? tinv*(xtrans(t,x2),ytrans(t,y2), + lb.z-min3(p).z) : (x2,y2,zmin); + triple b2=zmax == infinity ? tinv*(xtrans(t,x2),ytrans(t,y2), + rt.z-max3(p).z) : (x2,y2,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(x2) ? 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.vector(); + 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.vector(); + 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(pic.scale.z.T(min)); + + if(pic.scale.z.automax) { + if(pic.scale.z.automax()) pic.userMaxz(mz.max); + } else pic.userMaxz(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) +{ + 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,Scale(pic,v),dir,size,p); +} + +void xtick3(picture pic=currentpicture, real x, triple dir=Y, + real size=Ticksize, pen p=currentpen) +{ + xtick(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) +{ + xtick(pic,v,dir,size,p); +} + +void ytick(picture pic=currentpicture, real y, triple dir=X, + real size=Ticksize, pen p=currentpen) +{ + xtick(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 ztick(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,v); + xtick(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) +{ + ytick(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) +{ + ztick(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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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=nullpen) +{ + 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); +} + +// 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); + 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]; + 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]}); + } + } + return s; +} + +private 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 g=surface(count); + int k=-1; + for(int i=0; i < n; ++i) { + bool[] condi=all ? null : cond[i]; + real xi=x[i]; + real[] zi=z[i]; + real[] zp=z[i+1]; + real[] ri=r[i]; + real[] rp=r[i+1]; + real[] pi=p[i]; + real[] pp=p[i+1]; + real[] qi=q[i]; + real[] qp=q[i+1]; + real xp=x[i+1]; + real hx=(xp-xi)/3; + for(int j=0; j < m; ++j) { + real yj=y[j]; + real yp=y[j+1]; + if(all || condi[j]) { + triple[][] P={ + {O,O,O,O}, + {O,O,O,O}, + {O,O,O,O}, + {O,O,O,O}}; + real hy=(yp-yj)/3; + real hxy=hx*hy; + // first x and y directions + for(int k=0 ; k < 4 ; ++k) { + P[k][0] += xi*X; + P[0][k] += yj*Y; + P[k][1] += (xp+2*xi)/3*X; + P[1][k] += (yp+2*yj)/3*Y; + P[k][2] += (2*xp+xi)/3*X; + P[2][k] += (2*yp+yj)/3*Y; + P[k][3] += xp*X; + P[3][k] += yp*Y; + } + // now z, first the value + P[0][0] += zi[j]*Z; + P[0][3] += zp[j]*Z; + P[3][0] += zi[j+1]*Z; + P[3][3] += zp[j+1]*Z; + // 2nd, first derivative + P[0][1] += (P[0][0].z+hx*pi[j])*Z; + P[3][1] += (P[3][0].z+hx*pi[j+1])*Z; + P[0][2] += (P[0][3].z-hx*pp[j])*Z; + P[3][2] += (P[3][3].z-hx*pp[j+1])*Z; + P[1][0] += (P[0][0].z+hy*qi[j])*Z; + P[1][3] += (P[0][3].z+hy*qp[j])*Z; + P[2][0] += (P[3][0].z-hy*qi[j+1])*Z; + P[2][3] += (P[3][3].z-hy*qp[j+1])*Z; + // 3nd, second derivative + P[1][1] += (P[1][0].z+P[0][1].z-P[0][0].z+hxy*ri[j])*Z; + P[2][1] += (P[2][0].z+P[3][1].z-P[3][0].z-hxy*ri[j+1])*Z; + P[1][2] += (P[0][2].z+P[1][3].z-P[0][3].z-hxy*rp[j])*Z; + P[2][2] += (P[3][2].z+P[2][3].z-P[3][3].z+hxy*rp[j+1])*Z; + g.s[++k]=patch(P); + } + } + } + return g; +} + +// return the surface described by a real matrix f, interpolated with +// splinetype. +surface surface(real[][] f, real[] x, real[] y, + splinetype splinetype=null, bool[][] cond={}) +{ + if(splinetype == null) + splinetype=(x[0] == x[x.length-1] && y[0] == y[y.length-1]) ? + 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]=splinetype(x,ft[j]); + real[][] q=new real[n][]; + for(int i=0; i < n ; ++i) + q[i]=splinetype(y,f[i]); + real[][] qt=transpose(q); + real[] d1=splinetype(x,qt[0]); + real[] d2=splinetype(x,qt[m-1]); + real[][] r=new real[n][]; + for(int i=0; i < n ; ++i) + r[i]=clamped(d1[i],d2[i])(y,f[i]); + return bispline(f,transpose(tp),q,r,x,y,cond); +} + +// return the surface described by a real matrix f, interpolated with +// splinetype. +surface surface(real[][] f, pair a, pair b, splinetype splinetype, + 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,splinetype,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 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 splinetype. +surface surface(real f(pair z), pair a, pair b, int nx=nmesh, int ny=nx, + splinetype splinetype, 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,splinetype,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) +{ + pen thin=is3D() ? thin() : defaultpen; + begingroup3(pic); + 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); + 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); + } + } + } + endgroup3(pic); +} + +void draw(picture pic=currentpicture, Label[] L=new Label[], + guide3[][] g, pen p=currentpen) +{ + draw(pic,L,g,sequence(new pen(int) {return p;},g.length)); +} + +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) +{ + 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; + + 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); + if(truesize) { + picture opic; + draw(opic,g,p,arrow,margin); + add(pic,opic,f(z)); + } else + draw(pic,shift(f(z))*g,p,arrow,margin); + } + } + } + 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(phi1 >= phi2 && direction) phi1 -= 2pi; + if(phi2 >= phi1 && !direction) phi2 -= 2pi; + + 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) +{ + return Arc(c,r,90,0,90,360,normal,n)&cycle; +} diff --git a/Master/texmf/asymptote/graph_settings.asy b/Master/texmf/asymptote/graph_settings.asy new file mode 100644 index 00000000000..f5d04d0797b --- /dev/null +++ b/Master/texmf/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/asymptote/grid3.asy b/Master/texmf/asymptote/grid3.asy new file mode 100644 index 00000000000..dcb220e0746 --- /dev/null +++ b/Master/texmf/asymptote/grid3.asy @@ -0,0 +1,399 @@ +// grid3.asy +// Author: Philippe Ivaldi (Grids in 3D) +// 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; + og.create(pic,pic.userMin--X(pic),Y(pic)--XY(pic),pic.userMin--Z(pic), + pic.userMin.x,pic.userMax.x,pos,pic.scale.x); + return og; + }; +}; +grid3routine XYgrid=XYgrid(); + +grid3routine YXgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + og.create(pic,pic.userMin--Y(pic),X(pic)--XY(pic),pic.userMin--Z(pic), + pic.userMin.y,pic.userMax.y,pos,pic.scale.y); + return og; + }; +}; +grid3routine YXgrid=YXgrid(); + + +grid3routine XZgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + og.create(pic,pic.userMin--X(pic),Z(pic)--ZX(pic),pic.userMin--Y(pic), + pic.userMin.x,pic.userMax.x,pos,pic.scale.x); + return og; + }; +}; +grid3routine XZgrid=XZgrid(); + +grid3routine ZXgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + og.create(pic,pic.userMin--Z(pic),X(pic)--ZX(pic),pic.userMin--Y(pic), + pic.userMin.z,pic.userMax.z,pos,pic.scale.z); + return og; + }; +}; +grid3routine ZXgrid=ZXgrid(); + +grid3routine YZgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + og.create(pic,pic.userMin--Y(pic),Z(pic)--YZ(pic),pic.userMin--X(pic), + pic.userMin.y,pic.userMax.y,pos,pic.scale.y); + return og; + }; +}; +grid3routine YZgrid=YZgrid(); + +grid3routine ZYgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + og.create(pic,pic.userMin--Z(pic),Y(pic)--YZ(pic),pic.userMin--X(pic), + pic.userMin.z,pic.userMax.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/asymptote/interpolate.asy b/Master/texmf/asymptote/interpolate.asy new file mode 100644 index 00000000000..a6b820bc7d5 --- /dev/null +++ b/Master/texmf/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 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/asymptote/labelpath.asy b/Master/texmf/asymptote/labelpath.asy new file mode 100644 index 00000000000..fe597a1608d --- /dev/null +++ b/Master/texmf/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; + } + write("warning: 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/asymptote/labelpath3.asy b/Master/texmf/asymptote/labelpath3.asy new file mode 100644 index 00000000000..acfa64d528f --- /dev/null +++ b/Master/texmf/asymptote/labelpath3.asy @@ -0,0 +1,86 @@ +// Fit a label to a path3. +// Author: Jens Schwaiger + +import three; +size(7.5cm,0); +real eps=100*realEpsilon; +currentprojection=perspective(20,18,8); +currentlight=light(8,10,2); +//texpreamble("\textwidth=50cm"); // Avoid line breaking in long texts + +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; + for(int i=1; i < subdiv+1; i=i+1) { + bf[i][0]=dir(p,reltime(p,reltimestart+(i/subdiv)*lg)); + }; + for(int i=1; i < subdiv+1; i=i+1) { + 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 txt, path3 p, real angle=90, triple optional=O) +{ + real Cos=Cos(angle); + real Sin=Sin(angle); + path[] text=shift(-labelmargin(currentpen)*NE)* + texpath(Label(txt,(0,0),NE,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/asymptote/latin1.asy b/Master/texmf/asymptote/latin1.asy new file mode 100644 index 00000000000..3d76925be24 --- /dev/null +++ b/Master/texmf/asymptote/latin1.asy @@ -0,0 +1,2 @@ +usepackage("fontenc","T1"); +usepackage("inputenc","latin1"); diff --git a/Master/texmf/asymptote/markers.asy b/Master/texmf/asymptote/markers.asy new file mode 100644 index 00000000000..3fef6eb2d6c --- /dev/null +++ b/Master/texmf/asymptote/markers.asy @@ -0,0 +1,217 @@ +// Mark routines and markers written by Philippe Ivaldi. + +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/asymptote/math.asy b/Master/texmf/asymptote/math.asy new file mode 100644 index 00000000000..f204f746349 --- /dev/null +++ b/Master/texmf/asymptote/math.asy @@ -0,0 +1,392 @@ +// 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 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; +} + +// If strict=false, return whether i > j implies x[i] >= x[j] +// If strict=true, return whether i > j implies x[i] > x[j] +bool increasing(real[] x, bool strict=false) +{ + real[] xp=copy(x); + xp.delete(0); + xp.push(0); + bool[] b=strict ? (xp > x) : (xp >= x); + b[x.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; +} + +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; +} + +real[][] operator + (real[][] a, real[][] b) +{ + int n=a.length; + real[][] m=new real[n][]; + for(int i=0; i < n; ++i) + m[i]=a[i]+b[i]; + return m; +} + +real[][] operator - (real[][] a, real[][] b) +{ + int n=a.length; + real[][] m=new real[n][]; + for(int i=0; i < n; ++i) + m[i]=a[i]-b[i]; + return m; +} + +private string incommensurate= + "Multiplication of incommensurate matrices is undefined"; + +real[] operator * (real[] b, real[][] a) +{ + int nb=b.length; + if(nb != a.length) + abort(incommensurate); + int na0=a[0].length; + real[] m=new real[na0]; + for(int j=0; j < na0; ++j) { + real sum; + for(int k=0; k < nb; ++k) + sum += b[k]*a[k][j]; + m[j]=sum; + } + return m; +} + +real[][] operator * (real[][] a, real b) +{ + int n=a.length; + real[][] m=new real[n][]; + for(int i=0; i < n; ++i) + m[i]=a[i]*b; + return m; +} + +real[][] operator * (real b, real[][] a) +{ + return a*b; +} + +real[][] operator / (real[][] a, real b) +{ + return a*(1/b); +} + +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, 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. + 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; +} diff --git a/Master/texmf/asymptote/metapost.asy b/Master/texmf/asymptote/metapost.asy new file mode 100644 index 00000000000..525ffe755e1 --- /dev/null +++ b/Master/texmf/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/asymptote/nopapersize.ps b/Master/texmf/asymptote/nopapersize.ps new file mode 100644 index 00000000000..67e31404744 --- /dev/null +++ b/Master/texmf/asymptote/nopapersize.ps @@ -0,0 +1,3 @@ +@ a4size 0in 0in + +@ letterSize 0in 0in diff --git a/Master/texmf/asymptote/obj.asy b/Master/texmf/asymptote/obj.asy new file mode 100644 index 00000000000..02f839b594d --- /dev/null +++ b/Master/texmf/asymptote/obj.asy @@ -0,0 +1,104 @@ +// 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=word(line(input(datafile))); + 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: ",(string) 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); + } +} + +void draw(picture pic=currentpicture, obj o, light light=currentlight) +{ + draw(pic,o.s,o.surfacepen,o.meshpen,light); +} diff --git a/Master/texmf/asymptote/ode.asy b/Master/texmf/asymptote/ode.asy new file mode 100644 index 00000000000..5135dd9443f --- /dev/null +++ b/Master/texmf/asymptote/ode.asy @@ -0,0 +1,15 @@ +real euler(real y, real f(real x, real y), real a, real b=a, int n=0, + real h=0, bool dynamic=false, real tolmin=0, real tolmax=0) +{ + if(h == 0) { + if(b == a) return y; + if(n == 0) abort("Either n or h must be specified"); + else h=(b-a)/n; + } + real x=a; + for(int i=0; i < n; ++i) { + y += h*f(x,y); + x += h; + } + return y; +} diff --git a/Master/texmf/asymptote/palette.asy b/Master/texmf/asymptote/palette.asy new file mode 100644 index 00000000000..e9d3fb921db --- /dev/null +++ b/Master/texmf/asymptote/palette.asy @@ -0,0 +1,473 @@ +private import graph; + +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) +{ + _image(f,transpose ? transpose(data) : copy ? copy(data) : data, + initial,final,palette,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) +{ + _image(f,transpose ? transpose(data) : copy ? copy(data) : data, + initial,final,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); + int minindex=floor((dmin-rmin)/(rmax-rmin)*palette.length); + if(minindex < 0) minindex=0; + int maxindex=floor((dmax-rmin)/(rmax-rmin)*palette.length); + if(maxindex > palette.length) maxindex=palette.length; + if(minindex > 0 || maxindex < palette.length) { + pen[] newpalette; + for(int i=minindex; i < maxindex; ++i) + newpalette.push(palette[i]); + return newpalette; + } + 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(transpose) f=transpose(f); + else 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); + + pic.add(new void(frame F, transform t) { + _image(F,f,initial,final,palette,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(transpose) data=transpose(data); + else if(copy) data=copy(data); + + initial=Scale(pic,initial); + final=Scale(pic,final); + + pic.add(new void(frame F, transform t) { + _image(F,data,initial,final,t,copy=false,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); + + // 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)}; + if(vertical) pdata=transpose(pdata); + + pic.add(new void(frame f, transform t) { + _image(f,pdata,initial,final,palette,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/asymptote/patterns.asy b/Master/texmf/asymptote/patterns.asy new file mode 100644 index 00000000000..56fa3bf4a24 --- /dev/null +++ b/Master/texmf/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/asymptote/plain.asy b/Master/texmf/asymptote/plain.asy new file mode 100644 index 00000000000..a918a6bdc09 --- /dev/null +++ b/Master/texmf/asymptote/plain.asy @@ -0,0 +1,270 @@ +/***** + * 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) { + write(stdout,"warning: using possibly incompatible version "+ + version.VERSION+" of plain.asy"+'\n'); +} + +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_strings; +include plain_markers; +include plain_arrows; +include plain_debugger; + +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() +{ + write("warning: 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() +{ + write("warning: 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); +} + +// Conditionally process each file name in array s in a new environment. +void asy(string format, bool overwrite=false ... string[] s) +{ + for(int i=0; i < s.length; ++i) { + string f=s[i]; + int n=rfind(f,".asy"); + if(n != -1) f=erase(f,n,-1); + if(overwrite || error(input(f+"."+format,check=false))) { + string outformat=settings.outformat; + bool interactiveView=settings.interactiveView; + bool batchView=settings.batchView; + settings.outformat=format; + settings.interactiveView=false; + settings.batchView=false; + eval("defaultfilename=\""+f+"\"; import \""+f+ + "\" as dummy; exitfunction()"); + 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/asymptote/plain_Label.asy b/Master/texmf/asymptote/plain_Label.asy new file mode 100644 index 00000000000..1279880ca3a --- /dev/null +++ b/Master/texmf/asymptote/plain_Label.asy @@ -0,0 +1,601 @@ +// A rotation in the direction dir limited to [-90,90] +// This is useful for rotating text along a line in the direction dir. +transform rotate(explicit pair dir) +{ + real angle=degrees(dir); + if(angle > 90 && angle < 270) angle -= 180; + return rotate(angle); +} + +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; + D=abs(A[1][1]); + if(D != 0) 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));}; +} + +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(scaleless(shiftless(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); + },true); + } + + 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=nullpen, 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=nullpen, 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=nullpen, 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=nullpen, 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") +{ + static string[] stringcache; + static pen[] pencache; + static path[][] pathcache; + path[] g; + + string s=L.s; + pen p=L.p; + int k=0; + int i; + while((i=find(stringcache == s,++k)) >= 0) { + if(pencache[i] == p) { + g=pathcache[i]; + break; + } + } + + if(i == -1) { + g=tex ? _texpath(s,p) : textpath(s,p); + stringcache.push(s); + pencache.push(p); + pathcache.push(g); + } + + pair a; + if(g.length == 0) return g; + pair m=min(g); + pair M=max(g); + pair dir=rectify(inverse(L.T)*-L.align.dir); + if(basealign(p) == 1) + dir -= (0,(1-dir.y)*m.y/(M.y-m.y)); + a=m+realmult(dir,M-m); + + return shift(L.position+L.align.dir*labelmargin(p))*L.T*shift(-a)*g; +} diff --git a/Master/texmf/asymptote/plain_arcs.asy b/Master/texmf/asymptote/plain_arcs.asy new file mode 100644 index 00000000000..01e750f42cd --- /dev/null +++ b/Master/texmf/asymptote/plain_arcs.asy @@ -0,0 +1,43 @@ +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(t1 >= t2 && direction) t1 -= n; + if(t2 >= t1 && !direction) 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/asymptote/plain_arrows.asy b/Master/texmf/asymptote/plain_arrows.asy new file mode 100644 index 00000000000..0ef8c2b2d5c --- /dev/null +++ b/Master/texmf/asymptote/plain_arrows.asy @@ -0,0 +1,569 @@ +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,T[1],0); +}; + +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="", path g, align align=NoAlign, + pen p=currentpen, arrowbar arrow=None, arrowbar bar=None, + margin margin=NoMargin, Label legend="", marker marker=nomarker) +{ + Label L=L.copy(); + L.align(align); + if(marker != nomarker && !marker.above) marker.mark(pic,g); + bool drawpath=arrow(pic,g,p,margin); + if(bar(pic,g,p,margin) && drawpath) _draw(pic,g,p,margin); + if(L.s != "") { + L.p(p); + L.out(pic,g); + } + if(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="", path g, + align align=NoAlign, pen p=currentpen, arrowbar arrow=None, + arrowbar bar=None, margin margin=NoMargin, Label legend="", + 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="", marker marker=nomarker) +{ + 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="", 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="", 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="", 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="", pair b, pair dir, + real length=arrowlength, align align=NoAlign, + pen p=currentpen, arrowbar arrow=Arrow, margin margin=EndMargin) +{ + Label 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); +} + +// Force an array of 2D pictures to be as least as large as picture all. +void rescale2(picture[] pictures, picture all) +{ + if(!all.empty2()) { + transform t=inverse(all.calculateTransform()*pictures[0].T); + pair m=t*min(all); + pair M=t*max(all); + for(int i=0; i < pictures.length; ++i) { + draw(pictures[i],m,nullpen); + draw(pictures[i],M,nullpen); + } + } +} + +// Force an array of pictures to have a uniform scaling. +void rescale(picture[] pictures) +{ + if(pictures.length == 0) return; + picture all; + size(all,pictures[0]); + for(picture pic : pictures) + add(all,pic); + rescale2(pictures,all); +} diff --git a/Master/texmf/asymptote/plain_boxes.asy b/Master/texmf/asymptote/plain_boxes.asy new file mode 100644 index 00000000000..7fb82c37c1f --- /dev/null +++ b/Master/texmf/asymptote/plain_boxes.asy @@ -0,0 +1,136 @@ +// 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; + path g=box(min(src)+0.5*sign*min(p)-z,max(src)+0.5*sign*max(p)+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; + path g=ellipse(0.5*(M+m),factor*D.x+0.5*sign*max(p).x+xmargin, + factor*D.y+0.5*sign*max(p).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(string prefix="", picture pic=currentpicture, + real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill) +{ + frame f=pic.fit(prefix,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/asymptote/plain_constants.asy b/Master/texmf/asymptote/plain_constants.asy new file mode 100644 index 00000000000..c1d8c086f71 --- /dev/null +++ b/Master/texmf/asymptote/plain_constants.asy @@ -0,0 +1,157 @@ +restricted int undefined=intMax+((intMax % 2)-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 up=(0,1); +restricted pair down=(0,-1); +restricted pair right=(1,0); +restricted pair left=(-1,0); + +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 tab(file file) {write(file,'\t');} +void comma(file file) {write(file,',');} +typedef void suffix(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); + else write("default"); +} + +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/asymptote/plain_debugger.asy b/Master/texmf/asymptote/plain_debugger.asy new file mode 100644 index 00000000000..8568ee0bd8d --- /dev/null +++ b/Master/texmf/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/asymptote/plain_filldraw.asy b/Master/texmf/asymptote/plain_filldraw.asy new file mode 100644 index 00000000000..b9cd191e075 --- /dev/null +++ b/Master/texmf/asymptote/plain_filldraw.asy @@ -0,0 +1,234 @@ +// 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); + }); +} + +// 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/asymptote/plain_margins.asy b/Master/texmf/asymptote/plain_margins.asy new file mode 100644 index 00000000000..c4522754481 --- /dev/null +++ b/Master/texmf/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/asymptote/plain_markers.asy b/Master/texmf/asymptote/plain_markers.asy new file mode 100644 index 00000000000..67b218f716d --- /dev/null +++ b/Master/texmf/asymptote/plain_markers.asy @@ -0,0 +1,377 @@ +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); +} + +marker nomarker; + +real circlescale=0.85; + +marker[] Mark={ + marker(scale(circlescale)*unitcircle), + marker(polygon(3)),marker(polygon(4)), + marker(polygon(5)),marker(invert*polygon(3)), + marker(cross(4)),marker(cross(6)) +}; + +marker[] MarkFill={ + marker(scale(circlescale)*unitcircle,Fill),marker(polygon(3),Fill), + marker(polygon(4),Fill),marker(polygon(5),Fill), + marker(invert*polygon(3),Fill) +}; + +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/asymptote/plain_paths.asy b/Master/texmf/asymptote/plain_paths.asy new file mode 100644 index 00000000000..486f3f0cc7b --- /dev/null +++ b/Master/texmf/asymptote/plain_paths.asy @@ -0,0 +1,380 @@ +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(file file, string s="", explicit guide[] x, suffix suffix=none) +{ + write(file,s,(path[]) x,suffix); +} + +void write(string s="", explicit path[] x, suffix suffix=endl) +{ + write(stdout,s,x,suffix); +} + +void write(string s="", explicit guide[] x, suffix suffix=endl) +{ + write(stdout,s,(path[]) 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; +} + +guide operator ::(... guide[] a) +{ + if(a.length == 0) return nullpath; + guide g=a[0]; + for(int i=1; i < a.length; ++i) + g=g..operator tension(1,true)..a[i]; + return g; +} + +guide operator ---(... guide[] a) +{ + if(a.length == 0) return nullpath; + guide g=a[0]; + for(int i=1; i < a.length; ++i) + g=g..operator tension(infinity,true)..a[i]; + return g; +} + +// 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 h) +{ + return 0.5*(dir(p)+dir(h)); +} + +// 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); + real[] T=intersections(p,z,z+I*dir(p,i)); + // 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(inside(windingnumber(p,m),fillrule)) return m; + } + } + } + abort("cannot find an interior point"); + return 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/asymptote/plain_pens.asy b/Master/texmf/asymptote/plain_pens.asy new file mode 100644 index 00000000000..003c79149c7 --- /dev/null +++ b/Master/texmf/asymptote/plain_pens.asy @@ -0,0 +1,355 @@ +real labelmargin=0.3; +real dotfactor=6; + +pen solid=linetype(""); +pen dotted=linetype("0 4"); +pen dashed=linetype("8 8"); +pen longdashed=linetype("24 8"); +pen dashdotted=linetype("8 8 0 8"); +pen longdashdotted=linetype("24 8 0 8"); + +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=dotted+1.0; +pen Dotted(pen p=currentpen) {return dotted+2*linewidth(p);} + +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 inside(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=gray; +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="") +{ + if(settings.tex == "context") + return fontsize(size)+font(name+","+(string) size+"pt",options); + return fontsize(size)+font(name+" at "+(string) size+"pt"); +} + +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/asymptote/plain_picture.asy b/Master/texmf/asymptote/plain_picture.asy new file mode 100644 index 00000000000..4d894dc6dea --- /dev/null +++ b/Master/texmf/asymptote/plain_picture.asy @@ -0,0 +1,1892 @@ +real camerafactor=2; // Factor used for camera adjustment. +pair viewportsize=0; // Horizontal and vertical viewport limits. + +restricted bool Aspect=true; +restricted bool IgnoreAspect=false; + +pair size(frame f) +{ + return max(f)-min(f); +} + +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; +} + +// A coordinate in "flex space." A linear combination of user and true-size +// coordinates. +struct coord { + real user,truesize; + bool finite=true; + + // 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; + } +} + +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)*((finite(cx.user) ? 0 : 1), + (finite(cy.user) ? 0 : 1)); + 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=(tinf.x == 0 ? z.x : infinity); + Cy.user=(tinf.y == 0 ? z.y : infinity); + 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); + } +} + +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)*((finite(cx.user) ? 0 : 1), + (finite(cy.user) ? 0 : 1), + (finite(cz.user) ? 0 : 1)); + 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=(tinf.x == 0 ? z.x : infinity); + Cy.user=(tinf.y == 0 ? z.y : infinity); + Cz.user=(tinf.z == 0 ? z.z : infinity); + Cx.truesize=w.x; + Cy.truesize=w.y; + Cz.truesize=w.z; + push(Cx,Cy,Cz); + } + } +} + +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; + + // Find the first finite restriction. + int first=0; + for(first=0; first < n; ++first) + if(finite(in[first].user)) break; + + if(first == n) + return c; + else { + // 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 || !finite(x.user)) + 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; +} + +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; + } +} + +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) +{ + 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; +} + +struct transformation { + transform3 modelview; // For orientation and positioning + transform3 projection; // For 3D to 2D projection + bool infinity; + bool oblique; + void operator init(transform3 modelview, bool oblique=false) { + this.modelview=modelview; + this.projection=identity4; + infinity=true; + this.oblique=oblique; + } + void operator init(transform3 modelview, transform3 projection) { + this.modelview=modelview; + this.projection=projection; + infinity=false; + oblique=false; + } + transform3 compute() { + return projection*modelview; + } +} + +struct projection { + transform3 t; // projection*modelview (cached) + bool infinity; + bool oblique; + bool absolute=false; + triple camera; + triple up; + triple target; + 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? + real angle; // Lens angle (currently only used by PRC viewpoint). + int ninterpolate; // Used for projecting nurbs to 2D Bezier curves. + + void calculate() { + transformation T=projector(camera,up,target); + t=T.compute(); + infinity=T.infinity; + oblique=T.oblique; + ninterpolate=infinity ? 1 : 16; + } + + transformation transformation() { + return projector(camera,up,target); + } + + transform3 modelview() {return transformation().modelview;} + + triple vector() { + return camera-target; + } + + void operator init(triple camera, triple up=(0,0,1), triple target=(0,0,0), + bool showtarget=true, bool autoadjust=true, + bool center=false, projector projector) { + this.camera=camera; + this.up=up; + this.target=target; + 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.oblique=oblique; + P.camera=camera; + P.up=up; + P.target=target; + P.showtarget=showtarget; + P.autoadjust=autoadjust; + P.center=center; + P.projector=projector; + P.angle=angle; + P.ninterpolate=ninterpolate; + 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)); + } + + // 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; + +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. +typedef void drawerBound(frame f, transform t, transform T, pair lb, pair rt); + +struct picture { + // 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; + + // The coordinates in flex space to be used in sizing the picture. + struct bounds { + coords2 point,min,max; + bool exact=true; // An accurate picture bounds is provided by the user. + void erase() { + point.erase(); + min.erase(); + max.erase(); + } + bounds copy() { + bounds b=new bounds; + b.point=point.copy(); + b.min=min.copy(); + b.max=max.copy(); + b.exact=exact; + return b; + } + void xclip(real Min, real Max) { + point.xclip(Min,Max); + min.xclip(Min,Max); + max.xclip(Min,Max); + } + void yclip(real Min, real Max) { + point.yclip(Min,Max); + min.yclip(Min,Max); + max.yclip(Min,Max); + } + void clip(triple Min, triple Max) { + xclip(Min.x,Max.x); + yclip(Min.y,Max.y); + } + } + + 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; + + // Transform to be applied to this picture. + transform T; + transform3 T3; + + // Cached user-space bounding box + triple userMin,userMax; + bool userSetx,userSety,userSetz; + + 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; + + void init() { + userMin=userMax=(0,0,0); + userSetx=userSety=userSetz=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(); + } + + bool empty2() { + return nodes.length == 0; + } + + bool empty3() { + return nodes3.length == 0; + } + + bool empty() { + return empty2() && empty3(); + } + + pair userMin() {return (userMin.x,userMin.y);} + pair userMax() {return (userMax.x,userMax.y);} + + void userMinx(real x) { + userMin=(x,userMin.y,userMin.z); + userSetx=true; + } + + void userMiny(real y) { + userMin=(userMin.x,y,userMin.z); + userSety=true; + } + + void userMinz(real z) { + userMin=(userMin.x,userMin.y,z); + userSetz=true; + } + + void userMaxx(real x) { + userMax=(x,userMax.y,userMax.z); + userSetx=true; + } + + void userMaxy(real y) { + userMax=(userMax.x,y,userMax.z); + userSety=true; + } + + void userMaxz(real z) { + userMax=(userMax.x,userMax.y,z); + userSetz=true; + } + + void userCorners(pair c00, pair c01, pair c10, pair c11) { + userMin=(min(c00.x,c01.x,c10.x,c11.x),min(c00.y,c01.y,c10.y,c11.y), + userMin.z); + userMax=(max(c00.x,c01.x,c10.x,c11.x),max(c00.y,c01.y,c10.y,c11.y), + userMax.z); + } + + void userCorners(triple c000, triple c001, triple c010, triple c011, + triple c100, triple c101, triple c110, triple c111) { + userMin=(min(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x), + min(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y), + min(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z)); + userMax=(max(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x), + max(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y), + max(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z)); + } + + void userCopy(picture pic) { + userMin=(triple) pic.userMin; + userMax=(triple) pic.userMax; + userSetx=pic.userSetx; + userSety=pic.userSety; + userSetz=pic.userSetz; + } + + typedef real binop(real, real); + + // Cache the current user-space bounding box x coodinates + void userBoxX(real min, real max, binop m=min, binop M=max) { + if(userSetx) { + userMin=(m(userMin.x,min),userMin.y,userMin.z); + userMax=(M(userMax.x,max),userMax.y,userMax.z); + } else { + userMin=(min,userMin.y,userMin.z); + userMax=(max,userMax.y,userMax.z); + userSetx=true; + } + } + + // Cache the current user-space bounding box y coodinates + void userBoxY(real min, real max, binop m=min, binop M=max) { + if(userSety) { + userMin=(userMin.x,m(userMin.y,min),userMin.z); + userMax=(userMax.x,M(userMax.y,max),userMax.z); + } else { + userMin=(userMin.x,min,userMin.z); + userMax=(userMax.x,max,userMax.z); + userSety=true; + } + } + + // Cache the current user-space bounding box z coodinates + void userBoxZ(real min, real max, binop m=min, binop M=max) { + if(userSetz) { + userMin=(userMin.x,userMin.y,m(userMin.z,min)); + userMax=(userMax.x,userMax.y,M(userMax.z,max)); + } else { + userMin=(userMin.x,userMin.y,min); + userMax=(userMax.x,userMax.y,max); + userSetz=true; + } + } + + // Cache the current user-space bounding box + void userBox(pair min, pair max) { + userBoxX(min.x,max.x); + userBoxY(min.y,max.y); + } + + // Cache the current user-space bounding box + void userBox(triple min, triple max) { + userBoxX(min.x,max.x); + userBoxY(min.y,max.y); + userBoxZ(min.z,max.z); + } + + // Clip the current user-space bounding box + void userClip(pair min, pair max) { + userBoxX(min.x,max.x,max,min); + userBoxY(min.y,max.y,max,min); + } + + void add(drawerBound d, bool exact=false) { + uptodate=false; + if(!exact) bounds.exact=false; + nodes.push(d); + } + + void add(drawer d, bool exact=false, bool above=true) { + uptodate=false; + if(!exact) bounds.exact=false; + nodes.push(new void(frame f, transform t, transform T, pair, pair) { + d(f,t*T); + }); + } + + 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); + } + + void clip(drawer d, bool exact=false) { + bounds.clip(userMin,userMax); + this.add(d,exact); + } + + void clip(drawerBound d, bool exact=false) { + bounds.clip(userMin,userMax); + this.add(d,exact); + } + + // Add a point to the sizing. + void addPoint(pair user, pair truesize=0) { + bounds.point.push(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); + userBox(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.min.push(userMin,trueMin); + bounds.max.push(userMax,trueMax); + userBox(userMin,userMax); + } + + 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); + userBox(userMin,userMax); + } + + // Add a (user space) path to the sizing. + void addPath(path g) { + if(size(g) > 0) + addBox(min(g),max(g)); + } + void addPath(path[] g) { + for(int i=0; i < g.length; ++i) + addPath(g[i]); + } + + // Add a path to the sizing with the additional padding of a pen. + void addPath(path g, pen p) { + if(size(g) > 0) + addBox(min(g),max(g),min(p),max(p)); + } + + 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; + } + + // 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(finite(c[i].user) && 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(finite(c[i].user) && s.scale(c[i]) > M) + M=s.scale(c[i]); + return M; + } + + // Calculate the min for the final frame, given the coordinate transform. + pair min(transform t) { + if(bounds.min.x.length == 0 && bounds.point.x.length == 0 && + bounds.max.x.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(min(min(infinity,xs,bounds.point.x),xs,bounds.min.x), + xs,bounds.max.x), + min(min(min(infinity,ys,bounds.point.y),ys,bounds.min.y), + ys,bounds.max.y)); + } + + // Calculate the max for the final frame, given the coordinate transform. + pair max(transform t) { + if(bounds.min.x.length == 0 && bounds.point.x.length == 0 && + bounds.max.x.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(max(max(-infinity,xs,bounds.point.x),xs,bounds.min.x), + xs,bounds.max.x), + max(max(max(-infinity,ys,bounds.point.y),ys,bounds.min.y), + ys,bounds.max.y)); + } + + // 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)); + } + + // Calculate the sizing constants for the given array and maximum size. + real calculateScaling(string dir, coord[] coords, 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); + } + + coord[] m=maxcoords(coords,operator >=); + coord[] M=maxcoords(coords,operator <=); + + 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) { + return scaling.build(p.a(),p.b()).a; + } else if(status == simplex.problem.UNBOUNDED) { + if(warn) write("warning: "+dir+" scaling in picture unbounded"); + return 0; + } else { + if(!warn) return 1; + bool userzero=true; + for(int i=0; i < coords.length; ++i) { + if(coords[i].user != 0) userzero=false; + if(!finite(coords[i].user) || !finite(coords[i].truesize)) + abort("unbounded picture"); + } + if(userzero) return 1; + write("warning: cannot fit picture to "+dir+"size "+(string) size + +"...enlarging..."); + return calculateScaling(dir,coords,sqrt(2)*size,warn); + } + } + + void append(coords2 point, coords2 min, coords2 max, transform t, + bounds bounds) + { + // Add the coord info to this picture. + if(t == identity()) { + point.append(bounds.point); + min.append(bounds.min); + max.append(bounds.max); + } else { + point.push(t,bounds.point,bounds.point); + // Add in all 4 corner points, to properly size rectangular pictures. + point.push(t,bounds.min,bounds.min); + point.push(t,bounds.min,bounds.max); + point.push(t,bounds.max,bounds.min); + point.push(t,bounds.max,bounds.max); + } + } + + 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); + } + } + + // Returns the transform for turning user-space pairs into true-space pairs. + transform scaling(real xsize, real ysize, bool keepAspect=true, + bool warn=true) { + if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0) + return identity(); + + coords2 Coords; + + append(Coords,Coords,Coords,T,bounds); + + 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; + + 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); + } + + 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; + for(int i=0; i < nodes.length; ++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)); + } + + 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); + } + + frame scaled() { + frame f=fit(fixedscaling); + pair d=size(f); + static real epsilon=100*realEpsilon; + if(d.x > xsize*(1+epsilon)) + write("warning: frame exceeds xlimit: "+(string) d.x+" > "+ + (string) xsize); + if(d.y > ysize*(1+epsilon)) + write("warning: 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 thisture + // 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); + } + + // 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; + } + + 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)); + } + + // 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, + 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="", + projection P=currentprojection) { + return fitter == null ? fit2(xsize,ysize,keepAspect) : + fitter(prefix,this,format,xsize,ysize,keepAspect,view,options,script,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; + } + + // 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; + dest.userCopy(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; + } + + // 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.userSetx) userBoxX(src.userMin.x,src.userMax.x); + if(src.userSety) userBoxY(src.userMin.y,src.userMax.y); + if(src.userSetz) userBoxZ(src.userMin.z,src.userMax.z); + + 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; + } +} + +picture operator * (transform t, picture orig) +{ + picture pic=orig.copy(); + pic.T=t*pic.T; + pic.userCorners(t*(pic.userMin.x,pic.userMin.y), + t*(pic.userMin.x,pic.userMax.y), + t*(pic.userMax.x,pic.userMin.y), + t*(pic.userMax.x,pic.userMax.y)); + pic.bounds.exact=false; + return pic; +} + +picture operator * (transform3 t, picture orig) +{ + picture pic=orig.copy(); + pic.T3=t*pic.T3; + pic.userCorners(t*pic.userMin, + t*(pic.userMin.x,pic.userMin.y,pic.userMax.z), + t*(pic.userMin.x,pic.userMax.y,pic.userMin.z), + t*(pic.userMin.x,pic.userMax.y,pic.userMax.z), + t*(pic.userMax.x,pic.userMin.y,pic.userMin.z), + t*(pic.userMax.x,pic.userMin.y,pic.userMax.z), + t*(pic.userMax.x,pic.userMax.y,pic.userMin.z), + t*pic.userMax); + 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; +} + +pair point(picture pic=currentpicture, pair dir, bool user=true) +{ + pair z=pic.userMin()+realmult(rectify(dir),pic.userMax()-pic.userMin()); + 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.userMin()+realmult(z,pic.userMax()-pic.userMin()); +} + +void add(picture pic=currentpicture, drawer 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 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); +} + +void _draw(picture pic=currentpicture, path g, pen p=currentpen, + margin margin=NoMargin) +{ + 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) +{ + 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,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[] 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,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(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) +{ + if(pic.clipmin.length > 0 && pic.clipmax.length > 0) + pic.userClip(pic.clipmin.pop(),pic.clipmax.pop()); + pic.clip(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 about position to frame dest 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 about position to picture dest 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 attach(picture,frame,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 tex(picture pic=currentpicture, string s) +{ + pic.add(new void(frame f, transform) { + tex(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, pair min, pair max) +{ + 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(); +} diff --git a/Master/texmf/asymptote/plain_shipout.asy b/Master/texmf/asymptote/plain_shipout.asy new file mode 100644 index 00000000000..22aeef7121f --- /dev/null +++ b/Master/texmf/asymptote/plain_shipout.asy @@ -0,0 +1,113 @@ +// Default file prefix used for inline LaTeX mode +string defaultfilename; + +string outprefix(string prefix=defaultfilename) { + string s=prefix != "" ? prefix : + (settings.outname == "" && interactive()) ? "out" : settings.outname; + return stripextension(s); +} + +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, 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="", + projection P=currentprojection) +{ + if(is3D(f)) { + f=enclose(prefix,embed3(prefix,f,format,options,script,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.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="", + projection P=currentprojection) +{ + if(!uptodate()) { + bool inlinetex=settings.inlinetex; + bool prc=prc(format); + if(prc && !pic.empty3()) + settings.inlinetex=settings.inlineimage; + frame f=pic.fit(prefix,format,view=view,options,script,P); + if(!pic.empty2() || settings.render == 0 || prc) + 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/asymptote/plain_strings.asy b/Master/texmf/asymptote/plain_strings.asy new file mode 100644 index 00000000000..801e63e83cf --- /dev/null +++ b/Master/texmf/asymptote/plain_strings.asy @@ -0,0 +1,192 @@ +string defaultformat(int n, string trailingzero="", bool fixed=false, + bool signed=true) +{ + return "$%"+trailingzero+"."+string(n)+(fixed ? "f" : "g")+"$"; +} + +string defaultformat=defaultformat(4); + +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 +string[] split(string s, string delimiter) +{ + string[] S; + int last=0; + int i; + int N=length(delimiter); + while((i=find(s,delimiter,last)) >= 0) { + if(i >= last) + S.push(substr(s,last,i-last)); + last=i+N; + } + int n=length(s); + if(n >= last) + S.push(substr(s,last,n-last)); + return 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 graphic(string name, string options="") +{ + if(latex()) { + if(options != "") options="["+options+"]"; + return "\includegraphics"+options+"{"+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(real x) +{ + return format(defaultformat,x); +} + +string phantom(string s) +{ + return "\phantom{"+s+"}"; +} diff --git a/Master/texmf/asymptote/plain_xasy.asy b/Master/texmf/asymptote/plain_xasy.asy new file mode 100644 index 00000000000..162cbfb35ad --- /dev/null +++ b/Master/texmf/asymptote/plain_xasy.asy @@ -0,0 +1,128 @@ +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); + } +} + +framedTransformStack xformStack; + +void deconstruct(picture pic=currentpicture, real magnification=1) +{ + deconstruct(pic.fit(),currentpatterns,magnification,xformStack.pop); +} diff --git a/Master/texmf/asymptote/pstoedit.asy b/Master/texmf/asymptote/pstoedit.asy new file mode 100644 index 00000000000..7baccd3ed69 --- /dev/null +++ b/Master/texmf/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/asymptote/reload.js b/Master/texmf/asymptote/reload.js new file mode 100644 index 00000000000..a6526803056 --- /dev/null +++ b/Master/texmf/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/asymptote/roundedpath.asy b/Master/texmf/asymptote/roundedpath.asy new file mode 100644 index 00000000000..1a426f91dcb --- /dev/null +++ b/Master/texmf/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/asymptote/simplex.asy b/Master/texmf/asymptote/simplex.asy new file mode 100644 index 00000000000..bd9f69c9324 --- /dev/null +++ b/Master/texmf/asymptote/simplex.asy @@ -0,0 +1,272 @@ +/***** + * 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) + if (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/asymptote/size10.asy b/Master/texmf/asymptote/size10.asy new file mode 100644 index 00000000000..3bbd31227a9 --- /dev/null +++ b/Master/texmf/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/asymptote/size11.asy b/Master/texmf/asymptote/size11.asy new file mode 100644 index 00000000000..93712288152 --- /dev/null +++ b/Master/texmf/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/asymptote/slide.asy b/Master/texmf/asymptote/slide.asy new file mode 100644 index 00000000000..5b3596d02e6 --- /dev/null +++ b/Master/texmf/asymptote/slide.asy @@ -0,0 +1,574 @@ +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; + real temp=settings.paperwidth; + settings.paperwidth=settings.paperheight; + settings.paperheight=temp; + pagewidth += settings.paperwidth; + pageheight += settings.paperheight; +} 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 step() +{ + if(!stepping || !allowstepping) return; + if(!checkposition()) return; + lastnode.push(currentpicture.nodes.length-1); + 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 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) + write("warning: slide too wide on page "+(string) page+':\n'+(string) s); + + if(abs(offset.y+M.y) > 1) { + void toohigh() { + write("warning: 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) +{ + 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) + write("warning: 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); + 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) +{ + display(new frame[] {f},margin,align,p,figuremattpen); +} + +void display(string[] s, real margin=0, string[] captions=new string[], + string caption="", pair align=S, pen p=itempen, + pen figuremattpen=figuremattpen) +{ + 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); + if(caption != "") center(caption,p); +} + +void display(string s, string caption="", pair align=S, pen p=itempen, + pen figuremattpen=figuremattpen) +{ + display(new string[] {s},caption,align,p,figuremattpen); +} + +void figure(string[] s, string options="", real margin=0, + string[] captions=new string[], string caption="", + pair align=S, pen p=itempen, pen figuremattpen=figuremattpen) +{ + string[] S; + for(int i=0; i < s.length; ++i) { + S[i]=graphic(s[i],options); + } + + display(S,margin,captions,caption,align,itempen,figuremattpen); +} + +void figure(string s, string options="", string caption="", pair align=S, + pen p=itempen, pen figuremattpen=figuremattpen) +{ + figure(new string[] {s},options,caption,align,p,figuremattpen); +} + +string[] codefile; + +void asyinclude(string s, real xsize=0, real ysize=xsize) +{ + picture currentpictureSave=currentpicture; + currentpicture=new picture; + _eval("include \""+s+"\";",true); + s=stripdirectory(settings.outname+"_"+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=settings.outname+"_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$\,$"+s,p, + -bulletwidth*pt,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; + delete(outprefix()+"_.aux"); + 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/asymptote/slopefield.asy b/Master/texmf/asymptote/slopefield.asy new file mode 100644 index 00000000000..bdbb0252670 --- /dev/null +++ b/Master/texmf/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/asymptote/solids.asy b/Master/texmf/asymptote/solids.asy new file mode 100644 index 00000000000..0b2ffa2a508 --- /dev/null +++ b/Master/texmf/asymptote/solids.asy @@ -0,0 +1,395 @@ +import three; +import graph3; + +pen defaultbackpen=linetype("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=currentprojection) +{ + 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); + } + + // Return the surface of rotation obtain by rotating the path3 (x,0,f(x)) + // sampled n times between x=a and x=b about an axis lying in the XZ plane. + void operator init(triple c=O, real f(real x), real a, real b, int n=ngraph, + interpolate3 join=operator --, triple axis=Z, + real angle1=0, real angle2=360) { + operator init(c,graph(new triple(real x) {return (x,0,f(x));},a,b,n, + join),axis,angle1,angle2); + } + + 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; + // must be recomputed if camera is adjusted + void transverse(skeleton s, real t, int n=nslice, + projection P=currentprojection) { + 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=currentprojection) { + 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()) + write("warning: 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; + // must be recomputed if camera is adjusted + void longitudinal(skeleton s, int n=nslice, projection P=currentprojection) { + 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=currentprojection) { + 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, projection P=currentprojection) +{ + pen thin=is3D() ? thin() : defaultpen; + skeleton s=r.skeleton(m,n,P); + begingroup3(pic); + if(frontpen != nullpen) { + draw(pic,s.transverse.back,thin+defaultbackpen+backpen,light); + draw(pic,s.transverse.front,thin+frontpen,light); + } + if(longitudinalpen != nullpen) { + draw(pic,s.longitudinal.back,thin+defaultbackpen+longitudinalbackpen,light); + draw(pic,s.longitudinal.front,thin+longitudinalpen,light); + } + endgroup3(pic); +} + +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/asymptote/splinetype.asy b/Master/texmf/asymptote/splinetype.asy new file mode 100644 index 00000000000..366f91ac370 --- /dev/null +++ b/Master/texmf/asymptote/splinetype.asy @@ -0,0 +1,238 @@ +typedef real[] splinetype(real[], real[]); + +restricted real[] defaultspline(real[] x, real[] y); +restricted real[] Spline(real[] x, real[] y); + +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)); +} + +// 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); + 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); + if(y[n-1] != y[0]) 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); + 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); + 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); + 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/asymptote/stats.asy b/Master/texmf/asymptote/stats.asy new file mode 100644 index 00000000000..be9efdfa429 --- /dev/null +++ b/Master/texmf/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/asymptote/syzygy.asy b/Master/texmf/asymptote/syzygy.asy new file mode 100644 index 00000000000..dce87805c9d --- /dev/null +++ b/Master/texmf/asymptote/syzygy.asy @@ -0,0 +1,936 @@ +/***** 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 +frame[] fit(picture[] pics) { + frame[] f; + for (int i=0; i<pics.length; ++i) { + frame ff=pics[i].fit(); + //label(ff, (string)i, (10,10)); + //f.push(pics[i].fit()); + f.push(ff); + } + return f; +} + +// 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/asymptote/texcolors.asy b/Master/texmf/asymptote/texcolors.asy new file mode 100644 index 00000000000..90d9606cb70 --- /dev/null +++ b/Master/texmf/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/asymptote/three.asy b/Master/texmf/asymptote/three.asy new file mode 100644 index 00000000000..8e0fab6e8d0 --- /dev/null +++ b/Master/texmf/asymptote/three.asy @@ -0,0 +1,2662 @@ +private import math; + +if(inXasyMode) settings.render=0; + +if(prc0()) { + access embed; + Embed=embed.embed; + Link=embed.link; + if(settings.tex == "context") settings.prc=false; +} + +real defaultshininess=0.25; +real defaultgranularity=0; +real linegranularity=0.01; +real tubegranularity=0.003; +real dotgranularity=0.0001; +pair viewportmargin=0; // Horizontal and vertical viewport margins. +real viewportfactor=1.005; // Factor used to expand orthographic viewport. + +real angleprecision=1e-3; // Precision for centering perspective projections. +real anglefactor=max(1.005,1+angleprecision); + // Factor used to expand perspective viewport. + +string defaultembed3Doptions; +string defaultembed3Dscript; + +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); +} + +bool operator != (real[][] a, real[][] b) { + return !(a == b); +} + +// 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); + return (abs(u) > sqrtEpsilon) ? unit(u) : unit(cross(v,Z)); +} + +// 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; + P.target=t*P.target; + 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, + bool showtarget=true, bool autoadjust=true, + bool center=autoadjust) +{ + if(camera == target) + abort("camera cannot be at target"); + return projection(camera,up,target,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, + bool showtarget=true, bool autoadjust=true, + bool center=autoadjust) +{ + return perspective((x,y,z),up,target,showtarget,autoadjust,center); +} + +projection orthographic(triple camera, triple up=Z, triple target=O, + bool showtarget=true, bool center=false) +{ + return projection(camera,up,target,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, bool showtarget=true, + bool center=false) +{ + return orthographic((x,y,z),up,target,showtarget,center=center); +} + +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; + return projection((c2,s2,1),up=Y, + new transformation(triple,triple,triple) { + return transformation(t,oblique=true);}); +} + +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; + return projection((1,c2,s2), + new transformation(triple,triple,triple) { + return transformation(t,oblique=true);}); +} + +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; + return projection((c2,-1,s2), + new transformation(triple,triple,triple) { + return transformation(t,oblique=true);}); +} + +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,showtarget=true); +projection TopView=orthographic(Z,showtarget=true); + +currentprojection=perspective(5,4,2); + +// 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.vector(),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.vector(),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); + }; +} + +guide3 operator ::(... guide3[] a) +{ + if(a.length == 0) return nullpath3; + guide3 g=a[0]; + for(int i=1; i < a.length; ++i) + g=g.. tension atleast 1 ..a[i]; + return g; +} + +guide3 operator ---(... guide3[] a) +{ + if(a.length == 0) return nullpath3; + guide3 g=a[0]; + for(int i=1; i < a.length; ++i) + g=g.. tension atleast infinity ..a[i]; + return g; +} + +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 th 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.cyclicflag; + 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;} + +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); +} + +// 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.vector()); + 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); + 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; + } + + 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.vector()); + 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) +{ + triple v=unit(P.oblique ? P.camera : P.vector()); + triple u=unit(perp(P.up,v)); + if(u == O) u=cross(perp(v),v); + return transform3(cross(u,v),u); +} + +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 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; +} + +typedef guide3 interpolate3(... guide3[]); + +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(t1 >= t2 && direction) t1 -= n; + if(t2 >= t1 && !direction) 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; +} + +triple size3(frame f) +{ + return max3(f)-min3(f); +} + +// PRC/OpenGL support + +include three_light; + +void draw(frame f, path3 g, material p=currentpen, light light=nolight, + projection P=currentprojection); + +void begingroup3(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform3, picture opic, projection) { + if(opic != null) + begingroup(opic); + },true); +} + +void endgroup3(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform3, picture opic, projection) { + if(opic != null) + endgroup(opic); + },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; + +void draw(picture pic=currentpicture, Label L="", path3 g, + align align=NoAlign, material p=currentpen, margin3 margin=NoMargin3, + light light=nolight) +{ + 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,null); + if(pic != null && size(G) > 0) { + pic.addPoint(min(G,P.t)); + pic.addPoint(max(G,P.t)); + } + } + 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_arrows; + +draw=new void(frame f, path3 g, material p=currentpen, + light light=nolight, projection P=currentprojection) { + pen q=(pen) p; + if(is3D()) { + p=material(p,(p.granularity >= 0) ? p.granularity : linegranularity); + void drawthick(path3 g) { + if(settings.thick) { + real width=linewidth(q); + if(width > 0) { + surface s=tube(g,width); + int L=length(g); + if(L >= 0) { + if(!cyclic(g)) { + real r=0.5*width; + real linecap=linecap(q); + transform3 scale3r=scale3(r); + surface cap; + triple dirL=dir(g,L); + triple dir0=dir(g,0); + if(linecap == 0) + cap=scale(r,r,1)*unitdisk; + else if(linecap == 1) + cap=scale3r*((dir0 == O || dirL == O) ? + unitsphere : unithemisphere); + else if(linecap == 2) { + cap=scale3r*unitcylinder; + cap.append(scale3r*shift(Z)*unitdisk); + } + s.append(shift(point(g,0))*align(-dir0)*cap); + s.append(shift(point(g,L))*align(dirL)*cap); + } + if(opacity(q) == 1) _draw(f,g,q); + } + for(int i=0; i < s.s.length; ++i) + draw3D(f,s.s[i],p,light); + } else _draw(f,g,q); + } else _draw(f,g,q); + } + string type=linetype(adjust(q,arclength(g),cyclic(g))); + if(length(type) == 0) drawthick(g); + else { + real[] dash=(real[]) split(type," "); + 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; + } + } + } + } else draw(f,project(g,P),q); +}; + +void draw(frame f, explicit path3[] g, material p=currentpen, + light light=nolight, projection P=currentprojection) +{ + for(int i=0; i < g.length; ++i) draw(f,g[i],p,light,P); +} + +void draw(picture pic=currentpicture, explicit path3[] g, + material p=currentpen, margin3 margin=NoMargin3, light light=nolight) +{ + for(int i=0; i < g.length; ++i) draw(pic,g[i],p,margin,light); +} + +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) +{ + begingroup3(pic); + 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); + endgroup3(pic); + label(pic,L,g,align,(pen) p); +} + +void draw(frame f, path3 g, material p=currentpen, arrowbar3 arrow, + light light=nolight, light arrowheadlight=currentlight, + projection P=currentprojection) +{ + picture pic; + if(arrow(pic,g,p,NoMargin3,light,arrowheadlight)) + draw(f,g,p,light,P); + add(f,pic.fit()); +} + +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) +{ + 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); + 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, + projection P=currentprojection) +{ + arrow(pic,L,b,invert(dir,b,P),length,align,p,arrow,margin,light, + arrowheadlight); +} + +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 v=pic.userMin+realmult(rectify(dir),pic.userMax-pic.userMin); + 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,new string[][] {{" ",","},{"}{",","},{"{",""},{"}",""},}); + string[] S=split(s,","); + target=((real) S[0],(real) S[1],(real) S[2])*cm; + camera=target+(real) S[6]*((real) S[3],(real) S[4],(real) S[5])*cm; + triple u=unit(target-camera); + triple w=unit(Z-u.z*u); + up=rotate((real) S[7],O,u)*w; + angle=S[8] == "" ? 30 : (real) S[8]; + } +} + +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) < 1e18,"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[] file3; + +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_VERTICAL; +} + +asyProjection(); + +handler=new CameraEventHandler(); +runtime.addEventHandler(handler); +handler.onEvent=function(event) +{ + asyProjection(); + scene.update(); +}"; +} + +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 != "") { + 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(projection P, real width, real height) +{ + pair viewportmargin=viewportmargin; + real xmargin=viewportmargin.x; + real ymargin=viewportmargin.y; + if(xmargin <= 0) xmargin=max(0.5*(viewportsize.x-width),0); + if(ymargin <= 0) ymargin=max(0.5*(viewportsize.y-height),0); + return (xmargin,ymargin); +} + +string embed3D(string label="", string text=label, string prefix, + frame f, string format="", + real width=0, real height=0, real angle=30, + 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); + real margin=(viewportfactor-1.0)*lambda.y+ + viewportmargin(P,lambda.x,lambda.y).y; + viewplanesize=(lambda.y+2*margin)/cm; + } + string name=prefix+".js"; + writeJavaScript(name,lightscript+projection(P.infinity,viewplanesize),script); + + shipout3(prefix,f); + + prefix += ".prc"; + if(!settings.inlinetex) + file3.push(prefix); + + triple v=P.vector()/cm; + 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.absolute ? P.angle : angle)+ + ",3Dc2c="+format(u)+ + ",3Dcoo="+format(P.target/cm)+ + ",3Droll="+format(roll)+ + ",3Droo="+format(abs(v))+ + ",3Dbg="+format(light.background()); + if(options != "") options3 += ","+options; + if(name != "") options3 += ",3Djscript="+stripdirectory(name); + + return Embed(stripdirectory(prefix),options3,width,height); +} + +object embed(string label="", string text=label, + string prefix=defaultfilename, + frame f, string format="", + real width=0, real height=0, real angle=30, + string options="", string script="", + light light=currentlight, projection P=currentprojection) +{ + object F; + + if(is3D(format)) + F.L=embed3D(label,text,prefix,f,format,width,height,angle,options,script, + light,P); + else + F.f=f; + 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="", real angle=0, + light light=currentlight, projection P=currentprojection) +{ + object F; + real xsize3=pic.xsize3, ysize3=pic.ysize3, zsize3=pic.zsize3; + bool warn=true; + transform3 modelview; + + if(xsize3 == 0 && ysize3 == 0 && zsize3 == 0) { + xsize3=ysize3=zsize3=max(xsize,ysize); + warn=false; + } + + projection P=P.copy(); + + if(!P.absolute && P.showtarget) + draw(pic,P.target,nullpen); + + transform3 t=pic.scaling(xsize3,ysize3,zsize3,keepAspect,warn); + bool adjusted=false; + transform3 tinv=inverse(t); + triple m=pic.min(t); + triple M=pic.max(t); + + if(!P.absolute) { + P=t*P; + if(P.center) { + bool recalculate=false; + triple target=0.5*(m+M); + P.target=target; + recalculate=true; + if(recalculate) P.calculate(); + } + if(P.autoadjust || P.infinity) + adjusted=adjusted | P.adjust(m,M); + } + + picture pic2; + + frame f=pic.fit3(t,pic.bounds3.exact ? pic2 : null,P); + + if(!pic.bounds3.exact) { + transform3 s=pic.scale3(f,xsize3,ysize3,zsize3,keepAspect); + t=s*t; + tinv=inverse(t); + P=s*P; + f=pic.fit3(t,pic2,P); + } + + bool is3D=is3D(format); + bool scale=xsize != 0 || ysize != 0; + + 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; + pair viewportmargin=viewportmargin(P,lambda.x,lambda.y); + real width=ceil(lambda.x+2*viewportmargin.x); + real height=ceil(lambda.y+2*viewportmargin.y); + + projection Q; + if(!P.absolute) { + if(scale) { + pair v=(s.xx,s.yy); + transform3 T=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); + } + pic2.erase(); + t=xscale3(f(v,x))*yscale3(f(v,y))*zscale3(f(v,z))*t; + f=pic.fit3(t,is3D ? null : pic2,P); + } + + if(P.autoadjust || P.infinity) + adjusted=adjusted | P.adjust(min3(f),max3(f)); + + triple target=P.target; + modelview=P.modelview(); + f=modelview*f; + P=modelview*P; + Q=P.copy(); + light=modelview*light; + + if(P.infinity) { + triple m=min3(f); + triple M=max3(f); + triple s=(-0.5(m.x+M.x),-0.5*(m.y+M.y),0); + f=shift(s)*f; // Eye will be at (0,0,0). + } else { + transform3 T=identity4; + // Choose the angle to be just large enough to view the entire image: + if(angle == 0) angle=P.angle; + int maxiterations=100; + if(is3D && angle == 0) { + 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 < maxiterations); + 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; + } + + angle=anglefactor*max(aTan(rx)+aTan(Rx),aTan(ry)+aTan(Ry)); + if(viewportmargin.y != 0) + angle=2*aTan(Tan(0.5*angle)-viewportmargin.y/P.target.z); + + modelview=T*modelview; + } + if(settings.verbose > 0) { + transform3 inv=inverse(modelview); + if(adjusted) + write("adjusting camera to ",tinv*inv*P.camera); + target=inv*P.target; + } + P=T*P; + } + if(settings.verbose > 0) { + if(P.center || (!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. + prefix=replace(prefix,new string[][]{{" ","_"},{".","_"}}); + if(settings.embed || nativeformat() == "pdf") + prefix += "+"+(string) file3.length; + } else + preview=false; + if(preview || (!prc && settings.render != 0)) { + frame f=f; + triple m,M; + real zcenter; + if(P.absolute) { + modelview=P.modelview(); + f=modelview*f; + P=modelview*P; + m=min3(f); + M=max3(f); + real r=0.5*abs(M-m); + zcenter=0.5*(M.z+m.z); + M=(M.x,M.y,zcenter+r); + m=(m.x,m.y,zcenter-r); + } else { + m=min3(f); + M=max3(f); + zcenter=P.target.z; + real d=P.distance(m,M); + M=(M.x,M.y,zcenter+d); + m=(m.x,m.y,zcenter-d); + } + + if(P.infinity) { + triple margin=(viewportfactor-1.0)*(abs(M.x-m.x), + abs(M.y-m.y),0)+ + viewportfactor*(0,viewportmargin.y,0); + M += margin; + m -= margin; + } else if(M.z >= 0) abort("camera too close"); + + shipout3(prefix,f,preview ? nativeformat() : format, + width,height,P.infinity ? 0 : angle,m,M, + 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 && settings.embed) { + image=prefix; + if(settings.inlinetex) image += "_0"; + image += "."+nativeformat(); + if(!settings.inlinetex) file3.push(image); + image=graphic(image); + } + if(prc) F.L=embed3D(label,text=image,prefix,f,format, + width,height,angle,options,script,light, + P.absolute ? P : Q); + } + + if(!is3D) { + transform T=pic2.scaling(xsize,ysize,keepAspect); + F.f=pic.fit(scale(t[0][0])*T); + add(F.f,pic2.fit(T)); + } + + return F; +} + +embed3=new object(string prefix, frame f, string format, string options, + string script, projection P) { + return embed(prefix=prefix,f,format,options,script,P); +}; + +currentpicture.fitter=new frame(string prefix, picture pic, string format, + real xsize, real ysize, + bool keepAspect, bool view, + string options, string script, projection P) { + frame f; + bool empty3=pic.empty3(); + if(is3D(format) || empty3) add(f,pic.fit2(xsize,ysize,keepAspect)); + if(!empty3) { + 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=embed(prefix=prefix,pic,format,xsize,ysize,keepAspect,view, + options,script,currentlight,P); + if(prc) + label(f,F.L); + else { + if(settings.render == 0) { + add(f,F.f); + if(currentlight.background != nullpen) + box(f,currentlight.background,Fill,above=false); + } else if(!view) + label(f,graphic(prefix)); + } + } + return f; +}; + +void addViews(picture dest, picture src, bool group=true, + filltype filltype=NoFill, bool above=true) +{ + frame Front=src.fit(FrontView); + add(dest,Front,group,filltype,above); + frame Top=src.fit(TopView); + add(dest,shift(0,min(Front).y-max(Top).y)*Top,group,filltype,above); + frame Right=src.fit(RightView); + add(dest,shift(min(Front).x-max(Right).x)*Right,group,filltype,above); +} + +void addViews(picture src, bool group=true, filltype filltype=NoFill, + bool above=true) +{ + addViews(currentpicture,src,group,filltype,above); +} + +// Force an array of 3D pictures to be as least as large as picture all. +void rescale3(picture[] pictures, picture all, projection P=currentprojection) +{ + if(!all.empty3()) { + transform3 t=inverse(all.calculateTransform3(P)*pictures[0].T3); + triple m=t*min3(all); + triple M=t*max3(all); + for(int i=0; i < pictures.length; ++i) { + draw(pictures[i],m,nullpen); + draw(pictures[i],M,nullpen); + } + } +} + +// Force an array of pictures to have a uniform scaling using currenprojection. +rescale=new void(picture[] pictures) { + if(pictures.length == 0) return; + picture all; + size(all,pictures[0]); + for(picture pic : pictures) + add(all,pic); + rescale2(pictures,all); + rescale3(pictures,all); +}; + +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/asymptote/three_arrows.asy b/Master/texmf/asymptote/three_arrows.asy new file mode 100644 index 00000000000..78ee56d9f04 --- /dev/null +++ b/Master/texmf/asymptote/three_arrows.asy @@ -0,0 +1,789 @@ +// 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); + } + } + } +} + +void render(path3 s, void f(path3, real)) +{ + static int maxdepth=ceil(-log(realEpsilon)/log(2))+1; + 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); + real R=infinity; + int nintervals=3; + real fuzz=sqrtEpsilon*max(abs(z0),abs(z1)); + if(S > 0) { + --depth; + for(int i=0; i <= nintervals; ++i) { + R=min(R,radius(z0,c0,c1,z1,i/nintervals)); + if(S > max(tubegranularity*R,fuzz)) { + 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)); +} + +surface tube(path3 g, real width) +{ + surface tube; + real r=0.5*width; + + transform3 t=scale3(r); + + int n=length(g); + for(int i=0; i < n; ++i) { + real S=straightness(g,i); + if(S < sqrtEpsilon*r) { + triple v=point(g,i); + triple u=point(g,i+1)-v; + tube.append(shift(v)*align(unit(u))*scale(r,r,abs(u))*unitcylinder); + } else { + render(subpath(g,i,i+1),new void(path3 q, real) { + real L=arclength(q); + surface segment=scale(r,r,L)*unitcylinder; + bend(segment,q,L); + tube.s.append(segment.s); + }); + } + + if((cyclic(g) || i > 0) && abs(dir(g,i,1)-dir(g,i,-1)) > sqrtEpsilon) + tube.append(shift(point(g,i))*t*align(dir(g,i,-1))*unithemisphere); + } + return tube; +} + +// Refine a noncyclic path3 g so that it approaches its endpoint in +// geometrically spaced steps. +path3 approach(path3 g, int n, real radix=3) +{ + guide3 G; + real L=length(g); + real tlast=0; + real r=1/radix; + for(int i=1; i < n; ++i) { + real t=L*(1-r^i); + G=G&subpath(g,tlast,t); + tlast=t; + } + return G&subpath(g,tlast,L); +} + +struct arrowhead3 +{ + arrowhead arrowhead2=DefaultHead; + real size(pen p)=arrowsize; + real arcsize(pen p)=arcarrowsize; + real gap=1; + 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.vector() : 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)); + 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) { + 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]=(k*p.P[i][j].x,k*p.P[i][j].y,p.P[i][j].z); + p.P[i][j]=bend(p.P[i][j],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); + + angle=min(angle*arrowhookfactor,45); + 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); + 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.01; + 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); + angle=min(angle*arrowhookfactor,45); + path h=a.project(g,forwards,P); + 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.01; + 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); + 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.gap=0.83; + a.splitpath=false; + 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); + 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.vector(),perp)) : d; + draw(opic,A-v--A+v,p,light); + add(f,opic.fit3(identity4,pic2,P)); + }); + triple v=d == O ? cross(currentprojection.vector(),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/asymptote/three_light.asy b/Master/texmf/asymptote/three_light.asy new file mode 100644 index 00000000000..54ca7a47151 --- /dev/null +++ b/Master/texmf/asymptote/three_light.asy @@ -0,0 +1,174 @@ +struct material { + pen[] p; // diffusepen,ambientpen,emissivepen,specularpen + real opacity; + real shininess; + real granularity; + void operator init(pen diffusepen=black, pen ambientpen=black, + pen emissivepen=black, pen specularpen=mediumgray, + real opacity=opacity(diffusepen), + real shininess=defaultshininess, + real granularity=-1) { + p=new pen[] {diffusepen,ambientpen,emissivepen,specularpen}; + this.opacity=opacity; + this.shininess=shininess; + this.granularity=granularity; + } + void operator init(material m, real granularity=m.granularity) { + p=copy(m.p); + opacity=m.opacity; + shininess=m.shininess; + this.granularity=granularity; + } + 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,", granularity=",x.granularity); + 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 && m.granularity == n.granularity; +} + +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, real granularity=m.granularity) +{ + return material(black+opacity(m.opacity),black,m.diffuse(),black,m.opacity,1, + granularity); +} + +struct light { + real[][] diffuse; + real[][] ambient; + real[][] specular; + pen background; // 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 implemented. + + transform3 T=identity(4); // Transform to apply to normal vectors. + + bool on() {return position.length > 0;} + + void operator init(pen[] diffuse=array(position.length,white), + pen[] ambient=array(position.length,black), + pen[] specular=diffuse, pen background=nullpen, + real specularfactor=1, + bool viewport=true, triple[] position) { + this.position=new triple[position.length]; + this.diffuse=new real[position.length][]; + this.ambient=new real[position.length][]; + this.specular=new real[position.length][]; + this.background=background; + for(int i=0; i < position.length; ++i) { + this.position[i]=unit(position[i]); + this.diffuse[i]=rgba(diffuse[i]); + this.ambient[i]=rgba(ambient[i]); + this.specular[i]=rgba(specular[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=true...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=true, + 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); + } + + pen color(triple normal, material m, transform3 T=T) { + 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=viewport ? position[i] : T*position[i]; + real Ldotn=max(dot(normal,L),0); + p += ambient[i]*Ambient+Ldotn*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*specularfactor*specular[i]*Specular; + } + return rgb(p[0],p[1],p[2])+opacity(opacity(m.diffuse())); + } + + real[] background() {return rgba(background == nullpen ? white : background);} +} + +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 currentlight=light(ambient=rgb(0.1,0.1,0.1),specularfactor=3, + (0.25,-0.25,1)); + +light adobe=light(gray(0.4),specularfactor=3,viewport=false, + (0.5,-0.5,-0.25),(0.5,0.5,0.25), + (0.5,-0.5,0.2),(-0.5,0.5,-0.2)); +light nolight; diff --git a/Master/texmf/asymptote/three_margins.asy b/Master/texmf/asymptote/three_margins.asy new file mode 100644 index 00000000000..b6f286ee4ff --- /dev/null +++ b/Master/texmf/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/asymptote/three_surface.asy b/Master/texmf/asymptote/three_surface.asy new file mode 100644 index 00000000000..55060cb516d --- /dev/null +++ b/Master/texmf/asymptote/three_surface.asy @@ -0,0 +1,1371 @@ +import bezulate; + +int nslice=12; +real camerafactor=1.2; + +private real Fuzz=10.0*realEpsilon; +private real nineth=1/9; + +struct patch { + triple[][] P=new triple[4][4]; + 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 + P[0][0]..controls P[0][1] and P[0][2].. + P[0][3]..controls P[1][3] and P[2][3].. + P[3][3]..controls P[3][2] and P[3][1].. + P[3][0]..controls P[2][0] and P[1][0]..cycle; + } + + triple[] internal() { + return new triple[] {P[1][1],P[1][2],P[2][2],P[2][1]}; + } + + triple cornermean() { + return 0.25*(P[0][0]+P[0][3]+P[3][3]+P[3][0]); + } + + triple[] corners() {return new triple[] {P[0][0],P[0][3],P[3][3],P[3][0]};} + + real[] map(real f(triple)) { + return new real[] {f(P[0][0]),f(P[0][3]),f(P[3][3]),f(P[3][0])}; + } + + 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) { + return straight ? Bu(0,u)--Bu(3,u) : + Bu(0,u)..controls Bu(1,u) and Bu(2,u)..Bu(3,u); + } + + 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) { + return straight ? Bv(0,v)--Bv(3,v) : + Bv(0,v)..controls Bv(1,v) and Bv(2,v)..Bv(3,v); + } + + 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(BvPP(0,v),BvPP(1,v),BvPP(2,v),BvPP(3,v),u), + bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v))+ + cross(bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u), + bezier(BuPP(0,u),BuPP(1,u),BuPP(2,u),BuPP(3,u),v))); + return abs(n) > epsilon ? n : + 0.25*cross(bezier(BvPP(0,v),BvPP(1,v),BvPP(2,v),BvPP(3,v),u), + bezier(BuPP(0,u),BuPP(1,u),BuPP(2,u),BuPP(3,u),v))+ + 1/6*(cross(bezier(BvPPP(0),BvPPP(1),BvPPP(2),BvPPP(3),u), + bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v))+ + cross(bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u), + bezier(BuPPP(0),BuPPP(1),BuPPP(2),BuPPP(3),v)))+ + 1/12*(cross(bezier(BvPPP(0),BvPPP(1),BvPPP(2),BvPPP(3),u), + bezier(BuPP(0,u),BuPP(1,u),BuPP(2,u),BuPP(3,u),v))+ + cross(bezier(BvPP(0,v),BvPP(1,v),BvPP(2,v),BvPP(3,v),u), + bezier(BuPPP(0),BuPPP(1),BuPPP(2),BuPPP(3),v)))+ + 1/36*cross(bezier(BvPPP(0),BvPPP(1),BvPPP(2),BvPPP(3),u), + bezier(BuPPP(0),BuPPP(1),BuPPP(2),BuPPP(3),v)); + } + + static real fuzz=1000*realEpsilon; + + triple normal(real u, real v) { + triple n=cross(bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u), + bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v)); + real epsilon=fuzz*change2(P); + return (abs(n) > epsilon) ? n : normal0(u,v,epsilon); + } + + triple normal00() { + triple n=9*cross(P[0][1]-P[0][0],P[1][0]-P[0][0]); + real epsilon=fuzz*change2(P); + return abs(n) > epsilon ? n : normal0(0,0,epsilon); + } + + triple normal01() { + triple n=9*cross(P[0][3]-P[0][2],P[1][3]-P[0][3]); + real epsilon=fuzz*change2(P); + return abs(n) > epsilon ? n : normal0(0,1,epsilon); + } + + triple normal11() { + triple n=9*cross(P[3][3]-P[3][2],P[3][3]-P[2][3]); + real epsilon=fuzz*change2(P); + return abs(n) > epsilon ? n : normal0(1,1,epsilon); + } + + triple normal10() { + triple n=9*cross(P[3][1]-P[3][0],P[3][0]-P[2][0]); + real epsilon=fuzz*change2(P); + return abs(n) > epsilon ? n : normal0(1,0,epsilon); + } + + pen[] colors(material m, light light=currentlight) { + bool nocolors=colors.length == 0; + if(normals.length > 0) + return new pen[] {light.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])}; + if(planar) { + triple normal=normal(0.5,0.5); + return new pen[] {light.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])}; + } + return new pen[] {light.color(normal00(),nocolors ? m : colors[0]), + light.color(normal01(),nocolors ? m : colors[1]), + light.color(normal11(),nocolors ? m : colors[2]), + light.color(normal10(),nocolors ? m : colors[3])}; + } + + 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=minbound(P,bound); + } + + triple max(triple bound=P[0][0]) { + if(havemax3) return maxbound(max3,bound); + havemax3=true; + return max3=maxbound(P,bound); + } + + triple center() { + return 0.5*(this.min()+this.max()); + } + + pair min(projection P, pair bound=project(this.P[0][0],P.t)) { + return minbound(this.P,P.t,bound); + } + + pair max(projection P, pair bound=project(this.P[0][0],P.t)) { + return maxbound(this.P,P.t,bound); + } + + void operator init(triple[][] P, triple[] normals=new triple[], + pen[] colors=new pen[], bool straight=false, + bool3 planar=default) { + init(); + this.P=copy(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),postcontrol(external,0),precontrol(external,1), + point(external,1)}, + {precontrol(external,0),internal[0],internal[1],postcontrol(external,1)}, + {postcontrol(external,3),internal[3],internal[2],precontrol(external,2)}, + {point(external,3),precontrol(external,3),postcontrol(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[0],external[1]-delta[0],external[1]}, + {external[0]-delta[3],internal[0],internal[1],external[1]+delta[1]}, + {external[3]+delta[3],internal[3],internal[2],external[2]-delta[1]}, + {external[3],external[3]-delta[2],external[2]+delta[2],external[2]} + }; + } +} + +patch operator * (transform3 t, patch s) +{ + patch S; + 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; +} + +struct surface { + patch[] s; + + 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]); + } + + 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) { + pen[] palettei=palette[i]; + s[i].colors=new pen[] {palettei[0],palettei[1],palettei[2],palettei[3]}; + } + } + + 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); + } + + // A constructor for a possibly nonconvex cyclic path in a given plane. + void operator init (path p, triple plane(pair)=XYplane, + bool checkboundary=true) { + if(!cyclic(p)) + abort("cyclic path expected"); + + int L=length(p); + + if(L > 4) { + for(path g : bezulate(p)) + s.append(surface(g,plane,checkboundary).s); + return; + } + + pair[][] P(path 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),postcontrol(p,0),precontrol(p,1),point(p,1)}, + {precontrol(p,0),internal[0],internal[1],postcontrol(p,1)}, + {postcontrol(p,3),internal[3],internal[2],precontrol(p,2)}, + {point(p,3),precontrol(p,3),postcontrol(p,2),point(p,2)} + }; + } + + bool straight=piecewisestraight(p); + if(L <= 3 && straight) { + s=new patch[] {patch(P(p),plane,straight)}; + return; + } + + // 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=surface(subpath(g,0,cut)--cycle,plane,checkboundary).s; + s.append(surface(subpath(g,cut,L)--cycle,plane,checkboundary).s); + 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; + } + } + + pair[][] P=P(p); + + if(straight) { + s=new patch[] {patch(P,plane,straight)}; + return; + } + + // 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)) + s=new patch[] {patch(P,plane)}; + return; + } + + 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[0][j]-P[1][j])*P[0][i]).y; // u=0 + c[1] += w*(conj(P[i][3])*(P[j][3]-P[j][2])).y; // v=1 + c[2] += w*(conj(P[3][j]-P[2][j])*P[3][i]).y; // u=1 + c[3] += w*(conj(P[i][0])*(P[j][1]-P[j][0])).y; // v=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(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u))* + bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v)).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={0,t,1,t}; + real[] V={t,1,t,0}; + 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; + } + + // 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; + + while(true) + for(int i=0; i < L; ++i) + if(!straight(p,i) && split(p,i+unitrand())) return; + } + + 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[]; + 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]; + 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; + + for(int i=0; i < L; ++i) { + path3 h=subpath(g,i,i+1); + path3 r=reverse(h); + triple max=max(h); + triple min=min(h); + triple perp=perp(max-c,axis); + real fuzz=epsilon*max(abs(max),abs(min)); + if(abs(perp) < fuzz) + perp=perp(min-c,axis); + perp=unit(perp); + triple normal=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=Tk*h{dirj}..{dirp}Tp*r{-dirp}..{-dirj}cycle; + Tk=Tp; + dirj=dirp; + s[++m]=color == null ? patch(G) : + patch(G,new pen[] {color(i,j),color(i+1,j),color(i+1,j+w), + color(i,j+w)}); + } + } + } + + 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]; + 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 for a subpatch of P on [u,1] x [v,1]. +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); + + u=1.0-u; + + triple[] c7=split(c3[2],c2[2],c1[2],c0[2],u); + triple[] c8=split(c3[3],c2[3],c1[3],c0[3],u); + triple[] c9=split(c3[4],c2[4],c1[4],c0[4],u); + triple[] c10=split(P3[3],P2[3],P1[3],P0[3],u); + + return new triple[][] {{c7[2],c8[2],c9[2],c10[2]}, + {c7[1],c8[1],c9[1],c10[1]}, + {c7[0],c8[0],c9[0],c10[0]}, + {c3[2],c3[3],c3[4],P3[3]}}; +} + +// Return the control points for a subpatch of P on [0,u] x [0,v]. +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); + + u=1.0-u; + + triple[] c4=split(P3[0],P2[0],P1[0],P0[0],u); + triple[] c5=split(c3[0],c2[0],c1[0],c0[0],u); + triple[] c6=split(c3[1],c2[1],c1[1],c0[1],u); + triple[] c7=split(c3[2],c2[2],c1[2],c0[2],u); + + return new triple[][] { + {P0[0],c0[0],c0[1],c0[2]}, + {c4[4],c5[4],c6[4],c7[4]}, + {c4[3],c5[3],c6[3],c7[3]}, + {c4[2],c5[2],c6[2],c7[2]}}; +} + +patch subpatch(patch s, real ua, real va, real ub, real vb) +{ + assert(ua >= 0 && va >= 0 && ub <= 1 && vb <= 1 && ua < ub && va < vb); + return patch(subpatchbegin(subpatchend(s.P,ub,vb),ua/ub,va/vb), + 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 fuzzFactor=10.0; + static real Fuzz=1000.0*realEpsilon; + real fuzz=max(fuzzFactor*fuzz,Fuzz)*abs(max(s)-min(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); +} + +triple point(patch s, real u, real v) +{ + return s.point(u,v); +} + +void draw3D(frame f, patch s, material m, light light=currentlight) +{ + if(s.colors.length > 0) + m=mean(s.colors); + bool lighton=light.on(); + if(!lighton && !invisible((pen) m)) + m=emissive(m); + real granularity=m.granularity >= 0 ? m.granularity : defaultgranularity; + draw(f,s.P,s.straight,m.p,m.opacity,m.shininess,granularity, + s.planar ? s.normal(0.5,0.5) : O,lighton,s.colors); +} + +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, + projection P=currentprojection) +{ + if(is3D()) { + for(int i=0; i < s.s.length; ++i) + draw3D(f,s.s[i],surfacepen[i],light); + pen 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) + draw(f,s.s[k].uequals(i*step),meshpen,meshlight); + step=nv == 0 ? 0 : 1/nv; + for(int j=0; j <= nv; ++j) + draw(f,s.s[k].vequals(j*step),meshpen,meshlight); + } + } + } else { + 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.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, + 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,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) +{ + if(s.empty()) return; + + 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); + } else if(pic != null) + pic.add(new void(frame f, transform T) { + draw(T,f,S,nu,nv,surfacepen,meshpen,light,meshlight,P); + },true); + if(pic != null) { + 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) +{ + material[] surfacepen={surfacepen}; + pen[] meshpen={meshpen}; + surfacepen.cyclic(true); + meshpen.cyclic(true); + draw(pic,s,nu,nv,surfacepen,meshpen,light,meshlight); +} + +void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1, + material[] surfacepen, pen meshpen, + light light=currentlight, light meshlight=light) +{ + pen[] meshpen={meshpen}; + meshpen.cyclic(true); + draw(pic,s,nu,nv,surfacepen,meshpen,light,meshlight); +} + +surface extrude(path p, triple axis=Z) +{ + static patch[] allocate; + path3 G=path3(p); + path3 G2=shift(axis)*G; + return surface(...sequence(new patch(int i) { + return patch(subpath(G,i,i+1)--subpath(G2,i+1,i)--cycle); + },length(G))); +} + +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) 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) 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) +{ + surface s=surface(texpath(L)); + return L.align.is3D ? align(s,L.T3,position,L.align.dir3,L.p) : + shift(position)*L.T3*s; +} + +path[] path(Label L, pair z=0, projection P) +{ + path[] g=texpath(L); + if(L.defaulttransform3) { + return L.align.is3D ? align(g,z,project(L.align.dir3,P)-project(O,P),L.p) : + shift(z)*g; + } else { + path3[] G=path3(g); + return L.align.is3D ? shift(z)*project(align(G,L.T3,O,L.align.dir3,L.p),P) : + shift(z)*project(L.T3*G,P); + } +} + +void label(frame f, Label L, triple position, align align=NoAlign, + pen p=currentpen, light light=nolight, + projection P=currentprojection) +{ + Label L=L.copy(); + L.align(align); + L.p(p); + if(L.defaulttransform3) + L.T3=transform3(P); + if(is3D()) { + for(patch S : surface(L,position).s) + draw3D(f,S,L.p,light); + } else { + if(L.filltype == NoFill) + fill(f,path(L,project(position,P.t),P), + light.color(L.T3*Z,L.p,shiftless(P.modelview()))); + else { + frame d; + fill(d,path(L,project(position,P.t),P), + light.color(L.T3*Z,L.p,shiftless(P.modelview()))); + add(f,d,L.filltype); + } + } +} + +void label(picture pic=currentpicture, Label L, triple position, + align align=NoAlign, pen p=currentpen, light light=nolight) +{ + Label L=L.copy(); + L.align(align); + L.p(p); + L.position(0); + path[] g=texpath(L); + if(g.length == 0 || (g.length == 1 && size(g[0]) == 0)) return; + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + // Handle relative projected 3D alignments. + Label L=L.copy(); + if(!align.is3D && L.align.relative && L.align.dir3 != O && + determinant(P.t) != 0) + L.align(L.align.dir*unit(project(L.align.dir3,P.t))); + + triple v=t*position; + if(L.defaulttransform3) + L.T3=transform3(P); + if(is3D()) + for(patch S : surface(L,v).s) + draw3D(f,S,L.p,light); + if(pic != null) { + if(L.filltype == NoFill) + fill(project(v,P.t),pic,path(L,P), + light.color(L.T3*Z,L.p,shiftless(P.modelview()))); + else { + picture d; + fill(project(v,P.t),d,path(L,P), + light.color(L.T3*Z,L.p,shiftless(P.modelview()))); + add(pic,d,L.filltype); + } + } + },!L.defaulttransform3); + + 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) +{ + 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)); +} + +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; + +private real a=4/3*(sqrt(2)-1); +private transform3 t=rotate(90,O,Z); +private transform3 t2=t*t; +private transform3 t3=t2*t; +private transform3 i=xscale3(-1)*zscale3(-1); + +restricted patch octant1=patch(X{Z}..{-X}Z..Z{Y}..{-Z}Y{X}..{-Y}cycle, + new triple[] {(1,a,a),(a,a^2,1),(a^2,a,1), + (a,1,a)}); + +restricted surface unithemisphere=surface(octant1,t*octant1,t2*octant1, + t3*octant1); +restricted surface unitsphere=surface(octant1,t*octant1,t2*octant1,t3*octant1, + i*octant1,i*t*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)--interp(Z,X,t1){Y}..{-X}interp(Z,Y,t1)-- + interp(Z,Y,t2){X}..{-Y}cycle, + new triple[] {(s2,s2*a,1-s2),(s1,s1*a,1-s1),(s1*a,s1,1-s1), + (s2*a,s2,1-s2)}); +} + +// 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]=t*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--X+Z{Y}..{-X}Y+Z--Y{X}..{-Y}cycle); + +restricted surface unitcylinder=surface(unitcylinder1,t*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, projection P=currentprojection) +{ + pen q=(pen) p; + if(is3D()) { + material m=material(p,p.granularity >= 0 ? p.granularity : dotgranularity); + for(patch s : unitsphere.s) + draw3D(f,shift(v)*scale3(0.5*linewidth(dotsize(q)+q))*s,m,light); + } else dot(f,project(v,P.t),q); +} + +void dot(frame f, path3 g, material p=currentpen, + projection P=currentprojection) +{ + for(int i=0; i <= length(g); ++i) dot(f,point(g,i),p,P); +} + +void dot(frame f, path3[] g, material p=currentpen, + projection P=currentprojection) +{ + for(int i=0; i < g.length; ++i) dot(f,g[i],p,P); +} + +void dot(picture pic=currentpicture, triple v, material p=currentpen, + light light=nolight) +{ + pen q=(pen) p; + real size=0.5*linewidth(dotsize(q)+q); + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + if(is3D()) { + material m=material(p,p.granularity >= 0 ? p.granularity : + dotgranularity); + for(patch s : unitsphere.s) + draw3D(f,shift(t*v)*scale3(size)*s,m,light); + } + if(pic != null) + dot(pic,project(t*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) +{ + for(int i=0; i < v.length; ++i) dot(pic,v[i],p); +} + +void dot(picture pic=currentpicture, explicit path3 g, material p=currentpen) +{ + for(int i=0; i <= length(g); ++i) dot(pic,point(g,i),p); +} + +void dot(picture pic=currentpicture, path3[] g, material p=currentpen) +{ + for(int i=0; i < g.length; ++i) dot(pic,g[i],p); +} + +void dot(picture pic=currentpicture, Label L, triple v, align align=NoAlign, + string format=defaultformat, material p=currentpen) +{ + 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); + label(pic,L,v); +} diff --git a/Master/texmf/asymptote/tree.asy b/Master/texmf/asymptote/tree.asy new file mode 100644 index 00000000000..0f3559112c3 --- /dev/null +++ b/Master/texmf/asymptote/tree.asy @@ -0,0 +1,86 @@ +/***** + * treedef.asy + * Andy Hammerlindl 2003/10/25 + * + * Implements a dynamic binary search tree. + *****/ + +struct tree +{ + tree left = null; + tree right = null; + 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/asymptote/trembling.asy b/Master/texmf/asymptote/trembling.asy new file mode 100644 index 00000000000..fc50ce9b7f5 --- /dev/null +++ b/Master/texmf/asymptote/trembling.asy @@ -0,0 +1,367 @@ +// Copyright (c) 2008, Philippe Ivaldi. +// 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: + +import geometry; +import stats; + +/*<asyxml><variable type="pair[]" signature="magneticPoints"><code></asyxml>*/ +pair[] magneticPoints; +/*<asyxml></code><documentation>Array of magnetic points. + When a point is magnetized, a trembled path will pass though P if the original path passes through P. + When trembling is enabled with the routine 'startTrembling' all the drawn points is added to this array. + For convenience, one can magnetize an arbitrary number of points with the routine 'magnetize'.<look href="#magnetize(...point[])"><look href="#startTrembling(real,real,real,real,bool"> + </documentation></variable></asyxml>*/ +real magneticRadius=1; // unit is bp in postscript coordinates. +real trembleFuzz(){return min(1e-3,magneticRadius/10);} +// real trembleFuzz(){return min(1e-1,1);} + +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 nearest point of 'm' which is on the path g. + '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; + // do { + // do {// Find a radius for intersection + // r += step; + // ig=T*scale(r)*unitcircle; + // if(ig == g) { + // T *= shift(shx,0); + // ig=T*scale(r)*unitcircle; + // } + // t=intersect(ig,g); + // } while(t.length <= 0 && r <= maxR && inside(g,ig) != 1);// + // if(t.length <= 0) { // degenerated case + // r=initr; + // T *= shift(shx,0); + // write("warning: atime needs numerical adjustment."); + // } + // } while(t.length <= 0); + 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="void" signature="magnetize(...pair[])"><code></asyxml>*/ +void magnetize(...pair[] P) +{/*<asyxml></code><documentation>Magnetize the points P. + When a point is magnetized, a trembled path will pass though P if the original path passes through P.</documentation></function></asyxml>*/ + if(magneticRadius <= 0) return; + currentpicture.uptodate=false; + currentpicture.nodes.insert(0, + new void(frame f, transform t, transform T, pair lb, pair rt) + { + for (pair PT:P) { + magneticPoints.push(t*PT); + } + }); +} + +/*<asyxml><function type="void" signature="magnetize(...triangle[])"><code></asyxml>*/ +void magnetize(...triangle[] t) +{/*<asyxml></code><documentation>Magnetize the vertices of the triangles t.<look href="#magnetize(...pair[])"/></documentation></function></asyxml>*/ + for(int i=0; i < t.length; ++i) + magnetize(...new point[] {t[i].A, t[i].B, t[i].C}); +} + +void trueMagnetize(pair p) +{ + if(magneticRadius <= 0) return; + magneticPoints.push(p); +} + +/*<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); + og=og..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><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>*/ + +/*<asyxml><function type="path" signature="tremble(path,real,real,real,real)"><code></asyxml>*/ +path tremble(path g, + real angle=trembleAngle, + real frequency=trembleFrequency, + real random=trembleRandom, + real fuzz=trembleFuzz()) +{/*<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 'angle'. + random is 0 means don't use randomized coefficient; + More 'random' is hight more the coefficient is hight and the trembling seems randomized.</documentation></function></asyxml>*/ + if(length(g) == 0) return g; + 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(), a=first; + real gle; + for(int i=1; i <= l; ++i) + { + pair P=point(tg,i); + // gle=(i == l && (cyclic(tg))) ? first : a; + // pair post=rotate(gle,point(tg,i-1))*postcontrol(tg,i-1); + // pair pre=rotate(gle,P)*precontrol(tg,i); + 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; +} + +typedef path pathModifier(path); +pathModifier NoModifier=new path(path g){return g;}; +pathModifier tremble(real angle=trembleAngle, + real frequency=trembleFrequency, + real random=trembleRandom, + real fuzz=trembleFuzz()) +{ + return new path(path g){return tremble(g,angle,frequency,random,fuzz);}; +} + +void orig_draw(frame f, path g, pen p=currentpen)=draw; +void tremble_draw(frame f, path g, pen p=currentpen){} + +void tremble_marknodes(picture pic=currentpicture, frame f, path g) {} +void tremble_markuniform(bool centered=false, int n, bool rotated=false) {} + +int tremble_circlenodesnumber(real r){return 5;} +int orig_circlenodesnumber(real r)=circlenodesnumber; + +int tremble_circlenodesnumber1(real r, real angle1, real angle2){return 4;} +int orig_circlenodesnumber1(real r, real angle1, real angle2)=circlenodesnumber; + +int tremble_ellipsenodesnumber(real a, real b){return 50;} +int orig_ellipsenodesnumber(real a, real b)=ellipsenodesnumber; + +int tremble_ellipsenodesnumber1(real a, real b, real angle1, real angle2, bool dir){return 20;} +int orig_ellipsenodesnumber1(real a, real b, real angle1, real angle2, bool dir)=ellipsenodesnumber; + +int tremble_parabolanodesnumber(parabola p, real angle1, real angle2){return 20;} +int orig_parabolanodesnumber(parabola p, real angle1, real angle2)=parabolanodesnumber; + +int tremble_hyperbolanodesnumber(hyperbola h, real angle1, real angle2){return 20;} +int orig_hyperbolanodesnumber(hyperbola h, real angle1, real angle2)=hyperbolanodesnumber; + +restricted bool tremblingMode=false; +/*<asyxml><function type="void" signature="startTrembling(real,real,real,real,bool"><code></asyxml>*/ +void startTrembling(real angle=trembleAngle, + real frequency=trembleFrequency, + real random=trembleRandom, + real fuzz=trembleFuzz(), + bool magnetizePoints=true) +{/*<asyxml></code><documentation>Calling this routine all drawn paths will be trembled with the givens parameters.<look href="#tremble(path,real,real,real,real)"/> + If 'magnetizePoints' is true, most dotted points are automatically magnetized.<look href="magnetize(...pair[])"/></documentation></function></asyxml>*/ + if(!tremblingMode) { + tremblingMode=true; + tremble_draw=new void(frame f, path g, pen p=currentpen) + { + if(length(g) == 0 && magnetizePoints) { + trueMagnetize(point(g,0)); + } + g=addnodes(g,fuzz*abs(max(g)-min(g)) ...magneticPoints); + orig_draw(f,tremble(g,angle,frequency,random,fuzz),p); + }; + + plain.draw=tremble_draw; + + if(magnetizePoints) { + marknodes=new void(picture pic=currentpicture, frame f, path g) + { + for(int i=0; i <= length(g); ++i) { + add(pic,f,point(g,i)); + magnetize(point(g,i)); + } + }; + dot=dot(); + + markuniform=new markroutine(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)); + magnetize(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); + } + } + }; + }; + } + + circlenodesnumber=tremble_circlenodesnumber; + circlenodesnumber=tremble_circlenodesnumber1; + + ellipsenodesnumber=tremble_ellipsenodesnumber; + ellipsenodesnumber=tremble_ellipsenodesnumber1; + + parabolanodesnumber=tremble_parabolanodesnumber; + hyperbolanodesnumber=tremble_hyperbolanodesnumber; + } +} + +// void stopTrembling() +// { +// if(tremblingMode) { +// tremblingMode=false; +// plain.draw=orig_draw; +// // draw=orig_draw1; +// circlenodesnumber=orig_circlenodesnumber; +// circlenodesnumber=orig_circlenodesnumber1; +// ellipsenodesnumber=orig_ellipsenodesnumber; +// ellipsenodesnumber=orig_ellipsenodesnumber1; +// parabolanodesnumber=orig_parabolanodesnumber; +// hyperbolanodesnumber=orig_hyperbolanodesnumber; +// } +// } diff --git a/Master/texmf/asymptote/tube.asy b/Master/texmf/asymptote/tube.asy new file mode 100644 index 00000000000..f62d3897adf --- /dev/null +++ b/Master/texmf/asymptote/tube.asy @@ -0,0 +1,210 @@ +// Author: Philippe Ivaldi +// 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 guideclosed=cyclic(A); + if(len < 2) {return A;}; + if(guideclosed) {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(guideclosed) { + 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; +} + +struct Rmf +{ + triple p,r,t; // s=cross(t,r); + real reltime; + void operator init(triple p, triple r, triple t, real reltime) + { + this.p=p; + this.r=r; + this.t=t; + this.reltime=reltime; + } +} + +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; +} + +private Rmf[] rmf(path3 g, Rmf U0=Rmf(O,O,O,0), real[] t) +{ + if(U0.t == O) { + triple d=dir(g,0); + U0=Rmf(point(g,0),perp(d),d,0); + } + int l=length(g); + Rmf[] R={U0}; + triple rp,v1,v2,tp,ti,p; + real c; + + for(int i=0; i < t.length-1; ++i) { + p=point(g,t[i+1]); + v1=p-R[i].p; + c=dot(v1,v1); + if(c != 0) { + rp=R[i].r-2*dot(v1,R[i].r)*v1/c; + ti=R[i].t; + tp=ti-2*dot(v1,ti)*v1/c; + ti=dir(g,t[i+1]); + v2=ti-tp; + rp=rp-2*dot(v2,rp)*v2/dot(v2,v2); + R.push(Rmf(p,unit(rp),unit(ti),t[i+1]/l)); + } else { + write("Warning: path3 has duplicated point in Rmf."); + R.push(R[R.length-1]); + } + } + return R; +} + +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, coloredpath cp,transform T(real)= + new transform(real t) {return identity();}, + 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(R[0].reltime)*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,cross(R[0].t,R[0].r),R[0].t)*sec, + sec2; + + for(int i=1; i < R.length; ++i) { + sec=path3(T(R[i].reltime)*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(R[i-1].reltime), tp2=cp.pens(R[i].reltime); + 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)); + t.cyclic(cyclic(g)); + return surface(rmf(g,t),section,T,cyclic(g)); +} diff --git a/Master/texmf/asymptote/unicode.asy b/Master/texmf/asymptote/unicode.asy new file mode 100644 index 00000000000..33571e8341a --- /dev/null +++ b/Master/texmf/asymptote/unicode.asy @@ -0,0 +1,2 @@ +usepackage("ucs"); +usepackage("inputenc","utf8x"); diff --git a/Master/texmf/asymptote/version.asy b/Master/texmf/asymptote/version.asy new file mode 100644 index 00000000000..d9427dad87e --- /dev/null +++ b/Master/texmf/asymptote/version.asy @@ -0,0 +1 @@ +string VERSION="1.75"; diff --git a/Master/texmf/asymptote/x11colors.asy b/Master/texmf/asymptote/x11colors.asy new file mode 100644 index 00000000000..77e0401f06e --- /dev/null +++ b/Master/texmf/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); diff --git a/Master/texmf/doc/info/asy-faq.info b/Master/texmf/doc/info/asy-faq.info new file mode 100644 index 00000000000..5d56d06d3c5 --- /dev/null +++ b/Master/texmf/doc/info/asy-faq.info @@ -0,0 +1,1670 @@ +Info file: asy-faq.info, -*-Text-*- +produced by bfnnconv.pl from the Bizarre Format With No Name. + +START-INFO-DIR-ENTRY +* asymptote FAQ: (asy-faq). Asymptote Frequently Asked Questions. +END-INFO-DIR-ENTRY + + +File: $prefix.info, Node: Top, Next: Question 1.1, Up: (dir) + + ASYMPTOTE FREQUENTLY ASKED QUESTIONS + 01 Jun 2009 + +This is the list of Frequently Asked Questions about Asymptote (asy). + +Index + +* Menu: +* Section 1:: About Asymptote +* Section 2:: Questions about installation and setup +* Section 3:: Questions about paths +* Section 4:: Questions about labels +* Section 5:: Questions about arrows +* Section 6:: Questions about 2D graphs +* Section 7:: Questions about programming +* Section 8:: Questions about differences between Asymptote and + MetaPost +* Section 9:: Questions about output + +Section 1, About Asymptote +* Question 1.1:: What is Asymptote? +* Question 1.2:: How do I obtain Asymptote? +* Question 1.3:: Where can I ask questions about Asymptote? +* Question 1.4:: Why was the name Asymptote chosen? +* Question 1.5:: In the internal Asymptote source code, what does the + name camp refer to? + +Section 2, Questions about installation and setup +* Question 2.1:: Is it possible to install Asymptote on Mac OS X? +* Question 2.2:: Why do I get the error Bad CPU type in executable on + installing Asymptote from the MAC OS binary? +* Question 2.3:: What do I do if I get the error: Error: pdfetex (file + pdftex.cfg): cannot open config file...texinfo.tex + appears to be broken? +* Question 2.4:: What do I do if I get the error: ! Undefined control + sequence. l.6 @copying? +* Question 2.5:: Is it possible to integrate Asymptote into LaTeX? +* Question 2.6:: Is it possible to integrate Asymptote into latex or + pdflatex? +* Question 2.7:: Do I need the tkinter package to install an Asymptote + rpm binary? +* Question 2.8:: What does the path %USERPROFILE%\.asy\config.asy mean? +* Question 2.9:: Why do I get the error "string not terminated" when I + try to set settings.dir="C:\asymptote\";? +* Question 2.10:: How do I change environment variables in Microsoft + Windows, for example, in order to change the default + PostScript viewer? +* Question 2.11:: Under Microsoft Windows XP, why do I get an error like + "Invalid Parameter - 432x432"? +* Question 2.12:: Why does Asymptote freeze upon trying to draw a label + with my MikTex installation under Microsoft Windows? + +Section 3, Questions about paths +* Question 3.1:: Why do I get a syntax error message when I specify an + integer value for the path tension? +* Question 3.2:: Shouldn't dots always be the same size? + +Section 4, Questions about labels +* Question 4.1:: How do I get Greek letters like omega to show up in my + labels? +* Question 4.2:: Can Asymptote use matrices as labels? +* Question 4.3:: How do I tell Asymptote to load a particular LaTeX + package, like mathptmx? +* Question 4.4:: How can I use international fonts in Asymptote labels? +* Question 4.5:: How can I use Fourier fonts? +* Question 4.6:: Is there any way to change the default appearance of + the decimal separator, using a comma instead of a dot? +* Question 4.7:: How can I get a rotated label with the filled box + rotated as well so that it fits the text? +* Question 4.8:: How can I rotate labels in a 3D figure? +* Question 4.9:: How can I draw some squares and circles of a fixed + size and put a label in the middle of them? +* Question 4.10:: The binary operator * can be used to scale the color + of a pen by a real number. Does this scaling factor + have to be less than 1? +* Question 4.11:: Why is the space after the comma decimal separator in + my locale so large? +* Question 4.12:: How can I prevent + texpreamble("\usepackage[pdftex]{hyperref}") from + changing the page size? + +Section 5, Questions about arrows +* Question 5.1:: How do I draw two arrows at arbitrary positions along + a path? +* Question 5.2:: How do I reverse the direction of an arrowhead? +* Question 5.3:: How do I change the size of all arrows? +* Question 5.4:: Can I create other arrowhead styles? + +Section 6, Questions about 2D graphs +* Question 6.1:: How can I draw x axis ticks on the right side, with + the tick labels on the left side (relative to the axis + path)? +* Question 6.2:: How can I reposition the x axis label to + three-quarters along the axis length? +* Question 6.3:: How can I move the x axis label down 10bp? +* Question 6.4:: Can I use different pens for the axis, the axis label, + and the tick labels? +* Question 6.5:: How can I change the font type of the axes label? +* Question 6.6:: How can I change the font type of the tick labels on + an axis? +* Question 6.7:: How can I prevent axes tick labels from rendering on + top of each other? +* Question 6.8:: How do I make the plot region of a graph, ignoring + labels and legends, have a fixed size? +* Question 6.9:: How can I plot a function f(x) within [0,1]x[0,2] + without explicitly calculating the x values for which + f(x) hits the boundary? +* Question 6.10:: Is it possible to define customized palettes? +* Question 6.11:: Is there an easy way to graph factorial functions + nicely? +* Question 6.12:: How do I indicate that a certain length should be + exactly the size I prescribe with no rescaling, within + a picture which has its own size? +* Question 6.13:: How can I make the y axis display base-2 logarithmic + values? +* Question 6.14:: How can I align the x axes of two graphs on the same + figure? +* Question 6.15:: How can I change the direction of the y-axis, such + that negatives values are on the upper y-axis? +* Question 6.16:: How can I fill a path with a function that defines the + color of each location? +* Question 6.17:: Is there a way to draw a function that is not + explicitly given, such as (y - 2)^2 = x - 1 ? +* Question 6.18:: Is it possible to reverse or stretch an axis? +* Question 6.19:: Why can't I use the UnFill option to draw graphs with + empty markers? +* Question 6.20:: How can I force several images to use the same palette + range (e.g. the entire 0-255 grayscale range)? + +Section 7, Questions about programming +* Question 7.1:: Is Asymptote an interpreter or a compiler? +* Question 7.2:: What is the difference between a frame and a picture? +* Question 7.3:: What is the difference between a path and a guide? +* Question 7.4:: What is a convenient way to declare and initialize an + array of pictures? +* Question 7.5:: Is there a way to define functions that act on arrays + in general (i.e. work for arrays of any type)? +* Question 7.6:: Is there any way to declare structures ahead of their + definition, e.g. where struct A performs some + operation on struct B, but B contains an A member? +* Question 7.7:: Where are static variables in for loops allocated? +* Question 7.8:: Is there a debugger for asy? +* Question 7.9:: Do you accept patches for Asymptote? + +Section 8, Questions about differences between Asymptote and + MetaPost +* Question 8.1:: What is the equivalent of the MetaPost c[a,b] + interpolation operator? +* Question 8.2:: How does picture scaling differ in Asymptote and + MetaPost? +* Question 8.3:: How can I avoid automatic scaling of a picture? +* Question 8.4:: What is the equivalent of MetaPost ... command? +* Question 8.5:: What is the equivalent of the MetaPost pickup command? +* Question 8.6:: What is the equivalent of the MetaPost whatever + command? +* Question 8.7:: What is the equivalent for the MetaPost command for + lray - horiz*v - verti*u = whatever*(LightSource - R), + a system of three linear equations for three unknowns: + horiz, verti, whatever? +* Question 8.8:: In MetaPost, it is possible to have a drawing remain + the same size in different pictures by defining a unit + u and explicitly multiply all the coordinates by u. Is + there a better way to do this in Asymptote? +* Question 8.9:: In MetaPost, one could produce tiling pictures by + generating a picture, and then clipping the picture to + a rectangle of fixed dimensions around the center of + the picture. How is that done in Asymptote? + +Section 9, Questions about output +* Question 9.1:: How can I disable automatic invocation of the PS + viewer after an asy file is done processing? +* Question 9.2:: How do I output jpeg images? +* Question 9.3:: Can I embed bitmaps (photos) into my drawings and + position and scale them? +* Question 9.4:: Does Asymptote support direct PDF output? +* Question 9.5:: How to I produce large pictures of high quality in + raster format (e.g. png, giff etc). +* Question 9.6:: Is it possible to produce multi-page documents with + asymptote? + + + +File: asy-faq.info, Node: Section 1, Next: Section 2, Previous: Top, Up: Top + +About Asymptote + +* Menu: +* Question 1.1:: What is Asymptote? +* Question 1.2:: How do I obtain Asymptote? +* Question 1.3:: Where can I ask questions about Asymptote? +* Question 1.4:: Why was the name Asymptote chosen? +* Question 1.5:: In the internal Asymptote source code, what does the + name camp refer to? + + + +File: asy-faq.info, Node: Question 1.1, Next: Question 1.2, Previous: Top, Up: Section 1 + +Question 1.1. What is Asymptote? + +Asymptote is a vector graphics language designed for technical graphics, +inspired by MetaPost but with IEEE floating-point numerics, native +three-dimensional graphics, Grayscale/RGB/CMYK colourspaces, and a +C++-like syntax. Unlike MetaPost, it natively supports multiple-segment +paths (and hence regions other than simply connected ones), tiling +patterns, Gouraud shading, tensor patch shading, and PostScript images. + + +File: asy-faq.info, Node: Question 1.2, Next: Question 1.3, Previous: Question 1.1, Up: Section 1 + +Question 1.2. How do I obtain Asymptote? + +Binary releases are available for Linux, MacOS X, and Microsoft Windows +platforms, in addition to full source code, from the website +http://asymptote.sourceforge.net/. Many Linux distributions (such as +RedHat and Debian) now include an Asymptote package (check your +distribution's documentation for further information about this). + + +File: asy-faq.info, Node: Question 1.3, Next: Question 1.4, Previous: Question 1.2, Up: Section 1 + +Question 1.3. Where can I ask questions about Asymptote? + +If you have a question, please try to find an answer in this FAQ, in the +extensive Asymptote documentation at +http://asymptote.sourceforge.net/doc/, or search the forum: +http://sourceforge.net/forum/forum.php?forum_id=409349. + + +File: asy-faq.info, Node: Question 1.4, Next: Question 1.5, Previous: Question 1.3, Up: Section 1 + +Question 1.4. Why was the name Asymptote chosen? + +Well, it isn't the perfect graphics package, but we do think it is getting +there asymptotically... + + +File: asy-faq.info, Node: Question 1.5, Next: Question 2.1, Previous: Question 1.4, Up: Section 1 + +Question 1.5. In the internal Asymptote source code, what does the name camp refer to? + +That was our original tentative name for this project, which stood for +"C's Answer to MetaPost" (the language that inspired Asymptote). However, +we eventually decided that the name Asymptote better emphasizes the +mathematical and graphical nature of this language. + + +File: asy-faq.info, Node: Section 2, Next: Section 3, Previous: Section 1, Up: Top + +Questions about installation and setup + +* Menu: +* Question 2.1:: Is it possible to install Asymptote on Mac OS X? +* Question 2.2:: Why do I get the error Bad CPU type in executable on + installing Asymptote from the MAC OS binary? +* Question 2.3:: What do I do if I get the error: Error: pdfetex (file + pdftex.cfg): cannot open config file...texinfo.tex + appears to be broken? +* Question 2.4:: What do I do if I get the error: ! Undefined control + sequence. l.6 @copying? +* Question 2.5:: Is it possible to integrate Asymptote into LaTeX? +* Question 2.6:: Is it possible to integrate Asymptote into latex or + pdflatex? +* Question 2.7:: Do I need the tkinter package to install an Asymptote + rpm binary? +* Question 2.8:: What does the path %USERPROFILE%\.asy\config.asy mean? +* Question 2.9:: Why do I get the error "string not terminated" when I + try to set settings.dir="C:\asymptote\";? +* Question 2.10:: How do I change environment variables in Microsoft + Windows, for example, in order to change the default + PostScript viewer? +* Question 2.11:: Under Microsoft Windows XP, why do I get an error like + "Invalid Parameter - 432x432"? +* Question 2.12:: Why does Asymptote freeze upon trying to draw a label + with my MikTex installation under Microsoft Windows? + + + +File: asy-faq.info, Node: Question 2.1, Next: Question 2.2, Previous: Question 1.5, Up: Section 2 + +Question 2.1. Is it possible to install Asymptote on Mac OS X? + +It is easy to compile Asymptote directly from the source code at +http://sourceforge.net/project/showfiles.php?group_id=120000 We recommend +first upgrading to the latest GNU readline library, unless you don't care +about interactive readline support (in which case configure will +automatically detect and disable obsolete versions of the readline +library). Marius Schamschula also maintains a binary package for various +MacOS X platforms +http://www.hmug.org/pub/MacOS_X/X/Applications/Publishing/asymptote. + + +File: asy-faq.info, Node: Question 2.2, Next: Question 2.3, Previous: Question 2.1, Up: Section 2 + +Question 2.2. Why do I get the error Bad CPU type in executable on installing Asymptote from the MAC OS binary? + +This means either that you have a binary distribution for another MAC +architecture, or (according to Marius Schamschula) that you may have a +missing library. The simplest solution is to compile Asymptote directly +from the official source: +http://sourceforge.net/project/showfiles.php?group_id=120000. + + +File: asy-faq.info, Node: Question 2.3, Next: Question 2.4, Previous: Question 2.2, Up: Section 2 + +Question 2.3. What do I do if I get the error: Error: pdfetex (file pdftex.cfg): cannot open config file...texinfo.tex appears to be broken? + +Simply put http://asymptote.sourceforge.net/asymptote.pdf in the directory +doc and repeat the command make all. Or, if you don't want to build a +local copy of the documentation, simply proceed with make install-asy. + + +File: asy-faq.info, Node: Question 2.4, Next: Question 2.5, Previous: Question 2.3, Up: Section 2 + +Question 2.4. What do I do if I get the error: ! Undefined control sequence. l.6 @copying? + +Either upgrade your texinfo package or follow one of the easy work arounds +in *Note Question 2.3:: `What do I do if I get the error: Error: pdfetex +(file pdftex.cfg): cannot open config file...texinfo.tex appears to be +broken?'. + + +File: asy-faq.info, Node: Question 2.5, Next: Question 2.6, Previous: Question 2.4, Up: Section 2 + +Question 2.5. Is it possible to integrate Asymptote into LaTeX? + +Yes, see the example latexusage.tex. Dario Teixeira has also written a +detailed guide on the topic. You can download it from +http://dario.dse.nl/projects/asylatex/. + +Philippe Ivaldi has contributed an Asymptote mode for Emacs users +http://asymptote.sourceforge.net/doc/Editing-modes.html, which includes a +lasy-mode that allows one to compile and view the output of one +\begin{asy}...\end{asy} section at a time. + + +File: asy-faq.info, Node: Question 2.6, Next: Question 2.7, Previous: Question 2.5, Up: Section 2 + +Question 2.6. Is it possible to integrate Asymptote into latex or pdflatex? + +Yes, as of version 1.14, Asymptote supports latex and pdflatex (both in +EPS/PDF and inline mode), as illustrated by the example latexusage.tex: + +pdflatex latexusage +asy latexusage +pdflatex latexusage + +File: asy-faq.info, Node: Question 2.7, Next: Question 2.8, Previous: Question 2.6, Up: Section 2 + +Question 2.7. Do I need the tkinter package to install an Asymptote rpm binary? + +No, you don't need tkinter unless you want to try out the GUI xasy. Try + +rpm -Uvh --nodeps asymptote-x.xx-1.i386.rpm +where x.xx represents the version number. + + +File: asy-faq.info, Node: Question 2.8, Next: Question 2.9, Previous: Question 2.7, Up: Section 2 + +Question 2.8. What does the path %USERPROFILE%\.asy\config.asy mean? + +That is the way that Microsoft Windows refers to the user profile +directory. There's nothing really to understand here, just put your +configuration commands in the file config.asy in a new folder +%USERPROFILE%\.asy. + + +File: asy-faq.info, Node: Question 2.9, Next: Question 2.10, Previous: Question 2.8, Up: Section 2 + +Question 2.9. Why do I get the error "string not terminated" when I try to set settings.dir="C:\asymptote\";? + +The backslash is an escape character here, so \" is interpreted as a +verbatim quotation mark, leaving the string without a terminating +quotation mark. Fortunately, this is the only escaped character in +double-quoted strings. A final backslash isn't needed here anyway, but +should you really want one somewhere, you can say: +settings.dir="C:\asymptote"+'\\';. + + +File: asy-faq.info, Node: Question 2.10, Next: Question 2.11, Previous: Question 2.9, Up: Section 2 + +Question 2.10. How do I change environment variables in Microsoft Windows, for example, in order to change the default PostScript viewer? + +While it is easier to set the corresponding Asymptote configuration +variable in your config.asy file, here is the procedure for changing +Microsoft Windows environment variables: + +Click on the [Start] button + +* RIGHT-click on 'My Computer' + +* Choose 'Properties' from the popup menu + +* Click the 'Advanced' tab + +* Click the 'Environment Variables' button. + + +File: asy-faq.info, Node: Question 2.11, Next: Question 2.12, Previous: Question 2.10, Up: Section 2 + +Question 2.11. Under Microsoft Windows XP, why do I get an error like "Invalid Parameter - 432x432"? + +This means that ImageMagick wasn't properly installed and you are using +the MSDOS convert program rather than the ImageMagick one. Or you may have +installed ImageMagick but ran Asymptote from an existing MSDOS window. In +that case, simply open a new window and try again. If that doesn't work, +check that + +convert --version +returns something like + +Version: ImageMagick 6.2.8 06/27/06 Q16 http://www.imagemagick.org + +File: asy-faq.info, Node: Question 2.12, Next: Question 3.1, Previous: Question 2.11, Up: Section 2 + +Question 2.12. Why does Asymptote freeze upon trying to draw a label with my MikTex installation under Microsoft Windows? + +Likely, this means that latex and dvips are not in your default path. Try +adding the appropriate paths in your config.asy file, for example: + +import settings; +latex="C:\Program Files\MiKTeX 2.7\miktex\bin\latex.exe"; +dvips="C:\Program Files\MiKTeX 2.7\miktex\bin\dvips.exe"; + +File: asy-faq.info, Node: Section 3, Next: Section 4, Previous: Section 2, Up: Top + +Questions about paths + +* Menu: +* Question 3.1:: Why do I get a syntax error message when I specify an + integer value for the path tension? +* Question 3.2:: Shouldn't dots always be the same size? + + + +File: asy-faq.info, Node: Question 3.1, Next: Question 3.2, Previous: Question 2.12, Up: Section 3 + +Question 3.1. Why do I get a syntax error message when I specify an integer value for the path tension? + +What is happening here is that + +draw((0,0)..tension 2..(0,50)..(100,100)); +is read as + +draw((0,0)..tension 2. .(0,50)..(100,100)); +So the first . after the two is treated as a decimal point. Just put a +space after the integer tension value: + +draw((0,0)..tension 2 ..(0,50)..(100,100)); + +File: asy-faq.info, Node: Question 3.2, Next: Question 4.1, Previous: Question 3.1, Up: Section 3 + +Question 3.2. Shouldn't dots always be the same size? + +From the documentation: + +"The dot command defined in the module plain draws a dot having a diameter +equal to an explicit pen linewidth or the default linewidth magnified by +dotfactor (6 by default)." + +Thus, when you use the default pen, the dot will have size 6*linewidth, +but when you give a pen with an explicit width specified, you will have a +dot of size linewidth. If you want the first case to behave like the +second, you may set dotfactor=1. + + +File: asy-faq.info, Node: Section 4, Next: Section 5, Previous: Section 3, Up: Top + +Questions about labels + +* Menu: +* Question 4.1:: How do I get Greek letters like omega to show up in my + labels? +* Question 4.2:: Can Asymptote use matrices as labels? +* Question 4.3:: How do I tell Asymptote to load a particular LaTeX + package, like mathptmx? +* Question 4.4:: How can I use international fonts in Asymptote labels? +* Question 4.5:: How can I use Fourier fonts? +* Question 4.6:: Is there any way to change the default appearance of + the decimal separator, using a comma instead of a dot? +* Question 4.7:: How can I get a rotated label with the filled box + rotated as well so that it fits the text? +* Question 4.8:: How can I rotate labels in a 3D figure? +* Question 4.9:: How can I draw some squares and circles of a fixed + size and put a label in the middle of them? +* Question 4.10:: The binary operator * can be used to scale the color + of a pen by a real number. Does this scaling factor + have to be less than 1? +* Question 4.11:: Why is the space after the comma decimal separator in + my locale so large? +* Question 4.12:: How can I prevent + texpreamble("\usepackage[pdftex]{hyperref}") from + changing the page size? + + + +File: asy-faq.info, Node: Question 4.1, Next: Question 4.2, Previous: Question 3.2, Up: Section 4 + +Question 4.1. How do I get Greek letters like omega to show up in my labels? + +In (La)TeX, Greek letters can be obtained in math mode by prepending a +backslash to the letter name. So for a omega symbol, use "$\omega$". +Everything between the dollar signs is considered to be a math formula. +Uppercase Greek letters can be used by capitalizing the first letter of +the name: + +label("$\omega$",(0,0)); +label("$\Omega$",(20,0)); + +File: asy-faq.info, Node: Question 4.2, Next: Question 4.3, Previous: Question 4.1, Up: Section 4 + +Question 4.2. Can Asymptote use matrices as labels? + +Yes: +usepackage("amsmath"); +label("$\begin{matrix} 1 & 2 \\\ 1 & 1 \end{matrix}$",(0,0)); + +File: asy-faq.info, Node: Question 4.3, Next: Question 4.4, Previous: Question 4.2, Up: Section 4 + +Question 4.3. How do I tell Asymptote to load a particular LaTeX package, like mathptmx? + +Put + +usepackage("mathptmx"); +at the beginning of your file. Note: to enable the Adobe Times Roman font +for text, you will also need to say: +defaultpen(TimesRoman()); + +File: asy-faq.info, Node: Question 4.4, Next: Question 4.5, Previous: Question 4.3, Up: Section 4 + +Question 4.4. How can I use international fonts in Asymptote labels? + +See http://asymptote.sourceforge.net/doc/unicode.html. + + +File: asy-faq.info, Node: Question 4.5, Next: Question 4.6, Previous: Question 4.4, Up: Section 4 + +Question 4.5. How can I use Fourier fonts? + +usepackage("fourier"); +defaultpen(font("T1","fut\textfamilyextension","m","n")); + +File: asy-faq.info, Node: Question 4.6, Next: Question 4.7, Previous: Question 4.5, Up: Section 4 + +Question 4.6. Is there any way to change the default appearance of the decimal separator, using a comma instead of a dot? + +Just set your locale appropriately: + +locale("it_IT"); +usepackage("icomma"); +label(format(0.5)); + +File: asy-faq.info, Node: Question 4.7, Next: Question 4.8, Previous: Question 4.6, Up: Section 4 + +Question 4.7. How can I get a rotated label with the filled box rotated as well so that it fits the text? + +frame f; +label(f,"This is some text",white,Fill(blue)); +add(rotate(65)*f); + +File: asy-faq.info, Node: Question 4.8, Next: Question 4.9, Previous: Question 4.7, Up: Section 4 + +Question 4.8. How can I rotate labels in a 3D figure? + +You need to first project the triple to a pair like this: + +import three; +size(100,100); + +draw(rotate(90,project(Z))*"A",O--X); + +File: asy-faq.info, Node: Question 4.9, Next: Question 4.10, Previous: Question 4.8, Up: Section 4 + +Question 4.9. How can I draw some squares and circles of a fixed size and put a label in the middle of them? + +Fixed-size objects should be drawn on a separate picture and then added to +currentpicture. Here is one way (see also +http://asymptote.sourceforge.net/gallery/subpictures.asy and +http://asymptote.sourceforge.net/gallery/mosquito.asy): + +real u=2cm; + +picture square; +draw(square,scale(u)*shift(-0.5,-0.5)*unitsquare); + +picture circle; +draw(circle,scale(0.5u)*unitcircle); + +void add(picture pic=currentpicture, Label L, picture object, pair z) { +add(pic,object,z); +label(pic,L,z); +} + +add("square",square,(0,0)); +add("circle",circle,(5cm,0)); + +File: asy-faq.info, Node: Question 4.10, Next: Question 4.11, Previous: Question 4.9, Up: Section 4 + +Question 4.10. The binary operator * can be used to scale the color of a pen by a real number. Does this scaling factor have to be less than 1? + +The scaling factor can be greater than 1. But keep in mind that the rgb +color components saturate at 1. + +Try +write(cyan); write(0.8*cyan); write(1.5*cyan); +and you will quickly see what is going on. + +To get a lighter cyan you can say white+cyan, which yields rgb(0.5,1,1). +If you want something even lighter specify the rgb colors directly, for +example, rgb(0.9,1,1). + +Alternatively, work in cmyk colour space, which is nicer in that it +handles saturation separately from hue: + +0.1*Cyan is light and 0.9*Cyan is dark. You can also say 0.1*cmyk(red). + + +File: asy-faq.info, Node: Question 4.11, Next: Question 4.12, Previous: Question 4.10, Up: Section 4 + +Question 4.11. Why is the space after the comma decimal separator in my locale so large? + +LaTeX is treating the comma as punctuation and not as a decimal separator. +The solution is to load the icomma package near the beginning of your +file: +usepackage("icomma"); + +File: asy-faq.info, Node: Question 4.12, Next: Question 5.1, Previous: Question 4.11, Up: Section 4 + +Question 4.12. How can I prevent texpreamble("\usepackage[pdftex]{hyperref}") from changing the page size? + +texpreamble("\usepackage[pdftex,setpagesize=false]{hyperref}"); + +File: asy-faq.info, Node: Section 5, Next: Section 6, Previous: Section 4, Up: Top + +Questions about arrows + +* Menu: +* Question 5.1:: How do I draw two arrows at arbitrary positions along + a path? +* Question 5.2:: How do I reverse the direction of an arrowhead? +* Question 5.3:: How do I change the size of all arrows? +* Question 5.4:: Can I create other arrowhead styles? + + + +File: asy-faq.info, Node: Question 5.1, Next: Question 5.2, Previous: Question 4.12, Up: Section 5 + +Question 5.1. How do I draw two arrows at arbitrary positions along a path? + +Assuming that at least one of the arrowheads is to be filled, you can do +this: + +size(200); +path g = (0,0)..(1,3)..(3,0); +draw(g,Arrow(Relative(0.9))); +add(arrow(g,invisible,FillDraw(black),Relative(0.5))); +add(arrow(reverse(g),invisible,FillDraw(white,black),Relative(0.9))); +If both of the arrowheads are to be drawn with filltype NoFill, one will +need to create a specialized version of the arrow routine in +plain_arrows.asy: + +void arrow(frame f, arrowhead arrowhead=DefaultHead, + path g, pen p=currentpen, real size=0, + real angle=arrowangle, filltype filltype=arrowhead.defaultfilltype, + position position=EndPoint, bool forwards=true, + margin margin=NoMargin, bool center=false); + +File: asy-faq.info, Node: Question 5.2, Next: Question 5.3, Previous: Question 5.1, Up: Section 5 + +Question 5.2. How do I reverse the direction of an arrowhead? + +Simply reverse the direction of the path. + +path g=((0,0)--(5cm,0)); +draw(reverse(g),Arrow(Relative(0.55))); + +File: asy-faq.info, Node: Question 5.3, Next: Question 5.4, Previous: Question 5.2, Up: Section 5 + +Question 5.3. How do I change the size of all arrows? + +To override the arrowsize you can give every Arrow drawing attribute a +real size argument. If you want to do this globally, you can override the +pen-dependent arrowsize function like this: + +arrowsize=new real(pen p=currentpen) {return 2mm;}; + +File: asy-faq.info, Node: Question 5.4, Next: Question 6.1, Previous: Question 5.3, Up: Section 5 + +Question 5.4. Can I create other arrowhead styles? + +Yes, you can build custom arrowheads like this (see the predefined +arrowhead styles in plain_arrows.asy for further examples): + +arrowhead DotHead; +DotHead.head=new path(path g, position position=EndPoint, pen p=currentpen, + real size=0, real angle=arrowangle) { + if(size == 0) size=DotHead.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); + return circle(0.5(x+y),0.5size); +}; + +size(100); +draw((0,0)..(1,1)..(2,0),Arrow(DotHead)); +dot((2,0),red); +If you submit your alternate arrowheads to the Forum or the Patch +Tracking System, we'll consider including them in a future release. + + +File: asy-faq.info, Node: Section 6, Next: Section 7, Previous: Section 5, Up: Top + +Questions about 2D graphs + +* Menu: +* Question 6.1:: How can I draw x axis ticks on the right side, with + the tick labels on the left side (relative to the axis + path)? +* Question 6.2:: How can I reposition the x axis label to + three-quarters along the axis length? +* Question 6.3:: How can I move the x axis label down 10bp? +* Question 6.4:: Can I use different pens for the axis, the axis label, + and the tick labels? +* Question 6.5:: How can I change the font type of the axes label? +* Question 6.6:: How can I change the font type of the tick labels on + an axis? +* Question 6.7:: How can I prevent axes tick labels from rendering on + top of each other? +* Question 6.8:: How do I make the plot region of a graph, ignoring + labels and legends, have a fixed size? +* Question 6.9:: How can I plot a function f(x) within [0,1]x[0,2] + without explicitly calculating the x values for which + f(x) hits the boundary? +* Question 6.10:: Is it possible to define customized palettes? +* Question 6.11:: Is there an easy way to graph factorial functions + nicely? +* Question 6.12:: How do I indicate that a certain length should be + exactly the size I prescribe with no rescaling, within + a picture which has its own size? +* Question 6.13:: How can I make the y axis display base-2 logarithmic + values? +* Question 6.14:: How can I align the x axes of two graphs on the same + figure? +* Question 6.15:: How can I change the direction of the y-axis, such + that negatives values are on the upper y-axis? +* Question 6.16:: How can I fill a path with a function that defines the + color of each location? +* Question 6.17:: Is there a way to draw a function that is not + explicitly given, such as (y - 2)^2 = x - 1 ? +* Question 6.18:: Is it possible to reverse or stretch an axis? +* Question 6.19:: Why can't I use the UnFill option to draw graphs with + empty markers? +* Question 6.20:: How can I force several images to use the same palette + range (e.g. the entire 0-255 grayscale range)? + + + +File: asy-faq.info, Node: Question 6.1, Next: Question 6.2, Previous: Question 5.4, Up: Section 6 + +Question 6.1. How can I draw x axis ticks on the right side, with the tick labels on the left side (relative to the axis path)? + +import graph; + +size(250,200,IgnoreAspect); + +draw(graph(exp,-1,1),red); + +xaxis("$x$",RightTicks(Label(align=left))); +yaxis("$y$",RightTicks); + +File: asy-faq.info, Node: Question 6.2, Next: Question 6.3, Previous: Question 6.1, Up: Section 6 + +Question 6.2. How can I reposition the x axis label to three-quarters along the axis length? + +import graph; + +size(250,200,IgnoreAspect); + +draw(graph(exp,-1,1),red); + +xaxis(Label("$x$",0.75),LeftTicks); +yaxis("$y$",RightTicks); + +File: asy-faq.info, Node: Question 6.3, Next: Question 6.4, Previous: Question 6.2, Up: Section 6 + +Question 6.3. How can I move the x axis label down 10bp? + +import graph; +size(250,200,IgnoreAspect); + +draw(graph(exp,-1,1),red); + +xaxis(shift(0,-10)*"$x$",LeftTicks); +yaxis("$y$",RightTicks); + +File: asy-faq.info, Node: Question 6.4, Next: Question 6.5, Previous: Question 6.3, Up: Section 6 + +Question 6.4. Can I use different pens for the axis, the axis label, and the tick labels? + +Yes: + +import graph; +size(300,200,IgnoreAspect); + +xlimits(-50,50); +ylimits(0,100); + +xaxis(Label("$x$",MidPoint,red),Bottom,blue,LeftTicks(green)); +yaxis("$y$",Left,RightTicks); + +File: asy-faq.info, Node: Question 6.5, Next: Question 6.6, Previous: Question 6.4, Up: Section 6 + +Question 6.5. How can I change the font type of the axes label? + +import graph; +size(300,200,IgnoreAspect); + +xlimits(-50,50); +ylimits(0,100); + +xaxis("x",Bottom,Courier("m","n"),LeftTicks); +yaxis("$y$",Left,RightTicks); + +File: asy-faq.info, Node: Question 6.6, Next: Question 6.7, Previous: Question 6.5, Up: Section 6 + +Question 6.6. How can I change the font type of the tick labels on an axis? + +Tick labels are by default typeset in (TeX) math mode, so to use other +fonts you need to override the default tick format: + +import graph; +size(300,200,IgnoreAspect); + +xlimits(-50,50); +ylimits(0,100); + +xaxis("$x$",Bottom,LeftTicks("%.4g",Courier("m","n")+fontsize(12))); +yaxis("$y$",Left,RightTicks); + +File: asy-faq.info, Node: Question 6.7, Next: Question 6.8, Previous: Question 6.6, Up: Section 6 + +Question 6.7. How can I prevent axes tick labels from rendering on top of each other? + +Either: + +(i) give LeftTicks/RightTicks/Ticks the arguments beginlabel=false and/or +endlabel=false; + +(ii) explicitly remove specific ticks and their labels (drawing them +manually; see +http://asymptote.svn.sourceforge.net/viewvc/asymptote/trunk/asymptote/base/graph.asy +for the definition of NoZero): + +import graph; + +size(10cm); + +real f(real x) {return x^2;} + +draw(graph(f,-2,2)); + +xaxis(Ticks(NoZero)); +yaxis(Ticks(NoZero)); + +label("$0$",(0,0),SW); +(iii) explicitly remove specific tick labels and draw them manually (see +http://asymptote.svn.sourceforge.net/viewvc/asymptote/trunk/asymptote/base/graph.asy +for the definition of NoZeroFormat): + +import graph; + +size(10cm); + +real f(real x) {return x^2;} + +draw(graph(f,-2,2)); + +xaxis(Ticks(NoZeroFormat)); +yaxis(Ticks(NoZeroFormat)); + +label("$0$",(0,0),SW); +(iv) use the xasy GUI to move overlapping labels; + +(v) change the Label argument of LeftTicks, RightTicks, or Ticks to: + +Label(currentpen+overwrite(Move)) +Solution (v) will move labels that might otherwise overwrite a previous +label. Other possible overwrite arguments are Allow (allows overlapping +labels; the default), Suppress (an overlapping label will not be written +at all), SuppressQuiet, and MoveQuiet. The last two achieve the same +result as the non-quiet types, but will not notify you which labels are +overlapping. See: http://asymptote.sourceforge.net/doc/Pens.html. + +In the case of a user-specified tick array, you can change which labels +get suppressed/moved by changing the order of array entries. + + +File: asy-faq.info, Node: Question 6.8, Next: Question 6.9, Previous: Question 6.7, Up: Section 6 + +Question 6.8. How do I make the plot region of a graph, ignoring labels and legends, have a fixed size? + +Either: + +i) Specify an explicit unitsize, which overrides any call to size: +unitsize(x=1cm,y=2cm); +ii) Explicitly tell Asymptote to map the plot region to a specific size: +import graph; +real[] x={0,1,2,3}; +real[] y=x^2; +draw(graph(x,y),red); +xaxis("$x$",BottomTop,LeftTicks); +yaxis("$y$",LeftRight,RightTicks); + +size(5cm,5cm,point(SW),point(NE)); + +label("$f_\mathrm{T}$",point(N),2N); +iii) Specify the points in user coordinates that should correspond to a +given picture size: + +import graph; + +size(250,200,IgnoreAspect); + +draw(graph(exp,-1,1),red); + +xaxis("$x$",BottomTop,LeftTicks); +yaxis("$y$",LeftRight,RightTicks); + +fixedscaling((-1.5,-0.5),(1.5,3.5)); +In this example, the user coordinate (-1.5,-0.5) will end up being the +lower left corner of the figure and (1.5,3.5) will be the upper right +corner. You can use this option to ensure multiple figures have the same +scaling and same resulting figure size (just ensure the two coordinates +given to fixedscaling() leaves room for any labels). + +See also http://asymptote.sourceforge.net/doc/Frames-and-pictures.html. + + +File: asy-faq.info, Node: Question 6.9, Next: Question 6.10, Previous: Question 6.8, Up: Section 6 + +Question 6.9. How can I plot a function f(x) within [0,1]x[0,2] without explicitly calculating the x values for which f(x) hits the boundary? + +Call limits with the Crop option before drawing the graph: +import graph; + +size(250,200,IgnoreAspect); + +draw(graph(exp,-1,1),red); + +limits((0,0),(1,2),Crop); + +xaxis("$x$",BottomTop,LeftTicks); +yaxis("$y$",LeftRight,RightTicks); +See also http://asymptote.sourceforge.net/doc/graph.html. + + +File: asy-faq.info, Node: Question 6.10, Next: Question 6.11, Previous: Question 6.9, Up: Section 6 + +Question 6.10. Is it possible to define customized palettes? + +Yes, you may generate your own pen[] array. For example: + +int NColors=32768; +pen[] MyPalette=new pen[NColors]; +real step=1/(NColors-1.0); +// Start at black: rgb(0,0,0) +// End at yellow: rgb(1,1,0) +for(int i=0; i < NColors; ++i) { + real rgval=i*step; + MyPalette[i]=rgb(rgval,rgval,0.0); +} + +File: asy-faq.info, Node: Question 6.11, Next: Question 6.12, Previous: Question 6.10, Up: Section 6 + +Question 6.11. Is there an easy way to graph factorial functions nicely? + +The example below shows a continuous function and two methods for placing +markers at integer values of x: + +import graph; + +size(200,200,IgnoreAspect); + +real factorial(real t) {return gamma(t+1);} + +scale(Linear,Log); + +// Graph the factorial function. +draw(graph(factorial,0,10)); + +// Method 1: Draw nodes, but hide line +pair F(int t) {return (t,factorial(t));} +// Graph of factorial function from 0 to 10 +pair[] z=sequence(F,11); +draw(graph(z),invisible,marker(scale(0.8mm)*unitcircle,blue,Fill)); + +// Method 2: Nongraphing routines require explicit scaling: +pair dotloc(int t) {return Scale(F(t));} +pair[] dotlocs=sequence(dotloc,11); +dot(dotlocs); + +xaxis("$x$",BottomTop,LeftTicks); +yaxis("$y$",LeftRight,RightTicks); + +File: asy-faq.info, Node: Question 6.12, Next: Question 6.13, Previous: Question 6.11, Up: Section 6 + +Question 6.12. How do I indicate that a certain length should be exactly the size I prescribe with no rescaling, within a picture which has its own size? + +Here's an easy way to do this. + +size(12cm,0); + +void distance(picture pic=currentpicture, pair A, pair B, Label L="", real n=0, + pen p=currentpen) +{ + real d=3mm; + path g=A--B; + transform T=shift(-n*d*unit(B-A)*I); + pic.add(new void(frame f, transform t) { + picture opic; + path G=T*t*g; + draw(opic,Label(L,Center,UnFill(1)),G,p,Arrows(NoFill),Bars,PenMargins); + add(f,opic.fit()); + }); + pic.addBox(min(g),max(g),T*min(p),T*max(p)); +} + +pair A=(0,0), B=(3,3); + +dot(A); +dot(B); + +distance(A,B,"$\ell$",1); + +File: asy-faq.info, Node: Question 6.13, Next: Question 6.14, Previous: Question 6.12, Up: Section 6 + +Question 6.13. How can I make the y axis display base-2 logarithmic values? + +See the example http://asymptote.sourceforge.net/gallery/2D +graphs/log2graph.asy. + + +File: asy-faq.info, Node: Question 6.14, Next: Question 6.15, Previous: Question 6.13, Up: Section 6 + +Question 6.14. How can I align the x axes of two graphs on the same figure? + +An easy way to do this, if the axes to be aligned have the same scaling +and size, is illustrated in the example +http://asymptote.sourceforge.net/gallery/2D graphs/alignedaxis.asy. + +Here is a more general solution to the problem of aligning two arbitrary +axes. One fits the second picture to a frame based on the horizontal +scaling for the first picture: + +import graph; + +real width=15cm; +real aspect=0.3; + +picture pic1,pic2; + +size(pic1,width,aspect*width,IgnoreAspect); +size(pic2,width,aspect*width,IgnoreAspect); + +scale(pic1,false); +scale(pic2,false); + +real xmin1=6; +real xmax1=9; +real xmin2=8; +real xmax2=16; + +real a1=1; +real a2=0.001; + +real f1(real x) {return a1*sin(x/2*pi);} +real f2(real x) {return a2*sin(x/4*pi);} + +draw(pic1,graph(pic1,f1,xmin1,xmax1)); +draw(pic2,graph(pic2,f2,xmin2,xmax2)); + +xaxis(pic1,Bottom,LeftTicks()); +yaxis(pic1,"$f_1(x)$",Left,RightTicks); + +xaxis(pic2,"$x$",Bottom,LeftTicks(Step=4)); +yaxis(pic2,"$f_2(x)$",Left,RightTicks); + +yequals(pic1,0,Dotted); +yequals(pic2,0,Dotted); + +pair min1=point(pic1,SW); +pair max1=point(pic1,NE); + +pair min2=point(pic2,SW); +pair max2=point(pic2,NE); + +real scale=(max1.x-min1.x)/(max2.x-min2.x); +real shift=min1.x/scale-min2.x; + +transform t1=pic1.calculateTransform(); +transform t2=pic2.calculateTransform(); +transform T=xscale(scale*t1.xx)*yscale(t2.yy); + +add(pic1.fit()); +real height=truepoint(N,user=false).y-truepoint(S,user=false).y; +add(shift(0,-height)*(shift(shift)*pic2).fit(T)); + +File: asy-faq.info, Node: Question 6.15, Next: Question 6.16, Previous: Question 6.14, Up: Section 6 + +Question 6.15. How can I change the direction of the y-axis, such that negatives values are on the upper y-axis? + +Here is a simple example (see also the example +http://asymptote.sourceforge.net/gallery/2D graphs/diatom.asy or the +discussion of Linear(-1) in the documentation): + +import graph; +size(250,200,IgnoreAspect); + +scale(Linear,Linear(-1)); + +draw(graph(log,0.1,10),red); + +xaxis("$x$",LeftTicks); +yaxis("$y$",RightTicks); + +File: asy-faq.info, Node: Question 6.16, Next: Question 6.17, Previous: Question 6.15, Up: Section 6 + +Question 6.16. How can I fill a path with a function that defines the color of each location? + +Use functionshade with a PDF tex engine, as illustrated by the example +{functionshading.asy}. + +If you want to produce PostScript output, an approximate solution for now +would be to superimpose a fine grid and specify colors to latticeshade +that depend on position as a single pen[][] lattice. Alternatively, it may +be more efficient to use tensorshade}. + + +File: asy-faq.info, Node: Question 6.17, Next: Question 6.18, Previous: Question 6.16, Up: Section 6 + +Question 6.17. Is there a way to draw a function that is not explicitly given, such as (y - 2)^2 = x - 1 ? + +Yes, use the parametric form + +y=t +x=(t-2)^2+1 +See the example http://asymptote.sourceforge.net/gallery/2D +graphs/parametricgraph.asy. + + +File: asy-faq.info, Node: Question 6.18, Next: Question 6.19, Previous: Question 6.17, Up: Section 6 + +Question 6.18. Is it possible to reverse or stretch an axis? + +The real scaling argument to Linear is used to stretch (or reverse) the +axis. To see the effect of axis stretching, be sure not to specify +IgnoreAspect in the picture size command. + +A secondary axis has the same length as the primary axis, so stretching +cannot have any effect. But one can still reverse the axis, with +Linear(-1). + + +File: asy-faq.info, Node: Question 6.19, Next: Question 6.20, Previous: Question 6.18, Up: Section 6 + +Question 6.19. Why can't I use the UnFill option to draw graphs with empty markers? + +UnFill won't work here because it only affects the local frame the markers +are initially drawn on, before being added to currentpicture. Here is a +way of achieving the desired effect (assuming a white background): + +import graph; +size(10cm,0); +pair[] z={(0,0),(0.5,0.5),(1,1)}; +path g=graph(z); + +draw(shift(0,.5)*g,marker(scale(5)*unitcircle,FillDraw(white))); + +xaxis(BottomTop,LeftTicks); +yaxis(LeftRight,RightTicks); + +File: asy-faq.info, Node: Question 6.20, Next: Question 7.1, Previous: Question 6.19, Up: Section 6 + +Question 6.20. How can I force several images to use the same palette range (e.g. the entire 0-255 grayscale range)? + +The palette color space corresponds to a range of values specified by the +argument range, which can be Full, Automatic or an explicit range +Range(pair min, pair max). Here Full} specifies a range varying from the +minimum to maximum values of the function over the sampling interval, +while Automatic selects "nice" limits. + + +File: asy-faq.info, Node: Section 7, Next: Section 8, Previous: Section 6, Up: Top + +Questions about programming + +* Menu: +* Question 7.1:: Is Asymptote an interpreter or a compiler? +* Question 7.2:: What is the difference between a frame and a picture? +* Question 7.3:: What is the difference between a path and a guide? +* Question 7.4:: What is a convenient way to declare and initialize an + array of pictures? +* Question 7.5:: Is there a way to define functions that act on arrays + in general (i.e. work for arrays of any type)? +* Question 7.6:: Is there any way to declare structures ahead of their + definition, e.g. where struct A performs some + operation on struct B, but B contains an A member? +* Question 7.7:: Where are static variables in for loops allocated? +* Question 7.8:: Is there a debugger for asy? +* Question 7.9:: Do you accept patches for Asymptote? + + + +File: asy-faq.info, Node: Question 7.1, Next: Question 7.2, Previous: Question 6.20, Up: Section 7 + +Question 7.1. Is Asymptote an interpreter or a compiler? + +Asymptote compiles Asymptote commands into its own virtual machine code. +It then runs this pseudocode on a virtual machine to produce PostScript +code. + + +File: asy-faq.info, Node: Question 7.2, Next: Question 7.3, Previous: Question 7.1, Up: Section 7 + +Question 7.2. What is the difference between a frame and a picture? + +Frames are canvases for drawing in PostScript coordinates. While working +with frames directly is occasionally necessary for constructing deferred +drawing routines, pictures are usually more convenient to work with. See +*Note Question 8.8:: `In MetaPost, it is possible to have a drawing remain +the same size in different pictures by defining a unit u and explicitly +multiply all the coordinates by u. Is there a better way to do this in +Asymptote?'. + + +File: asy-faq.info, Node: Question 7.3, Next: Question 7.4, Previous: Question 7.2, Up: Section 7 + +Question 7.3. What is the difference between a path and a guide? + +A path is a cubic spline with fixed endpoint conditions. + +A guide is an unresolved cubic spline (list of cubic-spline nodes and +control points). A guide is like a path except that the computation of the +cubic spline is deferred until drawing time (when it is resolved into a +path); this allows two guides with free endpoint conditions to be joined +together smoothly. + + +File: asy-faq.info, Node: Question 7.4, Next: Question 7.5, Previous: Question 7.3, Up: Section 7 + +Question 7.4. What is a convenient way to declare and initialize an array of pictures? + +You could write yourself a routine such as: +picture[] picture(int n) { + picture[] pic; + for(int i=0; i < n; ++i) { + pic[i]=new picture; + size(pic[i],19cm,0); + } + return pic; +} + +picture[] pic=picture(6); + +File: asy-faq.info, Node: Question 7.5, Next: Question 7.6, Previous: Question 7.4, Up: Section 7 + +Question 7.5. Is there a way to define functions that act on arrays in general (i.e. work for arrays of any type)? + +Generic types aren't yet implemented. + +But for now you can at least say +typedef string T; +include F; + +typedef real T; +include F; +where F.asy contains some type-dependent code like +T[] operator $(T A, T B) {return new T[] {A,B};} + +File: asy-faq.info, Node: Question 7.6, Next: Question 7.7, Previous: Question 7.5, Up: Section 7 + +Question 7.6. Is there any way to declare structures ahead of their definition, e.g. where struct A performs some operation on struct B, but B contains an A member? + +Asymptote does not support forward declaration of types. You can, however, +nest structures, so that both types are visible for parts of the bodies of +both structure definitions. For example: + +struct B { + typedef void someroutine(B b); + + static struct A { + someroutine routine; + void operator init(someroutine routine) { + this.routine=routine; + } + } + + string test="Testing"; +} + +typedef B.A A; + +A a=B.A(new void(B b){write(b.test);}); + +B b; +a.routine(b); + +File: asy-faq.info, Node: Question 7.7, Next: Question 7.8, Previous: Question 7.6, Up: Section 7 + +Question 7.7. Where are static variables in for loops allocated? + +In the example + +void f() { + for(int i=0; i < 3; ++i) { + static int n; + ++n; + write(n); + } +} + +f(); // Writes 1, 2, 3 +the static qualifier means that n is allocated not just outside of the for +loop, but also outside the function. This is clear if you call f multiple +times; there is still only one instance of n. + +The "level" of a variable (where it is allocated) has nothing to do with +the "scope" of a variable (how long it can be referred to by name). The +curly braces enclosing a block affect only a variable's scope, not its +level. + +Static modifiers are meaningless at the top level; they generate a warning +and are simply ignored: + +for(int i=0; i < 3; ++i) { + static int n; + ++n; + write(n); +} +// Writes warning about top-level static modifier and then 1, 1, 1 +Since version 1.22, non-static variables allocated in a loop body are +allocated anew every iteration. This is only noticable in obscure cases +where a variable in a loop is accessed in the closure of a function +defined in the loop: + +int f(); + +for(int i=0; i < 10; ++i) { + int j=10*i; + if(i == 5) + f=new int() {return j;}; +} + +write(f()); // Writes 50 +Variables in the body of a loop last as long as that iteration of the +loop, unless they are kept alive by a function closure as in the example +above. In a function body, variables will last at least as long as the +function call, though because of closures and garbage collection, they may +last longer than that. If defined at the top level of a file or at the +interactive prompt, they will last at least until the end of the file or +prompt's run. + + +File: asy-faq.info, Node: Question 7.8, Next: Question 7.9, Previous: Question 7.7, Up: Section 7 + +Question 7.8. Is there a debugger for asy? + +Yes, Asymptote includes a line-based debugger: + +http://asymptote.sourceforge.net/doc/Debugger.html + + +File: asy-faq.info, Node: Question 7.9, Next: Question 8.1, Previous: Question 7.8, Up: Section 7 + +Question 7.9. Do you accept patches for Asymptote? + +Yes, in fact we would prefer that users submit patches for customized +features (to http://sourceforge.net/tracker/?atid=685685&group_id=120000) +instead of relying on us to do all of the coding. Development will proceed +faster that way. + + +File: asy-faq.info, Node: Section 8, Next: Section 9, Previous: Section 7, Up: Top + +Questions about differences between Asymptote and MetaPost + +* Menu: +* Question 8.1:: What is the equivalent of the MetaPost c[a,b] + interpolation operator? +* Question 8.2:: How does picture scaling differ in Asymptote and + MetaPost? +* Question 8.3:: How can I avoid automatic scaling of a picture? +* Question 8.4:: What is the equivalent of MetaPost ... command? +* Question 8.5:: What is the equivalent of the MetaPost pickup command? +* Question 8.6:: What is the equivalent of the MetaPost whatever + command? +* Question 8.7:: What is the equivalent for the MetaPost command for + lray - horiz*v - verti*u = whatever*(LightSource - R), + a system of three linear equations for three unknowns: + horiz, verti, whatever? +* Question 8.8:: In MetaPost, it is possible to have a drawing remain + the same size in different pictures by defining a unit + u and explicitly multiply all the coordinates by u. Is + there a better way to do this in Asymptote? +* Question 8.9:: In MetaPost, one could produce tiling pictures by + generating a picture, and then clipping the picture to + a rectangle of fixed dimensions around the center of + the picture. How is that done in Asymptote? + + + +File: asy-faq.info, Node: Question 8.1, Next: Question 8.2, Previous: Question 7.9, Up: Section 8 + +Question 8.1. What is the equivalent of the MetaPost c[a,b] interpolation operator? + +interp(a,b,c); + +File: asy-faq.info, Node: Question 8.2, Next: Question 8.3, Previous: Question 8.1, Up: Section 8 + +Question 8.2. How does picture scaling differ in Asymptote and MetaPost? + +Asymptote includes an optional facility to do automatic scaling of +pictures to achieve a given overall picture size, whereas Metapost only +supports manual scaling. Asymptote defers drawing of objects drawn to +pictures and distinguishes between true-size objects and objects that +should scale with the picture size. The resulting linear programming +problem is solved via the Simplex method. + +See the http://asymptote.sourceforge.net/gallery/dimension.asy example for +an example of how deferred drawing is used to accomodate both user and +true-size (PostScript) coordinates. + + +File: asy-faq.info, Node: Question 8.3, Next: Question 8.4, Previous: Question 8.2, Up: Section 8 + +Question 8.3. How can I avoid automatic scaling of a picture? + +If you really like Metapost-style manual (hard-wired) scaling either: + +(i) use the default size(0,0) for the entire picture and do all of the +scaling by hand, just like in MetaPost; + +(ii) draw to a separate picture pic and add(pic.fit()); + +(iii) use frames. + + +File: asy-faq.info, Node: Question 8.4, Next: Question 8.5, Previous: Question 8.3, Up: Section 8 + +Question 8.4. What is the equivalent of MetaPost ... command? + +The connector :: is a macro for tension atleast 1: + +size(100); +pair z0=(0,0); +pair z1=(1,0.25); +pair z2=(2,0); +draw(z0{up}::z1{right}::z2{down}); + +File: asy-faq.info, Node: Question 8.5, Next: Question 8.6, Previous: Question 8.4, Up: Section 8 + +Question 8.5. What is the equivalent of the MetaPost pickup command? + +Just say, for example: + +currentpen=red; + +File: asy-faq.info, Node: Question 8.6, Next: Question 8.7, Previous: Question 8.5, Up: Section 8 + +Question 8.6. What is the equivalent of the MetaPost whatever command? + +Asymptote does not implicitly solve linear equations and therefore does +not have the notion of a whatever unknown. Such a facility could certainly +be added (perhaps using the notation ?= since = means assignment). +However, the most common uses of whatever in MetaPost are covered by +functions like extension in math.asy: +pair extension(pair P, pair Q, pair p, pair q); +this returns the intersection point of the extensions of the line segments +PQ and pq. We find using routines like extension more explicit and less +confusing to new users. But we could be persuaded to add something similar +if someone can justify the need. In the meantime, one can always use the +explicit built-in linear solver solve (see +http://asymptote.sourceforge.net/doc/solve.html), which uses LU +decomposition. + + +File: asy-faq.info, Node: Question 8.7, Next: Question 8.8, Previous: Question 8.6, Up: Section 8 + +Question 8.7. What is the equivalent for the MetaPost command for lray - horiz*v - verti*u = whatever*(LightSource - R), a system of three linear equations for three unknowns: horiz, verti, whatever? + +Since horiz*v+verti*u spans a plane, you could use +real intersect(vector P, vector Q, vector n, vector Z); +to find the intersection time for the line lray-whatever*(LightSource - +R) and then extract the three desired values from there. (You'll still +need to use the built-in explicit linear solver to solve a 2x2 system to +get horiz and verti.) + + +File: asy-faq.info, Node: Question 8.8, Next: Question 8.9, Previous: Question 8.7, Up: Section 8 + +Question 8.8. In MetaPost, it is possible to have a drawing remain the same size in different pictures by defining a unit u and explicitly multiply all the coordinates by u. Is there a better way to do this in Asymptote? + +Yes, Asymptote has a better way: you definitely don't want to manually +scale all of your coordinates. To make the user coordinates represent +multiples of exactly 1cm: + +unitsize(1cm); +draw(unitsquare); +One can also specify different x and y unit sizes: + +unitsize(x=1cm,y=2cm); +draw(unitsquare); +Another way is to draw your fixed size object to a frame and add it to +currentpicture like this: + +path p=(0,0)--(1,0); +frame object; +draw(object,scale(100)*p); + +add(object); +add(object,(0,-10)); +To understand the difference between frames and pictures, try this: + +size(300,300); + +path p=(0,0)--(1,0); +picture object; +draw(object,scale(100)*p); + +add(object); +add(object,(0,-10)); // Adds truesize object to currentpicture + +File: asy-faq.info, Node: Question 8.9, Next: Question 9.1, Previous: Question 8.8, Up: Section 8 + +Question 8.9. In MetaPost, one could produce tiling pictures by generating a picture, and then clipping the picture to a rectangle of fixed dimensions around the center of the picture. How is that done in Asymptote? + +If you are using currentpicture the way one would in MetaPost (drawing in +raw PostScript coordinates), you can simply do something like: + +fill((0,0)--(100,100)--(200,0)--cycle); + +pair center(picture pic=currentpicture) {return 0.5*(pic.min()+pic.max());} + +real height=100; +real width=100; +pair delta=0.5(width,height); +pair c=center(); +clip(box(c-delta,c+delta)); +However, drawing in PostScript coordinates is often inconvenient. Here's +the Asymptote way of doing the same thing, using deferred drawing: + +size(200,100); +fill((0,0)--(1,1)--(2,0)--cycle); + +void clip(picture pic=currentpicture, real width, real height) +{ + pic.clip(new void (frame f, transform) { + pair center=0.5(min(f)+max(f)); + pair delta=0.5(width,height); + clip(f,box(center-delta,center+delta)); + }); +} + +clip(100,100); +See also the discussion of tilings in the documentation: +http://asymptote.sourceforge.net/doc/Pens.html. + + +File: asy-faq.info, Node: Section 9, Previous: Section 8, Up: Top + +Questions about output + +* Menu: +* Question 9.1:: How can I disable automatic invocation of the PS + viewer after an asy file is done processing? +* Question 9.2:: How do I output jpeg images? +* Question 9.3:: Can I embed bitmaps (photos) into my drawings and + position and scale them? +* Question 9.4:: Does Asymptote support direct PDF output? +* Question 9.5:: How to I produce large pictures of high quality in + raster format (e.g. png, giff etc). +* Question 9.6:: Is it possible to produce multi-page documents with + asymptote? + + + +File: asy-faq.info, Node: Question 9.1, Next: Question 9.2, Previous: Question 8.9, Up: Section 9 + +Question 9.1. How can I disable automatic invocation of the PS viewer after an asy file is done processing? + +It's actually not on by default, unless you happen to be using Microsoft +Windows (because that is what most Microsoft Windows users expect). +Microsoft Windows users can turn this feature off with the command-line +option -noV or by putting + +import settings; +interactiveView=false; +batchView=false; +in their config.asy file. See +http://asymptote.sourceforge.net/doc/Options.html. + + +File: asy-faq.info, Node: Question 9.2, Next: Question 9.3, Previous: Question 9.1, Up: Section 9 + +Question 9.2. How do I output jpeg images? + +If you have the ImageMagick convert program installed, simply type + +asy -f jpg test.asy + +File: asy-faq.info, Node: Question 9.3, Next: Question 9.4, Previous: Question 9.2, Up: Section 9 + +Question 9.3. Can I embed bitmaps (photos) into my drawings and position and scale them? + +Convert them to eps format and use the graphic(string) function just like +a Label: + +label(graphic("file"),(0,0)); +See the example http://asymptote.sourceforge.net/gallery/orthocenter.asy +and http://asymptote.sourceforge.net/doc/label.html. + + +File: asy-faq.info, Node: Question 9.4, Next: Question 9.5, Previous: Question 9.3, Up: Section 9 + +Question 9.4. Does Asymptote support direct PDF output? + +Yes, PDF output can be produced by the -f pdf option or -tex pdflatex +option. This supports transparency, annotations, embedded movies, and +U3D/PRC content. + + +File: asy-faq.info, Node: Question 9.5, Next: Question 9.6, Previous: Question 9.4, Up: Section 9 + +Question 9.5. How to I produce large pictures of high quality in raster format (e.g. png, giff etc). + +Try using some of the options to convert, mainly -geometry and -density. +For example: +convert -geometry 1000x3000 example.eps example.png +You can also change the default resolution of the image with: +convert -geometry 1000x3000 -density 300 -units PixelsPerInch example.eps example.png +This does not change the number of pixels in the image, but just gives a +hint as to how large each pixel should be displayed. + +If you include the -density option without the -geometry option, convert +will keep the image size constant (so a 4cm x 3cm eps figure will +generate a 4cm x 3cm png image). + + +File: asy-faq.info, Node: Question 9.6, Previous: Question 9.5, Up: Section 9 + +Question 9.6. Is it possible to produce multi-page documents with asymptote? + +Yes, simply call the newpage() function. This is used by the slide.asy +package to produce high-quality slide presentations (easier to use than +Prosper). + diff --git a/Master/texmf/doc/info/asymptote.info b/Master/texmf/doc/info/asymptote.info new file mode 100644 index 00000000000..5a6a1156c0d --- /dev/null +++ b/Master/texmf/doc/info/asymptote.info @@ -0,0 +1,167 @@ +This is asymptote.info, produced by makeinfo version 4.13 from +../asymptote.texi. + +This file documents `Asymptote', version 1.75. + + `http://asymptote.sourceforge.net' + + Copyright (C) 2004-9 Andy Hammerlindl, John Bowman, and Tom Prince. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Lesser General Public License + (see the file LICENSE in the top-level source directory). + + +INFO-DIR-SECTION Languages +START-INFO-DIR-ENTRY +* asymptote: (asymptote). Vector graphics language. +END-INFO-DIR-ENTRY + + +Indirect: +asymptote.info-1: 578 +asymptote.info-2: 295810 + +Tag Table: +(Indirect) +Node: Top578 +Node: Description6646 +Node: Installation10061 +Node: UNIX binary distributions11054 +Node: MacOS X binary distributions12160 +Node: Microsoft Windows13108 +Node: Configuring14278 +Node: Search paths18463 +Node: Compiling from UNIX source19143 +Ref: multisampling19732 +Node: Editing modes22158 +Node: Subversion24358 +Node: Uninstall24819 +Node: Tutorial25169 +Ref: unitcircle29177 +Ref: Bezier29386 +Node: Drawing commands34799 +Node: draw36508 +Ref: arrows37656 +Node: fill42881 +Ref: gradient shading43925 +Node: clip48118 +Node: label48710 +Ref: Label49409 +Node: Programming55205 +Ref: array iteration56022 +Node: Data types57104 +Ref: format66001 +Node: Paths and guides69398 +Ref: circle69652 +Ref: arctime75050 +Ref: extension78915 +Node: Pens85488 +Ref: fillrule92742 +Ref: basealign93639 +Ref: transparency96464 +Ref: makepen100013 +Ref: overwrite100905 +Node: Transforms102115 +Node: Frames and pictures103906 +Ref: envelope105047 +Ref: size106130 +Ref: unitsize107117 +Ref: shipout108177 +Ref: filltype110395 +Ref: add113378 +Ref: add about114324 +Ref: tex117325 +Node: Files118199 +Ref: cd119164 +Ref: scroll123545 +Node: Variable initializers126588 +Node: Structures128883 +Node: Operators136225 +Node: Arithmetic & logical136539 +Node: Self & prefix operators138436 +Node: User-defined operators139224 +Node: Implicit scaling140135 +Node: Functions140698 +Ref: stack overflow143451 +Node: Default arguments144015 +Node: Named arguments144754 +Node: Rest arguments146916 +Node: Mathematical functions149726 +Node: Arrays154331 +Ref: sort161512 +Ref: tridiagonal163803 +Ref: solve165031 +Node: Slices169158 +Node: Casts173052 +Node: Import175018 +Node: Static180248 +Node: LaTeX usage183144 +Node: Base modules188613 +Node: plain191049 +Node: simplex191701 +Node: math191974 +Node: interpolate193073 +Node: geometry193352 +Node: trembling193915 +Node: stats194276 +Node: patterns194536 +Node: markers194772 +Node: tree196661 +Node: binarytree196847 +Node: drawtree197493 +Node: syzygy197695 +Node: feynman197969 +Node: roundedpath198244 +Node: animation198527 +Ref: animate198946 +Node: embed200085 +Node: slide201887 +Node: MetaPost202227 +Node: unicode202943 +Node: latin1203831 +Node: babel204199 +Node: labelpath204428 +Node: labelpath3205248 +Node: annotate205559 +Node: CAD206030 +Node: graph206340 +Ref: ticks213178 +Ref: pathmarkers226599 +Ref: marker227064 +Ref: markuniform227415 +Ref: errorbars229206 +Ref: automatic scaling233543 +Node: palette244951 +Ref: images245069 +Ref: image249240 +Ref: logimage249769 +Ref: penimage250627 +Node: three251123 +Ref: PostScript3D272277 +Node: obj274022 +Node: graph3274272 +Ref: GaussianSurface279000 +Node: grid3280141 +Node: solids280935 +Node: tube281937 +Node: flowchart284168 +Node: contour288724 +Node: contour3294022 +Node: slopefield294329 +Node: Options295810 +Ref: configuration file300978 +Ref: settings300978 +Ref: convert302049 +Node: Interactive mode304808 +Ref: history306432 +Node: GUI307737 +Node: GUI Installation308240 +Node: GUI Usage309765 +Node: PostScript to Asymptote310668 +Node: Help311811 +Node: Debugger313538 +Node: Credits315323 +Node: Index316236 + +End Tag Table diff --git a/Master/texmf/doc/info/dir b/Master/texmf/doc/info/dir index 3f8ce24f548..ef385264303 100644 --- a/Master/texmf/doc/info/dir +++ b/Master/texmf/doc/info/dir @@ -14,57 +14,51 @@ File: dir, Node: Top This is the top of the INFO tree * Menu: -Texinfo documentation system -* Info: (info). How to use the documentation browsing system. -* Texinfo: (texinfo). The GNU documentation format. -* info standalone: (info-stnd). Read Info documents without Emacs. -* infokey: (info-stnd)Invoking infokey. Compile Info customizations. -* install-info: (texinfo)Invoking install-info. Update info/dir entries. -* makeinfo: (texinfo)Invoking makeinfo. Translate Texinfo source. -* texi2dvi: (texinfo)Format with texi2dvi. Print Texinfo documents. -* texindex: (texinfo)Format with tex/texindex. Sort Texinfo index files. - +Science +* Asymptote: (asymptote). Vector graphics language. +* Asymptote FAQ: (asy-faq). FAQ for Asymptote. + TeX -* DVI-to-PNG: (dvipng). DVI to Portable Network Graphics (PNG). -* DVI-to-Postscript: (dvips). Translating TeX DVI files to PostScript. -* Eplain: (eplain). Expanding on plain TeX. -* Kpathsea: (kpathsea). File lookup along search paths. -* LaTeX2e: (latex). LaTeXe help 1.6. -* Naming TeX fonts: (fontname). Filenames for TeX fonts. -* TDS: (tds). Standard TeX directory structure. -* TeXDraw: (texdraw). Drawing PostScript diagrams within TeX. -* Web2c: (web2c). TeX, Metafont, and companion programs. +* DVI-to-PNG: (dvipng). DVI to Portable Network Graphics (PNG). +* DVI-to-Postscript: (dvips). Translating TeX DVI files to PostScript. +* Eplain: (eplain). Expanding on plain TeX. +* Kpathsea: (kpathsea). File lookup along search paths. +* LaTeX2e: (latex2e). LaTeX2e unofficial reference manual. +* Naming TeX fonts: (fontname). Filenames for TeX fonts. +* TDS: (tds). Standard TeX directory structure. +* TeXDraw: (texdraw). Drawing PostScript diagrams within TeX. +* Web2c: (web2c). TeX, Metafont, and companion programs. * afm2tfm: (dvips)Invoking afm2tfm. Making Type 1 fonts available to TeX. -* bibtex: (web2c)bibtex invocation. Maintaining bibliographies. -* dmp: (web2c)dmp invocation. Troff->MPX (MetaPost pictures). -* dvicopy: (web2c)dvicopy invocation. Virtual font expansion -* dvips: (dvips)Invoking Dvips. DVI-to-PostScript translator. -* dvitomp: (web2c)dvitomp invocation. DVI to MPX (MetaPost pictures). -* dvitype: (web2c)dvitype invocation. DVI to human-readable text. -* gftodvi: (web2c)gftodvi invocation. Generic font proofsheets. -* gftopk: (web2c)gftopk invocation. Generic to packed fonts. -* gftype: (web2c)gftype invocation. GF to human-readable text. -* kpsewhich: (kpathsea)Invoking kpsewhich. TeX file searching. -* makempx: (web2c)makempx invocation. MetaPost label typesetting. -* mf: (web2c)mf invocation. Creating typeface families. -* mft: (web2c)mft invocation. Prettyprinting Metafont source. -* mktexlsr: (kpathsea)Filename database. Update ls-R. -* mktexmf: (kpathsea)mktex scripts. MF source generation. -* mktexpk: (kpathsea)mktex scripts. PK bitmap generation. -* mktextex: (kpathsea)mktex scripts. TeX source generation. -* mktextfm: (kpathsea)mktex scripts. TeX font metric generation. -* mltex: (web2c)MLTeX. Multi-lingual TeX. -* mpost: (web2c)mpost invocation. Creating technical diagrams. -* mpto: (web2c)mpto invocation. MetaPost label extraction. -* newer: (web2c)newer invocation. Compare modification times. -* patgen: (web2c)patgen invocation. Creating hyphenation patterns. -* pktogf: (web2c)pktogf invocation. Packed to generic fonts. -* pktype: (web2c)pktype invocation. PK to human-readable text. -* pltotf: (web2c)pltotf invocation. Property list to TFM. -* pooltype: (web2c)pooltype invocation. Display WEB pool files. -* tangle: (web2c)tangle invocation. WEB to Pascal. -* tex: (web2c)tex invocation. Typesetting. -* tftopl: (web2c)tftopl invocation. TFM -> property list. -* vftovp: (web2c)vftovp invocation. Virtual font -> virtual pl. -* vptovf: (web2c)vptovf invocation. Virtual pl -> virtual font. -* weave: (web2c)weave invocation. WEB to TeX. +* bibtex: (web2c)bibtex invocation. Managing bibliographies. +* dmp: (web2c)dmp invocation. Troff->MPX (MetaPost pictures). +* dvicopy: (web2c)dvicopy invocation. Virtual font expansion. +* dvips: (dvips)Invoking Dvips. DVI-to-PostScript translator. +* dvitomp: (web2c)dvitomp invocation. DVI to MPX (MetaPost pictures). +* dvitype: (web2c)dvitype invocation. DVI to human-readable text. +* gftodvi: (web2c)gftodvi invocation. Generic font proofsheets. +* gftopk: (web2c)gftopk invocation. Generic to packed fonts. +* gftype: (web2c)gftype invocation. GF to human-readable text. +* kpsewhich: (kpathsea)Invoking kpsewhich. TeX file searching. +* makempx: (web2c)makempx invocation. MetaPost label typesetting. +* mf: (web2c)mf invocation. Creating typeface families. +* mft: (web2c)mft invocation. Prettyprinting Metafont source. +* mktexlsr: (kpathsea)Filename database. Update ls-R. +* mktexmf: (kpathsea)mktex scripts. MF source generation. +* mktexpk: (kpathsea)mktex scripts. PK bitmap generation. +* mktextex: (kpathsea)mktex scripts. TeX source generation. +* mktextfm: (kpathsea)mktex scripts. TeX font metric generation. +* mltex: (web2c)MLTeX. Multi-lingual TeX. +* mpost: (web2c)mpost invocation. Creating technical diagrams. +* mpto: (web2c)mpto invocation. MetaPost label extraction. +* newer: (web2c)newer invocation. Compare modification times. +* patgen: (web2c)patgen invocation. Creating hyphenation patterns. +* pktogf: (web2c)pktogf invocation. Packed to generic fonts. +* pktype: (web2c)pktype invocation. PK to human-readable text. +* pltotf: (web2c)pltotf invocation. Property list to TFM. +* pooltype: (web2c)pooltype invocation. Display WEB pool files. +* tangle: (web2c)tangle invocation. WEB to Pascal. +* tex: (web2c)tex invocation. Typesetting. +* tftopl: (web2c)tftopl invocation. TFM -> property list. +* vftovp: (web2c)vftovp invocation. Virtual font -> virtual pl. +* vptovf: (web2c)vptovf invocation. Virtual pl -> virtual font. +* weave: (web2c)weave invocation. WEB to TeX. diff --git a/Master/texmf/doc/man/man1/asy.1 b/Master/texmf/doc/man/man1/asy.1 new file mode 100644 index 00000000000..d0699897509 --- /dev/null +++ b/Master/texmf/doc/man/man1/asy.1 @@ -0,0 +1,252 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH ASY 1 "1 Dec 2004" +.SH NAME +asy \- Asymptote: a script-based vector graphics language +.SH SYNOPSIS +.B asy +.RI [ options ] +.RI [ file \ ...] +.SH DESCRIPTION +\fBAsymptote\fP is a powerful descriptive vector graphics language for +technical drawings, inspired by MetaPost but with an improved C++-like syntax. +Asymptote provides for figures the same high-quality level of typesetting that +LaTeX does for scientific text. +.SH OPTIONS +If no arguments are given, Asymptote runs in interactive mode. +.PP +If "\-" is given as the file argument, Asymptote reads from standard input. +.PP +A summary of options is included below. The effect of most options +can be negated by prepending +.B no +to the option name. +Default values for most options may also be entered in the +file +.B .asy/config.asy +in the user's home directory using the long form: +.PP + import settings; + batchView=true; +.PP +For a complete +description, see the Info files. +.TP +.B \-V,\-View +View output; command-line only. +.TP +.B \-a,\-align C|B|T|Z +Center, Bottom, Top, or Zero page alignment [Center]. +.TP +.B \-antialias n +Antialiasing width for rasterized output [2]. +.TP +.B \-auto3D +Automatically activate 3D scene [true]. +.TP +.B \-autoimport string +Module to automatically import. +.TP +.B \-autoplain +Enable automatic importing of plain [true]. +.TP +.B \-autorotate +Enable automatic PDF page rotation [false]. +.TP +.B \-batchMask +Mask fpu exceptions in batch mode [false]. +.TP +.B \-batchView +View output in batch mode [false]. +.TP +.B \-bw +Convert all colors to black and white [false]. +.TP +.B \-cd directory +Set current directory; command-line only. +.TP +.B \-cmyk +Convert rgb colors to cmyk [false]. +.TP +.B \-c,\-command string +Command to autoexecute. +.TP +.B \-compact +Conserve memory at the expense of speed [false]. +.TP +.B \-d,\-debug +Enable debugging messages [false]. +.TP +.B \-divisor n +Garbage collect using purge(divisor=n) [2]. +.TP +.B \-embed +Embed rendered preview image [true]. +.TP +.B \-exitonEOF +Exit interactive mode on EOF [true]. +.TP +.B \-fitscreen +Fit rendered image to screen [true]. +.TP +.B \-globalwrite +Allow write to other directory [false]. +.TP +.B \-gray +Convert all colors to grayscale [false]. +.TP +.B \-h,\-help +Show summary of options; command-line only. +.TP +.B \-historylines n +Retain n lines of history [1000]. +.TP +.B \-iconify +Iconify rendering window [false]. +.TP +.B \-inlineimage +Generate inline embedded image [false]. +.TP +.B \-inlinetex +Generate inline TeX code [false]. +.TP +.B \-interactiveMask +Mask fpu exceptions in interactive mode [true]. +.TP +.B \-interactiveView +View output in interactive mode [true]. +.TP +.B \-interactiveWrite +Write expressions entered at the prompt to stdout [true]. +.TP +.B \-k,\-keep +Keep intermediate files [false]. +.TP +.B \-keepaux +Keep intermediate LaTeX .aux files [false]. +.TP +.B \-level n +Postscript level [3]. +.TP +.B \-l,\-listvariables +List available global functions and variables [false]. +.TP +.B \-localhistory +Use a local interactive history file [false]. +.TP +.B \-m,\-mask +Mask fpu exceptions; command-line only. +.TP +.B \-maxtile pair +Maximum rendering tile size [(0,0)]. +.TP +.B \-maxviewport pair +Maximum viewport size [(2048,2048)]. +.TP +.B \-multiline +Input code over multiple lines at the prompt [false]. +.TP +.B \-multipleView +View output from multiple batch-mode files [false]. +.TP +.B \-multisample n +Multisampling width for screen images [4]. +.TP +.B \-O,\-offset pair +PostScript offset [(0,0)]. +.TP +.B \-f,\-outformat format +Convert each output file to specified format. +.TP +.B \-o,\-outname name +Alternative output directory/filename. +.TP +.B \-p,\-parseonly +Parse file [false]. +.TP +.B \-pdfreload +Automatically reload document in pdfviewer [false]. +.TP +.B \-pdfreloaddelay usec +Delay before attempting initial pdf reload [750000]. +.TP +.B \-position pair +Initial 3D rendering screen position [(0,0)]. +.TP +.B \-prc +Embed 3D PRC graphics in PDF output [true]. +.TP +.B \-prompt string +Prompt [> ]. +.TP +.B \-prompt2 string +Continuation prompt for multiline input [..]. +.TP +.B \-q,\-quiet +Suppress welcome message [false]. +.TP +.B \-render n +Render 3D graphics using n pixels per bp (-1=auto) [-1]. +.TP +.B \-rgb +Convert cmyk colors to rgb [false]. +.TP +.B \-safe +Disable system call [true]. +.TP +.B \-scroll n +Scroll standard output n lines at a time [0]. +.TP +.B \-tabcompletion +Interactive prompt auto-completion [true]. +.TP +.B \-tex engine +latex|pdflatex|xelatex|tex|pdftex|context|none [latex]. +.TP +.B \-thick +Render thick 3D lines [true]. +.TP +.B \-thin +Render thin 3D lines [true]. +.TP +.B \-threads +Use POSIX threads for 3D rendering [true]. +.TP +.B \-toolbar +Show 3D toolbar in PDF output [true]. +.TP +.B \-s,\-translate +Show translated virtual machine code [false]. +.TP +.B \-twice +Run LaTeX twice (to resolve references) [false]. +.TP +.B \-twosided +Use two-sided 3D lighting model for rendering [true]. +.TP +.B \-u,\-user string +General purpose user string. +.TP +.B \-v,\-verbose +Increase verbosity level (can specify multiple times) [0]. +.TP +.B \-version +Show version; command-line only. +.TP +.B \-wait +Wait for child processes to finish before exiting [false]. +.TP +.B \-where +Show where listed variables are declared [false]. +.TP +.B \-xformat format +GUI deconstruction format [png]. + +.SH SEE ALSO +Asymptote is documented fully in the asymptote Info page. +The manual can also be accessed in interactive mode with the "help" command. + +.SH AUTHOR +Asymptote was written by Andy Hammerlindl, John Bowman, and Tom Prince. +.PP +This manual page was written by Hubert Chan for the Debian project (but may +be used by others). diff --git a/Master/texmf/doc/man/man1/xasy.1 b/Master/texmf/doc/man/man1/xasy.1 new file mode 100644 index 00000000000..e882db25212 --- /dev/null +++ b/Master/texmf/doc/man/man1/xasy.1 @@ -0,0 +1,27 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH XASY 1x "27 Nov 2007" +.SH NAME +asy \- script-based vector graphics language +.SH SYNOPSIS +.B xasy +.RI " [-x magnification] [filename]" +.SH DESCRIPTION +\fBAsymptote\fP is a powerful descriptive vector graphics language for +technical drawing, inspired by MetaPost but with an improved C++\-like syntax. +Asymptote provides for figures the same high\-quality level of typesetting that +LaTeX does for scientific text. +.PP +\fBxasy\fP is a GUI for Asymptote that allows for final figure adjustments. + +.SH OPTIONS +.TP +.B \-x magnification +Initial zoom. + +.SH SEE ALSO +Asymptote and xasy are documented fully in the Asymptote Info page. +.SH AUTHOR +Asymptote was written by Andy Hammerlindl, John Bowman, and Tom Prince. +.PP +This manual page was written by Hubert Chan for the Debian project (but may +be used by others). diff --git a/Master/tlpkg/tlpsrc/asymptote.tlpsrc b/Master/tlpkg/tlpsrc/asymptote.tlpsrc new file mode 100644 index 00000000000..eaf6c8037a3 --- /dev/null +++ b/Master/tlpkg/tlpsrc/asymptote.tlpsrc @@ -0,0 +1,5 @@ +category TLCore +binpattern f bin/${ARCH}/*asy +docpattern f texmf/share/info/asy*.info +docpattern f texmf/man/man1/*asy.* +runpattern d texmf/asymptote diff --git a/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc b/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc index 6a58077d45c..5e6cb441a2e 100644 --- a/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-binextra.tlpsrc @@ -6,6 +6,7 @@ longdesc programs and macros for DVI file manipulation, literate longdesc programming, patgen, and the TeX Works Editor. depend collection-basic depend a2ping +depend asymptote depend bibtex8 depend ctie depend detex |