diff options
Diffstat (limited to 'Build/source/utils/asymptote/base')
89 files changed, 45368 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/base/CAD.asy b/Build/source/utils/asymptote/base/CAD.asy new file mode 100644 index 00000000000..1bed03bc20b --- /dev/null +++ b/Build/source/utils/asymptote/base/CAD.asy @@ -0,0 +1,367 @@ +struct sCAD +{ + int nLineGroup = 0; // 0-3 + + pen + // A + pA, + pVisibleEdge, // Sichtbare Kanten + pVisibleContour, // Sichtbarer Umriss + pUsableWindingLength, // Nitzbare Gewindelänge + pSystemLine, // Systemlinie (Stahlbau) + pDiagramCurve, // Kurve in Diagrammen + pSurfaceStructure, // Oberflächenstrukturen + // B + pB, + pLightEdge, // Lichtkante + pMeasureLine, // Maßlinie + pMeasureHelpLine, // Maßhilfslinie + pMeasureLineBound, // Maßlinienbegrenzung + pReferenceLine, // Hinweislinie + pHatch, // Schraffur + pWindingGround, // Gewindegrund + pDiagonalCross, // Diagonalkreuz + pBendLine, // Biegelinie + pProjectionLine, // Projektionslinie + pGrid, // Rasterlinien + // C + pC, + pFreehand, // Begrenzung abgebrochener oder unterbrochener + // Schnitte, wenn die Begrenzung + // keine Mittellinie ist + // E + pE, + pSurfaceTreatmentAllowed, // Bereich zulässiger Oberflächenbehandlung + // F + pF, + pInvisibleEdge, // unsichtbare Kante + pInvisibleContour, // unsichtbarer Umriss + // G + pG, + pMiddleLine, // Mittellinie + pSymmetryLine, // Symmetrielinie + pPartialCircle, // Teilkreis + pCircularHole, // Lochkreis + pDivisionPlane, // Teilungsebene + pTransferLine, // Trajektorien (Übertragunslinien) + // J + pJ, + pCuttingPlane, // Schnittebene + pSurfaceTreatmentRequested, // Bereich geforderter Behandlungen + // K + pK, + pContourBeforeDeformation, // Umrisse vor Verformung + pAdjacentPartContour, // Umrisse angrenzender Teile + pEndShapeRawMaterial, // Fertigformen in Rohteilen + pContourEligibleType, // Umrisse wahlweiser Ausführungen + pPartInFrontOfCuttingPlane; // Teile vor der Schnittebene + + + + static sCAD Create(int nLineGroup = 1) + { + sCAD cad = new sCAD; + if ( nLineGroup < 0 ) + nLineGroup = 0; + if ( nLineGroup > 3 ) + nLineGroup = 3; + cad.nLineGroup = nLineGroup; + + restricted real[] dblFullWidth = {0.35mm, 0.5mm, 0.7mm, 1.0mm}; + restricted real[] dblHalfWidth = {0.18mm, 0.25mm, 0.35mm, 0.5mm}; + + pen pFullWidth = linewidth(dblFullWidth[nLineGroup]); + pen pHalfWidth = linewidth(dblHalfWidth[nLineGroup]); + + // Linienarten: + // A + cad.pA = + cad.pVisibleEdge = + cad.pVisibleContour = + cad.pUsableWindingLength = + cad.pSystemLine = + cad.pDiagramCurve = + cad.pSurfaceStructure = + pFullWidth + solid; + // B + cad.pB = + cad.pLightEdge = + cad.pMeasureLine = + cad.pMeasureHelpLine = + cad.pMeasureLineBound = + cad.pReferenceLine = + cad.pHatch = + cad.pWindingGround = + cad.pDiagonalCross = + cad.pBendLine = + cad.pProjectionLine = + cad.pGrid = + pHalfWidth + solid; + // C + cad.pC = + cad.pFreehand = + pHalfWidth + solid; + // D + // Missing, as I have no idea how to implement this... + // E + cad.pE = + cad.pSurfaceTreatmentAllowed = + pFullWidth + linetype(new real[] {10,2.5}); + // F + cad.pF = + cad.pInvisibleEdge = + cad.pInvisibleContour = + pHalfWidth + linetype(new real[] {20,5}); + // G + cad.pG = + cad.pMiddleLine = + cad.pSymmetryLine = + cad.pPartialCircle = + cad.pCircularHole = + cad.pDivisionPlane = + cad.pTransferLine = + pHalfWidth + linetype(new real[] {40,5,5,5}); + // H + // see J + // I + // This letter is not used in DIN 15 + // J + cad.pJ = + cad.pCuttingPlane = + cad.pSurfaceTreatmentRequested = + pFullWidth + linetype(new real[] {20,2.5,2.5,2.5}); + // K + cad.pK = + cad.pContourBeforeDeformation = + cad.pAdjacentPartContour = + cad.pEndShapeRawMaterial = + cad.pContourEligibleType = + cad.pPartInFrontOfCuttingPlane = + pHalfWidth + linetype(new real[] {40,5,5,5,5,5}); + + return cad; + } // end of Create + + + + real GetMeasurementBoundSize(bool bSmallBound = false) + { + if ( bSmallBound ) + return 1.5 * linewidth(pVisibleEdge) / 2; + else + return 5 * linewidth(pVisibleEdge); + } + + + + path GetMeasurementBound(bool bSmallBound = false) + { + if ( bSmallBound ) + return scale(GetMeasurementBoundSize(bSmallBound = bSmallBound)) * + unitcircle; + else + return (0,0) -- + (-cos(radians(7.5)), -sin(radians(7.5))) * + GetMeasurementBoundSize(bSmallBound = bSmallBound) -- + (-cos(radians(7.5)), sin(radians(7.5))) * + GetMeasurementBoundSize(bSmallBound = bSmallBound) -- + cycle; + } + + + + void MeasureLine(picture pic = currentpicture, + Label L, + pair pFrom, + pair pTo, + real dblLeft = 0, + real dblRight = 0, + real dblRelPosition = 0.5, + bool bSmallBound = false) + { + if ( dblLeft < 0 ) + dblLeft = 0; + if ( dblRight < 0 ) + dblRight = 0; + if ( (dblLeft > 0) && (dblRight == 0) ) + dblRight = dblLeft; + if ( (dblLeft == 0) && (dblRight > 0) ) + dblLeft = dblRight; + pair pDiff = pTo - pFrom; + real dblLength = length(pDiff); + pair pBegin = pFrom - dblLeft * unit(pDiff); + pair pEnd = pTo + dblRight * unit(pDiff); + if ( bSmallBound ) + { + draw( + pic = pic, + g = pBegin--pEnd, + p = pMeasureLine); + } + else + { + real dblBoundSize = GetMeasurementBoundSize(bSmallBound = bSmallBound); + if ( dblLeft == 0 ) + draw( + pic = pic, + g = (pFrom + dblBoundSize/2 * unit(pDiff)) + -- (pTo - dblBoundSize/2 * unit(pDiff)), + p = pMeasureLine); + else + draw( + pic = pic, + g = pBegin -- (pFrom - dblBoundSize/2 * unit(pDiff)) + ^^ pFrom -- pTo + ^^ (pTo + dblBoundSize/2 * unit(pDiff)) -- pEnd, + p = pMeasureLine); + } + path gArrow = GetMeasurementBound(bSmallBound = bSmallBound); + picture picL; + label(picL, L); + pair pLabelSize = 1.2 * (max(picL) - min(picL)); + if ( dblLeft == 0 ) + { + fill( + pic = pic, + g = shift(pFrom) * rotate(degrees(-pDiff)) * gArrow, + p = pVisibleEdge); + fill( + pic = pic, + g = shift(pTo) * rotate(degrees(pDiff)) * gArrow, + p = pVisibleEdge); + if ( dblRelPosition < 0 ) + dblRelPosition = 0; + if ( dblRelPosition > 1 ) + dblRelPosition = 1; + label( + pic = pic, + L = rotate(degrees(pDiff)) * L, + position = + pFrom + + dblRelPosition * pDiff + + unit(rotate(90)*pDiff) * pLabelSize.y / 2); + } + else + { + fill( + pic = pic, + g = shift(pFrom) * rotate(degrees(pDiff)) * gArrow, + p = pVisibleEdge); + fill( + pic = pic, + g = shift(pTo) * rotate(degrees(-pDiff)) * gArrow, + p = pVisibleEdge); + if ( (dblRelPosition >= 0) && (dblRelPosition <= 1) ) + label( + pic = pic, + L = rotate(degrees(pDiff)) * L, + position = + pFrom + + dblRelPosition * pDiff + + unit(rotate(90)*pDiff) * pLabelSize.y / 2); + else + { + // draw label outside + if ( dblRelPosition < 0 ) + label( + pic = pic, + L = rotate(degrees(pDiff)) * L, + position = + pBegin + + pLabelSize.x / 2 * unit(pDiff) + + unit(rotate(90)*pDiff) * pLabelSize.y / 2); + else + // dblRelPosition > 1 + label( + pic = pic, + L = rotate(degrees(pDiff)) * L, + position = + pEnd + - pLabelSize.x / 2 * unit(pDiff) + + unit(rotate(90)*pDiff) * pLabelSize.y / 2); + } + } + } // end of MeasureLine + + + + void MeasureParallel(picture pic = currentpicture, + Label L, + pair pFrom, + pair pTo, + real dblDistance, + // Variables from MeasureLine + real dblLeft = 0, + real dblRight = 0, + real dblRelPosition = 0.5, + bool bSmallBound = false) + { + pair pDiff = pTo - pFrom; + pair pPerpendicularDiff = unit(rotate(90) * pDiff); + real dblDistancePlus; + if ( dblDistance >= 0 ) + dblDistancePlus = dblDistance + 1mm; + else + dblDistancePlus = dblDistance - 1mm; + draw( + pic = pic, + g = pFrom--(pFrom + dblDistancePlus*pPerpendicularDiff), + p = pMeasureHelpLine + ); + draw( + pic = pic, + g = pTo--(pTo + dblDistancePlus*pPerpendicularDiff), + p = pMeasureHelpLine + ); + MeasureLine( + pic = pic, + L = L, + pFrom = pFrom + dblDistance * pPerpendicularDiff, + pTo = pTo + dblDistance * pPerpendicularDiff, + dblLeft = dblLeft, + dblRight = dblRight, + dblRelPosition = dblRelPosition, + bSmallBound = bSmallBound); + } // end of MeasureParallel + + + + path MakeFreehand(pair pFrom, pair pTo, + real dblRelDivisionLength = 12.5, + real dblRelDistortion = 2.5, + bool bIncludeTo = true) + { + pair pDiff = pTo - pFrom; + pair pPerpendicular = dblRelDistortion * linewidth(pFreehand) * + unit(rotate(90) * pDiff); + + int nNumOfSubDivisions=ceil(length(pDiff) / + (dblRelDivisionLength * linewidth(pFreehand))); + + restricted real[] dblDistortion = {1, -.5, .75, -.25, .25, -1, .5, -.75, + .25, -.25}; + int nDistortion = 0; + + guide g; + g = pFrom; + for ( int i = 1 ; i < nNumOfSubDivisions ; ++i ) + { + g = g .. + (pFrom + + pDiff * i / (real)nNumOfSubDivisions + + pPerpendicular * dblDistortion[nDistortion]); + nDistortion += 1; + if ( nDistortion > 9 ) + nDistortion = 0; + } + + if ( bIncludeTo ) + g = g .. pTo; + + return g; + } // end of MakeFreehand + + + +} // end of CAD + diff --git a/Build/source/utils/asymptote/base/animate.asy b/Build/source/utils/asymptote/base/animate.asy new file mode 100644 index 00000000000..586e71041b6 --- /dev/null +++ b/Build/source/utils/asymptote/base/animate.asy @@ -0,0 +1,3 @@ +usepackage("animate"); +import animation; + diff --git a/Build/source/utils/asymptote/base/animation.asy b/Build/source/utils/asymptote/base/animation.asy new file mode 100644 index 00000000000..1ce89c60e6e --- /dev/null +++ b/Build/source/utils/asymptote/base/animation.asy @@ -0,0 +1,188 @@ +/***** + * animation.asy + * Andy Hammerlindl and John Bowman 2005/11/06 + * + * Produce GIF, inline PDF, or other animations. + *****/ + +// animation delay is in milliseconds +real animationdelay=50; + +typedef frame enclosure(frame); + +frame NoBox(frame f) { + return f; +} + +enclosure BBox(real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill) { + return new frame(frame f) { + box(f,xmargin,ymargin,p,filltype,above=false); + return f; + }; +} + +struct animation { + picture[] pictures; + string[] files; + int index; + + string prefix; + bool global; // If true, use a global scaling for all frames; this requires + // extra memory since the actual shipout is deferred until all frames have + // been generated. + + void operator init(string prefix="", bool global=true) { + prefix=replace(stripdirectory(outprefix(prefix))," ","_"); + this.prefix=prefix; + this.global=global; + } + + string basename(string prefix=stripextension(prefix)) { + return "_"+prefix; + } + + string name(string prefix, int index) { + return stripextension(prefix)+"+"+string(index); + } + + private string nextname() { + string name=basename(name(prefix,index)); + ++index; + return name; + } + + void shipout(string name=nextname(), frame f) { + string format=nativeformat(); + plain.shipout(name,f,format=format,view=false); + files.push(name+"."+format); + } + + void add(picture pic=currentpicture, enclosure enclosure=NoBox) { + if(global) { + ++index; + pictures.push(pic.copy()); + } else this.shipout(enclosure(pic.fit())); + } + + void purge(bool keep=settings.keep) { + if(!keep) { + for(int i=0; i < files.length; ++i) + delete(files[i]); + } + } + + int merge(int loops=0, real delay=animationdelay, string format="gif", + string options="", bool keep=settings.keep) { + string args="-loop " +(string) loops+" -delay "+(string)(delay/10)+ + " -alpha Off -dispose Background "+options; + for(int i=0; i < files.length; ++i) + args += " " +files[i]; + int rc=convert(args,prefix+"."+format,format=format); + this.purge(keep); + if(rc == 0) animate(file=prefix+"."+format,format=format); + else abort("merge failed"); + return rc; + } + + void glmovie(string prefix=prefix, projection P=currentprojection) { + if(!view() || settings.render == 0 || settings.outformat == "html") return; + fit(prefix,pictures,view=true,P); + } + + // Export all frames with the same scaling. + void export(string prefix=prefix, enclosure enclosure=NoBox, + bool multipage=false, bool view=false, + projection P=currentprojection) { + if(pictures.length == 0) return; + if(!global) multipage=false; + bool inlinetex=settings.inlinetex; + if(multipage) + settings.inlinetex=false; + frame multi; + frame[] fits=fit(prefix,pictures,view=false,P); + for(int i=0; i < fits.length; ++i) { + string s=name(prefix,i); + if(multipage) { + add(multi,enclosure(fits[i])); + newpage(multi); + files.push(s+"."+nativeformat()); + } else { + if(pictures[i].empty3() || settings.render <= 0) + this.shipout(s,enclosure(fits[i])); + else // 3D frames + files.push(s+"."+nativeformat()); + } + } + if(multipage) { + plain.shipout(prefix,multi,view=view); + settings.inlinetex=inlinetex; + } + } + + string load(int frames, real delay=animationdelay, string options="", + bool multipage=false) { + if(!global) multipage=false; + string s="\animategraphics["+options+"]{"+format("%.18f",1000/delay,"C")+ + "}{"+basename(); + if(!multipage) s += "+"; + s += "}{0}{"+string(frames-1)+"}"; + return s; + } + + bool pdflatex() + { + return latex() && pdf(); + } + + string pdf(enclosure enclosure=NoBox, real delay=animationdelay, + string options="", bool keep=settings.keep, bool multipage=true) { + settings.twice=true; + if(settings.inlinetex) multipage=true; + if(!global) multipage=false; + if(!pdflatex()) + abort("inline pdf animations require -tex pdflatex or -tex xelatex"); + if(settings.outformat != "") settings.outformat="pdf"; + + string filename=basename(); + string pdfname=filename+".pdf"; + + if(global) + export(filename,enclosure,multipage=multipage); + + if(!keep) { + exitfcn currentexitfunction=atexit(); + void exitfunction() { + if(currentexitfunction != null) currentexitfunction(); + if(multipage || !settings.inlinetex) + this.purge(); + if(multipage && !settings.inlinetex) + delete(pdfname); + } + atexit(exitfunction); + } + + if(!multipage) + delete(pdfname); + + return load(index,delay,options,multipage); + } + + int movie(enclosure enclosure=NoBox, int loops=0, real delay=animationdelay, + string format=settings.outformat == "" ? "gif" : settings.outformat, + string options="", bool keep=settings.keep) { + if(global) { + if(format == "pdf") { + export(enclosure,multipage=true,view=true); + return 0; + } + export(enclosure); + } + return merge(loops,delay,format,options,keep); + } +} + +animation operator init() { + animation a=animation(); + return a; +} diff --git a/Build/source/utils/asymptote/base/annotate.asy b/Build/source/utils/asymptote/base/annotate.asy new file mode 100644 index 00000000000..aba9309cb65 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/asy-init.el b/Build/source/utils/asymptote/base/asy-init.el new file mode 100644 index 00000000000..0e3f178a228 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/asy-kate.sh b/Build/source/utils/asymptote/base/asy-kate.sh new file mode 100644 index 00000000000..5f482b376d9 --- /dev/null +++ b/Build/source/utils/asymptote/base/asy-kate.sh @@ -0,0 +1,146 @@ +#!/bin/sh +echo '<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE language SYSTEM "language.dtd"> +<!-- based on asy-keywords.el and Highlighting file asymptote.xml by Christoph Hormann--> +<language version="1.0" kateversion="3.2.2" name="asymptote" section="Sources" extensions="*.asy" mimetype="text/x-asymptote" licence="LGPL" author="Carsten Brenner"> + +<highlighting> +' > asymptote.xml + +# 1. Change Name of lists in <\list> <list name="..."> +# 2. tail to get rid of the first lines +# 3. building the right line ending +# 4-5. kill linebreaks +# 6. change spaces into <\item><item> +# 7. Undo change (7.) in 'list name' +# 8. do some formatting + +cat asy-keywords.el | sed 's/^(.*\-\([^\-]*\)\-.*/\n<list name="\1"><item>/' | tail -14 | sed 's/ ))/<\/item><\/list>/' | tr '\n' '@' | sed 's/@//g' | sed 's/ /<\/item><item>/g' | sed 's/list<\/item><item>name/list name/g' | sed 's/></>\n</g' >> asymptote.xml + +echo ' +<contexts> + <context attribute="Normal Text" lineEndContext="#stay" name="Normal"> + <DetectSpaces /> + <RegExpr attribute="Preprocessor" context="Outscoped" String="#\s*if\s+0" beginRegion="Outscoped" firstNonSpace="true" /> + <DetectChar attribute="Preprocessor" context="Preprocessor" char="#" firstNonSpace="true" /> + <StringDetect attribute="Region Marker" context="Region Marker" String="//BEGIN" beginRegion="Region1" firstNonSpace="true" /> + <StringDetect attribute="Region Marker" context="Region Marker" String="//END" endRegion="Region1" firstNonSpace="true" /> + <keyword attribute="Keyword" context="#stay" String="keyword" /> + <keyword attribute="Extensions" context="#stay" String="extensions" /> + <keyword attribute="Function" context="#stay" String="function" /> + <keyword attribute="Data Type" context="#stay" String="type" /> + <keyword attribute="Constants" context="#stay" String="constants" /> + <keyword attribute="Variable" context="#stay" String="variable" /> + <HlCChar attribute="Char" context="#stay"/> + <DetectChar attribute="String" context="String" char="""/> + <DetectIdentifier /> + <Float attribute="Float" context="#stay"> + <AnyChar String="fF" attribute="Float" context="#stay"/> + </Float> + <HlCOct attribute="Octal" context="#stay"/> + <HlCHex attribute="Hex" context="#stay"/> + <Int attribute="Decimal" context="#stay"> + <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/> + </Int> + <IncludeRules context="##Doxygen" /> + <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/> + <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/> + <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" /> + <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" /> + <AnyChar attribute="Symbol" context="#stay" String=":!%&()+,-/.*<=>?[]{|}~^;"/> + </context> + <context attribute="String" lineEndContext="#pop" name="String"> + <LineContinue attribute="String" context="#stay"/> + <HlCStringChar attribute="String Char" context="#stay"/> + <DetectChar attribute="String" context="#pop" char="""/> + </context> + <context attribute="Region Marker" lineEndContext="#pop" name="Region Marker"> + </context> + <context attribute="Comment" lineEndContext="#pop" name="Commentar 1"> + <DetectSpaces /> + <IncludeRules context="##Alerts" /> + <DetectIdentifier /> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Commentar 2"> + <DetectSpaces /> + <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/> + <IncludeRules context="##Alerts" /> + <DetectIdentifier /> + </context> + <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor"> + <LineContinue attribute="Preprocessor" context="#stay"/> + <RegExpr attribute="Preprocessor" context="Define" String="define.*((?=\\))"/> + <RegExpr attribute="Preprocessor" context="#stay" String="define.*"/> + <RangeDetect attribute="Prep. Lib" context="#stay" char=""" char1="""/> + <RangeDetect attribute="Prep. Lib" context="#stay" char="<" char1=">"/> + <IncludeRules context="##Doxygen" /> + <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/> + <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*"/> + </context> + <context attribute="Preprocessor" lineEndContext="#pop" name="Define"> + <LineContinue attribute="Preprocessor" context="#stay"/> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor"> + <DetectSpaces /> + <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" /> + <DetectIdentifier /> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Outscoped" > + <DetectSpaces /> + <IncludeRules context="##Alerts" /> + <DetectIdentifier /> + <DetectChar attribute="String" context="String" char="""/> + <IncludeRules context="##Doxygen" /> + <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/> + <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/> + <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="Outscoped" firstNonSpace="true" /> + <RegExpr attribute="Preprocessor" context="#pop" String="#\s*(endif|else|elif)" endRegion="Outscoped" firstNonSpace="true" /> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern"> + <DetectSpaces /> + <IncludeRules context="##Alerts" /> + <DetectIdentifier /> + <DetectChar attribute="String" context="String" char="""/> + <IncludeRules context="##Doxygen" /> + <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/> + <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/> + <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="Outscoped" firstNonSpace="true"/> + <RegExpr attribute="Comment" context="#pop" String="#\s*endif" endRegion="Outscoped" firstNonSpace="true"/> + </context> + </contexts> + <itemDatas> + <itemData name="Char" defStyleNum="dsChar"/> + <itemData name="Comment" defStyleNum="dsComment"/> + <itemData name="Data Type" defStyleNum="dsDataType"/> + <itemData name="Decimal" defStyleNum="dsDecVal"/> + <itemData name="Extensions" defStyleNum="dsKeyword" color="#0095ff" selColor="#ffffff" bold="1" italic="0"/> + <itemData name="Float" defStyleNum="dsFloat"/> + <itemData name="Function" defStyleNum="dsFunction" /> + <itemData name="Hex" defStyleNum="dsBaseN"/> + <itemData name="Keyword" defStyleNum="dsKeyword"/> + <itemData name="Normal Text" defStyleNum="dsNormal"/> + <itemData name="Octal" defStyleNum="dsBaseN"/> + <itemData name="Prep. Lib" defStyleNum="dsOthers"/> + <itemData name="Preprocessor" defStyleNum="dsOthers"/> + <itemData name="Region Marker" defStyleNum="dsRegionMarker" /> + <itemData name="String Char" defStyleNum="dsChar"/> + <itemData name="String" defStyleNum="dsString"/> + <itemData name="Symbol" defStyleNum="dsNormal"/> + <itemData name="Variable" defStyleNum="dsOthers" /> + </itemDatas> + </highlighting> + <general> + <comments> + <comment name="singleLine" start="//" /> + <comment name="multiLine" start="/*" end="*/" region="Comment"/> + </comments> + <keywords casesensitive="1" /> + </general> + </language>' >> asymptote.xml diff --git a/Build/source/utils/asymptote/base/asy-mode.el b/Build/source/utils/asymptote/base/asy-mode.el new file mode 100644 index 00000000000..ec3a3746d74 --- /dev/null +++ b/Build/source/utils/asymptote/base/asy-mode.el @@ -0,0 +1,1600 @@ +;;; asy-mode.el --- Major mode for editing Asymptote source code. + +;; Copyright (C) 2006-8 + +;; Author: Philippe IVALDI 20 August 2006 +;; Maintainer: John Bowman +;; URL: https://github.com/vectorgraphics/asymptote +;; Version: 1.6 +;; Keywords: language, mode + +;;; License: + +;; 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 + +;; Major 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. + +;;; Code: + +(defvar asy-mode-version "1.6") + +;;;###autoload +(define-derived-mode asy-mode objc-mode "Asymptote" + "Emacs mode for editing Asymptote source code. +For full functionality the `two-mode-mode' package should also be installed +(http://www.dedasys.com/freesoftware/files/two-mode-mode.el). + +I. This package provides two modes: +1- asy-mode: +All the files with the extension '.asy' are edited in this mode, which provides the following features: +* Syntax color highlighting; +* Compiling and viewing current buffer with the key binding C-c C-c; +* Moving cursor to the error by pressing the key F4. +* Showing the available function prototypes for the command at the cursor with the key binding C-c ? +* Compiling and viewing a TeX document linked with the current buffer (usually a document that includes the output picture). +To link a Tex document try 'M-x asy-set-master-tex' follow by C-Return (see descriptions further of the key binding C-Return, C-S-Return, M-Return, M-S-Return etc within 2- lasy-mode) + +2- lasy-mode +Editing a TeX file that contains Asymptote code is facilitated with the hybrid mode 'lasy-mode'. +Toggle lasy-mode with M-x lasy-mode. +In this hybrid mode the major mode is LaTeX when the cursor is in LaTeX code and becomes asy-mode when the cursor is between '\\begin{asy}' and '\\end{asy}'. +All the features of asy-mode are provided and the key binding C-c C-c of asy-mode compiles and views only the code of the picture where the cursor is. +Note that some keys binding are added to the LaTeX-mode-map in lasy-mode if the value of the variable lasy-extra-key is t (the default) +. +* C-return: compile (if the buffer/file is modified) and view the PostScript output with sequence [latex->[asy->latex]->dvips]->PSviewer +* M-return: same with pdf output and with the sequence [pdflatex->[asy->pdflatex]]->PDFviewer +* C-M-return: same with pdf output and with the sequence [latex->[asy->latex]->dvips->ps2pdf]->PSviewer +* Add the Shift key to the sequence of keys to compile even if the file is not modified. + +II. To add a menu bar in current 'latex-mode' buffer and activate hot keys, use 'M-x asy-insinuate-latex <RET>'. +You can automate this feature for all the 'latex-mode' buffers by inserting the five following lines in your .emacs initialization file: +(eval-after-load \"latex\" + '(progn + ;; Add here your personal features for 'latex-mode': + (asy-insinuate-latex t) ;; Asymptote globally insinuates Latex. + )) + +You can access this help within Emacs by the key binding C-h f asy-mode <RET> + +BUGS: +This package has been tested in: +* Linux Debian Etch +- GNU Emacs 22.0.50.1 +- GNU Emacs 21.4.1 (only basic errors management and basic font-lock features within lasy-mode are supported) +* WindowsXP +- GNU Emacs 22.0.990.1 (i386-mingw-nt5.1.2600) + +This package seems to work with XEmacs 21.4 but not all the features are available (in particular syntax highlighting). + +Report bugs to https://github.com/vectorgraphics/asymptote/issues + +Some variables can be customized: M-x customize-group <RET> asymptote <RET>." + + (setq c++-font-lock-extra-types (cons "true" c++-font-lock-extra-types))) + +(require 'font-lock) +(require 'cc-mode) +(require 'cl) ;; Common Lisp extensions for Emacs +(require 'compile) +(require 'wid-edit) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.asy$" . asy-mode)) + +(defvar running-xemacs-p (featurep 'xemacs)) +(defvar running-unix-p (not (string-match "windows-nt\\|ms-dos" (symbol-name system-type)))) + +(when running-xemacs-p + (defalias 'turn-on-font-lock-if-enabled 'ignore) + (defalias 'line-number-at-pos 'line-number) + (defvar temporary-file-directory (temp-directory)) + (defun replace-regexp-in-string (regexp rep string) + (replace-in-string string regexp rep)) + ) + +(when (or (< emacs-major-version 22) running-xemacs-p) + ;; Add regexp for parsing the compilation errors of asy + (add-to-list 'compilation-error-regexp-alist + '("\\(.*?.asy\\): \\(.*?\\)\\.\\(.*?\\):" 1 2 3))) + +(when (< emacs-major-version 22) + (defun line-number-at-pos (&optional pos) + "Return (narrowed) buffer line number at position POS. +If POS is nil, use current buffer location. +Counting starts at (point-min), so the value refers +to the contents of the accessible portion of the buffer." + (let ((opoint (or pos (point))) start) + (save-excursion + (goto-char (point-min)) + (setq start (point)) + (goto-char opoint) + (forward-line 0) + (1+ (count-lines start (point))))))) + +(defcustom lasy-extra-key t + "* If on, the folowing binding keys are added in lasy-mode : + (define-key lasy-mode-map (kbd \"<C-return>\") 'lasy-view-ps) + (define-key lasy-mode-map (kbd \"<C-S-return>\") 'asy-master-tex-view-ps-f) + (define-key lasy-mode-map (kbd \"<M-return>\") 'lasy-view-pdf-via-pdflatex) + (define-key lasy-mode-map (kbd \"<M-S-return>\") 'asy-master-tex-view-pdflatex-f) + (define-key lasy-mode-map (kbd \"<C-M-return>\") 'lasy-view-pdf-via-ps2pdf) + (define-key lasy-mode-map (kbd \"<C-M-S-return>\") 'asy-master-tex-view-ps2pdf-f) + +If you also want this feature in pure latex-mode, you can set this variable to `nil' and add these lines in your .emacs: + +(require 'asy-mode) +(eval-after-load \"latex\" + '(progn + (define-key LaTeX-mode-map (kbd \"<C-return>\") 'lasy-view-ps) + (define-key LaTeX-mode-map (kbd \"<C-S-return>\") 'asy-master-tex-view-ps-f) + (define-key LaTeX-mode-map (kbd \"<M-return>\") 'lasy-view-pdf-via-pdflatex) + (define-key LaTeX-mode-map (kbd \"<M-S-return>\") 'asy-master-tex-view-pdflatex-f) + (define-key LaTeX-mode-map (kbd \"<C-M-return>\") 'lasy-view-pdf-via-ps2pdf) + (define-key LaTeX-mode-map (kbd \"<C-M-S-return>\") 'asy-master-tex-view-ps2pdf-f)))" + :type 'boolean + :group 'asymptote) + +(defcustom asy-compilation-buffer 'none + " 'visible means keep compilation buffer visible ; + 'available means keep compilation buffer available in other buffer but not visible; + 'none means delete compilation buffer automatically after a *successful* compilation. + 'never means don't open any window or buffer attached to the compilation process. +If the value is 'never': +* Emacs is suspended until the child program returns; +* the management of errors is poorer than with other value; +* the compilation doesn't modify your current window configuration." + :type '(choice (const visible) (const available) (const none) (const never)) + :group 'asymptote) + +(defcustom lasy-ask-about-temp-compilation-buffer t + "* If t, ask before visiting a temporary buffer of compilation." + :type 'boolean + :group 'asymptote) + +(defcustom lasy-compilation-inline-auto-detection nil + "* If t, lasy-mode detects automatically if the option 'inline' is passed to asymptote.sty. +In case of 'inline' option, the compilation of a figure separately of the document is processed by rebuilding the preamble and compiling it as a file '.tex' containing only this picture. + If nil (the default), the compilation of a figure separately of the document is processed by building a file '.asy', without the features of the LaTeX preamble." + :type 'boolean + :group 'asymptote) + +(defcustom asy-command-location "" + "* If not in the path, you can put here the name of the directory containing Asy's binary files. +this variable must end in /." + :type 'directory + :group 'asymptote) + +(defcustom asy-command "asy -V" + "* Command invoked to compile a Asymptote file. +You can define the location of this command with the variable `asy-command-location'." + :type 'string + :group 'asymptote) + +(defcustom lasy-command "asy" + "* Command invoked to compile a Asymptote file generated compiling a .tex file. +You can define the location of this command with the variable `asy-command-location'." + :type 'string + :group 'asymptote) + +(defcustom lasy-latex-command "latex -halt-on-error" + "* Command invoked to compile a .tex file with LaTeX." + :type 'string + :group 'asymptote) + +(defcustom lasy-pdflatex-command "pdflatex -halt-on-error" + "* Command invoked to compile a .tex file with pdflaTex." + :type 'string + :group 'asymptote) + +(defcustom lasy-dvips-pre-pdf-command "dvips -Ppdf" + "* Command invoked to convert a .dvi file to a temporary .ps file in order to +generate a final .pdf file." + :type 'string + :group 'asymptote) + +(defcustom lasy-dvips-command "dvips -q" + "* Command invoked to convert a .dvi file to a final .ps file." + :type 'string + :group 'asymptote) + +(defcustom lasy-ps2pdf-command "ps2pdf14" + "* Command invoked to convert a .dvi file to .ps file." + :type 'string + :group 'asymptote) + +(defcustom asy-temp-dir temporary-file-directory + "*The name of a directory for Asy's temporary files. +Such files are generated by functions like +`asy-compile' when lasy-mode is enable." + :type 'directory + :group 'asymptote) + +(defcustom ps-view-command (if running-unix-p "gv" "") + "Command to view a PostScript file generated by compiling a tex file within lasy-mode. +This variable is not used when running the Windows OS. +See `asy-open-file'." + :type 'string + :group 'asymptote) + +(defcustom pdf-view-command + (if running-unix-p + "xpdf" "") + "Command to view a pdf file generated by compiling a tex file within lasy-mode. +This variable is not used when running the Windows OS. +See `asy-open-file'." + :type 'string + :group 'asymptote) + +(defvar asy-TeX-master-file nil + "TeX file associate with current asymptote code. +This variable must be modified only using the function 'asy-set-master-tex by M-x asy-set-master-tex <RET>.") +(make-variable-buffer-local 'asy-TeX-master-file) + +(defvar lasy-compile-tex nil + "* Internal use. t if LaTeX compilation come from latex-mode.") + +(when (fboundp 'font-lock-add-keywords) + (if (< max-specpdl-size 2000) (setq max-specpdl-size 2000)) + (defun asy-add-function-keywords (function-keywords face-name) + (let* ((keyword-list (mapcar #'(lambda (x) + (symbol-name x)) + function-keywords)) + (keyword-regexp (concat "\\<\\(" + (regexp-opt keyword-list) + "\\)("))) + (font-lock-add-keywords 'asy-mode + `((,keyword-regexp 1 ',face-name))))) + + (defun asy-add-variable-keywords (function-keywords face-name) + (let* ((keyword-list (mapcar #'(lambda (x) + (symbol-name x)) + function-keywords)) + (keyword-regexp (concat "\\<[0-9]*\\(" + (regexp-opt keyword-list) + "\\)\\(?:[^(a-zA-Z]\\|\\'\\)"))) + (font-lock-add-keywords 'asy-mode + `((,keyword-regexp 1 ',face-name))))) + + ;; External definitions of keywords: + ;; asy-function-name and asy-variable-name + (if (locate-library "asy-keywords.el") + (load "asy-keywords.el") + (progn + ;; Use dummy keyword definitions if asy-keywords.el is not found: + (defvar asy-keyword-name nil) + (defvar asy-type-name nil) + (defvar asy-function-name nil) + (defvar asy-variable-name nil))) + + (defcustom asy-extra-type-name '() + "Extra user type names highlighted with 'font-lock-type-face" + :type '(repeat symbol) + :group 'asymptote) + + (defcustom asy-extra-function-name + '() + "Extra user function names highlighted with 'font-lock-function-name-face" + :type '(repeat symbol) + :group 'asymptote) + + (defcustom asy-extra-variable-name '() + "Extra user variable names highlighted with 'font-lock-constant-face" + :type '(repeat symbol) + :group 'asymptote) + + (asy-add-variable-keywords + asy-keyword-name + 'font-lock-builtin-face) + + (asy-add-variable-keywords + (nconc asy-type-name asy-extra-type-name) + 'font-lock-type-face) + + (asy-add-function-keywords + (nconc asy-function-name asy-extra-function-name) + 'font-lock-function-name-face) + + (asy-add-variable-keywords + (nconc asy-variable-name asy-extra-variable-name) + 'font-lock-constant-face) + + (defface asy-environment-face + `((t + (:underline t :inverse-video t))) + "Face used to highlighting the keywords '\\begin{asy}' and '\\end{asy}' within lasy-mode." + :group 'asymptote) + + (font-lock-add-keywords + 'asy-mode + '(("\\\\begin{asy}.*" . 'asy-environment-face) + ("\\\\end{asy}" . 'asy-environment-face))) + + (defface asy-link-face ;; widget-field-face + `((t + (:underline t))) + "Face used to highlighting the links." + :group 'asymptote) + + (font-lock-add-keywords + 'asy-mode + '(("\\[.*?\\.asy\\]" . 'asy-link-face))) + ) + +(setq buffers-menu-max-size nil) +(setq mode-name "Asymptote") + +(if running-xemacs-p + (defvar asy-menu + '("Asy" + ["Toggle lasy-mode" lasy-mode :active (and (featurep 'two-mode-mode) two-mode-bool)] + ["Compile/View" asy-compile t] + ["Go to error" asy-goto-error t] + ["Describe command" asy-show-function-at-point t]"--" + ("Master TeX file" + ["Set/Change value" (asy-set-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool))] + ["Erase value" (asy-unset-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool))] + ("Compile OR View" + ["PS" asy-master-tex-view-ps :active t] + ["PDF (pdflatex)" asy-master-tex-view-pdflatex :active t] + ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf :active t]) + ("Compile AND View" + ["PS" asy-master-tex-view-ps-f :active t] + ["PDF (pdflatex)" asy-master-tex-view-pdflatex-f :active t] + ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf-f :active t])) + ["Asymptote insinuates globally LaTeX" asy-insinuate-latex-globally :active (not asy-insinuate-latex-globally-p)]"--" + ("Debugger Buffer" + ["Visible" (setq asy-compilation-buffer 'visible) :style radio :selected (eq asy-compilation-buffer 'visible) :active t] + ["Available" (setq asy-compilation-buffer 'available) :style radio :selected (eq asy-compilation-buffer 'available) :active t] + ["None" (setq asy-compilation-buffer 'none) :style radio :selected (eq asy-compilation-buffer 'none) :active t] + ["Never" (setq asy-compilation-buffer 'never) :style radio :selected (eq asy-compilation-buffer 'never) :active t]) + ("Compilation Options" :included (and (featurep 'two-mode-mode) two-mode-bool) + ["Enable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection t) :style radio :selected lasy-compilation-inline-auto-detection :active t] + ["Disable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection nil) :style radio :selected (not lasy-compilation-inline-auto-detection) :active t]) + ["Customize" (customize-group "asymptote") :active t] + ["Help" (describe-function 'asy-mode) :active t] + )) + (defvar asy-menu + '("Asy" + ["Toggle Lasy-Mode" lasy-mode :visible (and (featurep 'two-mode-mode) two-mode-bool)] + ["Compile/View" asy-compile t] + ["Go to Error" asy-goto-error t] + ["Describe Command" asy-show-function-at-point t]"--" + ("Master TeX File" + ["Set/Change Value" (asy-set-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool)) :key-sequence nil] + ["Erase Value" (asy-unset-master-tex) :active (not (and (boundp two-mode-bool) two-mode-bool)) :key-sequence nil] + ("Compile or View" + ["PS" asy-master-tex-view-ps :active t] + ["PDF (pdflatex)" asy-master-tex-view-pdflatex :active t] + ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf :active t]) + ("Compile and View" + ["PS" asy-master-tex-view-ps-f :active t] + ["PDF (pdflatex)" asy-master-tex-view-pdflatex-f :active t] + ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf-f :active t])) + ["Asymptote Insinuates Globally LaTeX" asy-insinuate-latex-globally :active (not asy-insinuate-latex-globally-p)]"--" + ("Debugger Buffer" + ["Visible" (setq asy-compilation-buffer 'visible) :style radio :selected (eq asy-compilation-buffer 'visible) :active t :key-sequence nil] + ["Available" (setq asy-compilation-buffer 'available) :style radio :selected (eq asy-compilation-buffer 'available) :active t :key-sequence nil] + ["None" (setq asy-compilation-buffer 'none) :style radio :selected (eq asy-compilation-buffer 'none) :active t :key-sequence nil] + ["Never" (setq asy-compilation-buffer 'never) :style radio :selected (eq asy-compilation-buffer 'never) :active t :key-sequence nil]) + ("Compilation Options" :visible (and (featurep 'two-mode-mode) two-mode-bool) + ["Enable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection t) :style radio :selected lasy-compilation-inline-auto-detection :active t :key-sequence nil] + ["Disable Automatic Detection of Option" (setq lasy-compilation-inline-auto-detection nil) :style radio :selected (not lasy-compilation-inline-auto-detection) :active t :key-sequence nil]) + ["Customize" (customize-group "asymptote") :active t :key-sequence nil] + ["Help" (describe-function 'asy-mode) :active t :key-sequence nil] + ))) +(easy-menu-define asy-mode-menu asy-mode-map "Asymptote Mode Commands" asy-menu) +;; On the hook for XEmacs only. +(if running-xemacs-p + (add-hook 'asy-mode-hook + (lambda () + (and (eq major-mode 'asy-mode) + (easy-menu-add asy-mode-menu asy-mode-map))))) + +(defun asy-protect-file-name(Filename) + (concat "\"" Filename "\"")) + +(defun asy-get-temp-file-name(&optional noext) + "Get a temp file name for printing." + (if running-xemacs-p + (concat (make-temp-name asy-temp-dir) (if noext "" ".asy")) + (concat (make-temp-file + (expand-file-name "asy" asy-temp-dir)) (if noext "" ".asy")))) + +(defun asy-log-filename() + (concat buffer-file-name ".log")) + +(defun asy-compile() + "Compile Asymptote code." + (interactive) + (if (and (boundp two-mode-bool) two-mode-bool) + (lasy-compile) ;; compile asy code in a TeX file. + (progn ;; compile asy code in a asy file. + (let* + ((buffer-base-name (file-name-sans-extension (file-name-nondirectory buffer-file-name))) + (asy-compile-command + (concat asy-command-location asy-command + (if (eq asy-compilation-buffer 'never) + " " " -wait ") + (asy-protect-file-name buffer-base-name)))) + (if (buffer-modified-p) (save-buffer)) + (message "%s" asy-compile-command) + (asy-internal-compile asy-compile-command t t))))) + +(defun asy-error-message(&optional P) + (let ((asy-last-error + (asy-log-field-string + (asy-log-filename) 0))) + (if (and asy-last-error (not (string= asy-last-error ""))) + (message (concat asy-last-error (if P "\nPress F4 to go to error" ""))) + (when (and (boundp two-mode-bool) two-mode-bool lasy-run-tex (not (zerop asy-last-compilation-code))) + (message "The LaTeX code may be incorrect."))))) + +(defun asy-log-field-string(Filename Field) + "Return field of first line of file filename. +Fields are defined as 'field1: field2.field3:field4' . Field=0 <-> all fields" + (let ((view-inhibit-help-message t)) + (with-temp-buffer + (progn + (insert-file Filename) + (beginning-of-buffer) + (if (re-search-forward "^\\(.*?\\): \\(.*?\\)\\.\\(.*?\\):\\(.*\\)$" (point-max) t) + (match-string Field) nil))))) + +(defun asy-next-error(arg reset) + (if (> emacs-major-version 21) + (next-error arg reset) + (next-error arg))) + +(defun lasy-ask-visit-tem-compilation-buffer() + "* Ask before visiting a temporary compilation buffer depending the value of `lasy-ask-about-temp-compilation-buffer'." + (if lasy-ask-about-temp-compilation-buffer + (y-or-n-p "Visit temporary buffer of compilation ? ") t)) + +(defun lasy-place-cursor-to-error(Filename li co) + (save-excursion + (with-temp-buffer + (insert-file-contents + (if running-unix-p Filename + (replace-regexp-in-string + "//" ":/" + (replace-regexp-in-string "/cygdrive/" "" Filename)))) ;; Not right, +;;;maybe take a look at the code of compilation-find-file + (beginning-of-buffer) + (next-line (1- (string-to-number li))) + (setq line-err + (buffer-substring-no-properties + (progn (beginning-of-line) (point)) + (progn (end-of-line) (point)))))) + (beginning-of-buffer) + (search-forward line-err) + (beginning-of-line) + (forward-char (1- (string-to-number co)))) + +(defun asy-goto-error(&optional arg reset) + "Go to point of last error within asy/lasy-mode." + (interactive "P") + (if (or (eq asy-compilation-buffer 'never) + (and (boundp two-mode-bool) two-mode-bool)) + (let* ((log-file (asy-log-filename)) + (li_ (asy-log-field-string log-file 2)) + (co_ (asy-log-field-string log-file 3))) + (if (and (boundp two-mode-bool) two-mode-bool) ;; Within Lasy-mode + (progn ;; lasy-mode need the compilation of file.tex + ;; the error can be in Tex commands or in Asymptote commands + (if (eq asy-compilation-buffer 'never) ;; Find error in the log file. + (if li_ ;; Asy error found in the log-file + (progn + (lasy-place-cursor-to-error + (asy-log-field-string log-file 1) li_ co_) + (asy-error-message)) + (message "There is an error in your LaTeX code...")) + (if (or running-xemacs-p (< emacs-major-version 22)) + (when (lasy-ask-visit-tem-compilation-buffer) + (next-error arg)) + (let ((msg)) ;; Find error in the compilation buffer + (save-excursion + (set-buffer (next-error-find-buffer)) + (when reset + (setq compilation-current-error nil)) + (let* ((columns compilation-error-screen-columns) + (last 1) + (loc (compilation-next-error (or arg 1) nil + (or compilation-current-error + compilation-messages-start + (point-min)))) + (end-loc (nth 2 loc)) + (marker (point-marker))) + (setq compilation-current-error (point-marker) + overlay-arrow-position + (if (bolp) + compilation-current-error + (copy-marker (line-beginning-position))) + loc (car loc))) + (if (re-search-forward "^\\(.*?\\): \\(.*?\\)\\.\\(.*?\\):\\(.*\\)$" (point-max) t) + (progn + (setq msg (match-string 0) + log-file (match-string 1) + li_ (match-string 2) + co_ (match-string 3))) + (error "Not other errors."))) + (lasy-place-cursor-to-error log-file li_ co_) + (message msg))))) + (if li_ ;;Pure asy-mode and compilation with shell-command + (progn + (goto-line (string-to-number li_)) + (forward-char (1- (string-to-number co_))) + (asy-error-message)) + (progn (message "No error."))))) + (asy-next-error arg reset))) + +(defun asy-grep (Regexp) + "Internal function used by asymptote." + (let ((Strout "") + (case-fold-search-asy case-fold-search)) + (progn + (beginning-of-buffer) + (setq case-fold-search nil) + (while (re-search-forward Regexp (point-max) t) + (setq Strout (concat Strout (match-string 0) "\n\n"))) + (setq case-fold-search case-fold-search-asy) + (if (string= Strout "") "No match.\n" Strout)))) + +(defun asy-widget-open-file-at-pos (widget &optional event) + "" + (kill-buffer (current-buffer)) + (find-file (widget-get widget :follow-link)) + (goto-line (string-to-number (widget-get widget :value)))) + +(defun asy-show-function-at-point() + "Show the Asymptote definitions of the command at point." + (interactive) + (save-excursion + (let ((cWord (current-word)) + (cWindow (selected-window))) + (switch-to-buffer-other-window "*asy-help*") + (fundamental-mode) + (setq default-directory "/") + (if (> emacs-major-version 21) + (call-process-shell-command + (concat asy-command-location "asy -l --where") nil t nil) + (insert (shell-command-to-string "asy -l --where"))) + (let ((rHelp (asy-grep (concat "^.*\\b" cWord "(\\(.\\)*?$"))) + (tag)(file)(line)) + (erase-buffer) + (insert rHelp) + (beginning-of-buffer) + (while (re-search-forward "\\(.*\\): \\([0-9]*\\)\\.\\([0-9]*\\)" (point-max) t) + (setq file (match-string 1) + line (match-string 2) + tag (file-name-nondirectory file)) + (widget-create `(file-link + :tag ,tag + :follow-link ,file + :value ,line + :action asy-widget-open-file-at-pos + )))) + (beginning-of-buffer) + (while (re-search-forward "\\(.*: [0-9]*\\.[0-9]*\\)" (point-max) t) + (replace-match "")) + (asy-mode) + (use-local-map widget-keymap) + (widget-setup) + (goto-char (point-min)) + (select-window cWindow)))) + +(add-hook 'asy-mode-hook + (lambda () + (c-set-style "gnu"); + (c-set-offset (quote topmost-intro-cont) 0 nil) + (make-local-variable 'c-label-minimum-indentation) + (setq c-label-minimum-indentation 0) + (when (fboundp 'flyspell-mode) (flyspell-mode -1)) + (turn-on-font-lock) + (column-number-mode t) + )) + + +;;;###autoload (defun lasy-mode ()) +;;; ************************************ +;;; asy-mode mixed with LaTeX-mode: lasy +;;; ************************************ +(if (locate-library "two-mode-mode") + (progn + + (defvar lasy-fontify-asy-p nil + "Variable to communicate with `font-lock-unfontify-region'. +Internal use, don't set in any fashion.") + (setq lasy-fontify-asy-p nil) + + (eval-after-load "two-mode-mode" + '(progn + ;; Redefine `two-mode-mode-update-mode' to use regexp. + (defun two-mode-mode-update-mode () + "Redefined in `asy-mode.el' to use regexp" + (when (and two-mode-bool two-mode-update) + (setq two-mode-update 0) + (let ((mode-list second-modes) + (flag 0)) + (while mode-list + (let ((mode (car mode-list)) + (lm -1) + (rm -1)) + (save-excursion + (if (search-backward-regexp (cadr mode) nil t) + (setq lm (point)) + (setq lm -1))) + (save-excursion + (if (search-backward-regexp (car (cddr mode)) nil t) + (setq rm (point)) + (setq rm -1))) + (if (and (not (and (= lm -1) (= rm -1))) (>= lm rm)) + (progn + (setq flag 1) + (setq mode-list '()) + (two-mode-change-mode (car mode) (car (cdr (cddr mode))))))) + (setq mode-list (cdr mode-list))) + (if (= flag 0) + (two-mode-change-mode (car default-mode) (cadr default-mode)))))) + + (defun two-mode-change-mode (to-mode func) + "Redefined in asy-mode. +Change the variable `lasy-fontify-asy-p' according to the value of func and +the current mode." + (if (string= to-mode mode-name) + t + (progn + (setq lasy-fontify-asy-p (eq func 'asy-mode)) + (funcall func) + (hack-local-variables) + (two-mode-mode-setup) + (if two-mode-switch-hook + (run-hooks 'two-mode-switch-hook)) + (if (eq font-lock-mode t) + (font-lock-fontify-buffer)) + (turn-on-font-lock-if-enabled)))) + )) + + + (require 'two-mode-mode) + + (defun lasy-mode () + "Treat, in some cases, the current buffer as a literal Asymptote program." + (interactive) + (save-excursion + (let ((prefix + (progn + (goto-char (point-max)) + (re-search-backward "^\\([^\n]+\\)Local Variables:" + (- (point-max) 3000) t) + (match-string 1))) + (pos-b (point))) + (when + (and prefix + (progn + (re-search-forward (regexp-quote + (concat prefix + "End:")) (point-max) t) + (re-search-backward (concat "\\(" prefix "mode: .*\\)") pos-b t)) + ) + (error (concat "lasy-mode can not work if a mode is specified as local file variable. +You should remove the line " (int-to-string (line-number-at-pos))))))) + (set (make-local-variable 'asy-insinuate-latex-p) asy-insinuate-latex-p) + (make-local-variable 'lasy-fontify-asy-p) + (when (< emacs-major-version 22) + (make-local-variable 'font-lock-keywords-only)) + (setq default-mode '("LaTeX" latex-mode) + second-modes '(("Asymptote" + "^\\\\begin{asy}.*$" + "^\\\\end{asy}" + asy-mode))) + (if two-mode-bool + (progn + (latex-mode) + (asy-insinuate-latex)) + (progn + (two-mode-mode) + ))) + + (when (not running-xemacs-p) + (defadvice TeX-command-master (around asy-choose-compile act) + "Hack to circumvent the preempt of 'C-c C-c' by AucTeX within `lasy-mode'." + (if (string-match "asymptote" (downcase mode-name)) + (asy-compile) + ad-do-it))) + + (add-hook 'two-mode-switch-hook + (lambda () + (if (eq major-mode 'latex-mode) + (progn ;; Switch to latex-mode + ;; Disable LaTeX-math-Mode within lasy-mode (because of incompatibility) + (when LaTeX-math-mode (LaTeX-math-mode -1)) + (asy-insinuate-latex) + (when (< emacs-major-version 22) + (setq font-lock-keywords-only nil))) + (progn ;; Switch to asy-mode + (when (< emacs-major-version 22) + (setq font-lock-keywords-only t)) + )))) + ;; (setq two-mode-switch-hook nil) + + ;; Solve a problem restoring a TeX file via desktop.el previously in lasy-mode. + (if (boundp 'desktop-buffer-mode-handlers) + (progn + (defun asy-restore-desktop-buffer (desktop-b-f-name d-b-n d-b-m) + (find-file desktop-b-f-name)) + (add-to-list 'desktop-buffer-mode-handlers + '(asy-mode . asy-restore-desktop-buffer)))) + + ;; Functions and 'advises' to restrict 'font-lock-unfontify-region' + ;; and 'font-lock-fontify-syntactically-region' within lasy-mode + ;; Special thanks to Olivier Ramaré for his help. + (when (and (fboundp 'font-lock-add-keywords) (> emacs-major-version 21)) + (defun lasy-mode-at-pos (pos &optional interior strictly) + "If point at POS is in an asy environment return the list (start end)." + (save-excursion + (save-match-data + (goto-char pos) + (let* ((basy + (progn + (unless strictly (end-of-line)) + (when (re-search-backward "^\\\\begin{asy}" (point-min) t) + (when interior (next-line)) + (point)))) + (easy + (and basy + (progn + (when (re-search-forward "^\\\\end{asy}" (point-max) t) + (when interior (previous-line)(beginning-of-line)) + (point)))))) + (and basy easy + (> pos (- basy (if interior 12 0))) + (< pos (+ easy (if interior 10 0))) + (list basy easy)))))) + + (defun lasy-region (start end &optional interior) + "If the region 'start to end' contains the beginning or +the end of an asy environment return the list of points where +the asy environment starts and ends." + (let* ((beg (min start end)) + (lim (max start end))) + (or (lasy-mode-at-pos beg interior) + (save-match-data + (save-excursion + (goto-char beg) + (and (re-search-forward "^\\\\begin{asy}" lim t) + (lasy-mode-at-pos (point) interior))))))) + + (defun lasy-tags (start end) + "Return associated list of points where the tags starts and ends +restricted to the region (start end). +\"b\" associated with (start-beginTag end-beginTag), +\"e\" associated with (start-endTag end-endTag)." + (let* + ((beg (min start end)) + (lim (max start end)) + out) + (save-excursion + (goto-char beg)(beginning-of-line) + (while + (when (re-search-forward "^\\\\begin{asy}.*" lim t) + (push (list + (progn (beginning-of-line)(point)) + (progn (end-of-line)(point))) out))) + (goto-char beg)(beginning-of-line) + (while + (when (re-search-forward "^\\\\end{asy}" lim t) + (push (list + (progn (beginning-of-line)(point)) + (progn (end-of-line)(point))) out))) + out))) + + (defun lasy-restrict-region (start end &optional interior) + "If the region 'start to end' contains the beginning or +the end of an asy environment, returns the list of points wich +restricts the region to the asy environment. +Else, return (start end)." + (let* + ((beg (min start end)) + (lim (max start end)) + (be (if (lasy-mode-at-pos beg) + beg + (or (save-excursion + (goto-char beg) + (when (re-search-forward "^\\\\begin{asy}.*" lim t) + (unless interior (beginning-of-line)) + (point))) + beg))) + (en (or (save-excursion + (goto-char be) + (when (re-search-forward "^\\\\end{asy}" lim t) + (when interior (beginning-of-line)) + (point))) + lim))) + (list be en))) + + (defun lasy-parse-region (start end) + "Return a list ((a (start1 end1)) (b (start2 end2)) [...]). +where a, b, ... are nil or t; t means the region from 'startX' through 'endX' (are points) +is in a asy environnement." + (let (regasy out rr brr err tags) + (save-excursion + (goto-char start) + (while (< (point) end) + (setq regasy (lasy-region (point) end)) + (if regasy + (progn + (setq rr (lasy-mode-at-pos (point))) + (setq brr (and rr (nth 0 rr)) + err (and rr (nth 1 rr))) + (if rr + (progn + (push (list t (list (max 1 (1- (point))) (min end err))) out) + (goto-char (min end err))) + (progn + (push (list nil (list (point) (nth 0 regasy))) out) + (goto-char (1+ (nth 0 regasy)))))) + (progn + (push (list nil (list (min (1+ (point)) end) end)) out) + (goto-char end))) + )) + ;; Put start and end of tag in latex fontification. + (setq tags (lasy-tags start end)) + (dolist (tag tags) (push (list nil tag) out)) + (reverse out))) + + (defadvice font-lock-unfontify-region + (around asy-font-lock-unfontify-region (beg end)) + (if two-mode-bool + (let ((rstate (lasy-parse-region beg end)) + curr reg asy-fontify latex-fontify) + (while (setq curr (pop rstate)) + (setq reg (nth 1 curr)) + (setq asy-fontify (and (nth 0 curr) lasy-fontify-asy-p) + latex-fontify (and (not (nth 0 curr)) + (not lasy-fontify-asy-p))) + (when (or asy-fontify latex-fontify) + (setq beg (nth 0 reg) + end (nth 1 reg)) + (save-excursion + (save-restriction + (narrow-to-region beg end) + ad-do-it + (widen)))))) + ad-do-it)) + + (ad-activate 'font-lock-unfontify-region) + ;; (ad-deactivate 'font-lock-unfontify-region) + + (defadvice font-lock-fontify-syntactically-region + (around asy-font-lock-fontify-syntactically-region + (start end &optional loudly)) + (if (and two-mode-bool (eq major-mode 'asy-mode)) + (let*((reg (lasy-restrict-region start end))) + (save-restriction + (setq start (nth 0 reg) end (nth 1 reg)) + (narrow-to-region start end) + (condition-case nil + ad-do-it + (error nil)) + (widen) + )) + ad-do-it)) + + (ad-activate 'font-lock-fontify-syntactically-region) + ;; (ad-deactivate 'font-lock-fontify-syntactically-region) + + (defadvice font-lock-default-fontify-region + (around asy-font-lock-default-fontify-region + (beg end loudly)) + (if two-mode-bool + (let ((rstate (lasy-parse-region beg end)) + asy-fontify latex-fontify curr reg) + (while (setq curr (pop rstate)) + (setq reg (nth 1 curr)) + (setq asy-fontify (and (nth 0 curr) lasy-fontify-asy-p) + latex-fontify (and (not (nth 0 curr)) + (not lasy-fontify-asy-p))) + (when (or asy-fontify latex-fontify) + (setq beg (nth 0 reg) + end (nth 1 reg)) + (save-excursion + (save-restriction + (narrow-to-region beg end) + (condition-case nil + ad-do-it + (error nil)) + (widen) + ))))) + ad-do-it)) + + (ad-activate 'font-lock-default-fontify-region) + ;; (ad-deactivate 'font-lock-default-fontify-region) + + )) + (progn + (defvar two-mode-bool nil) + (defun lasy-mode () + (message "You must install the package two-mode-mode.el.")))) + +(setq asy-latex-menu-item + '(["Toggle lasy-mode" lasy-mode :active (featurep 'two-mode-mode)] + ["View asy picture near cursor" lasy-compile :active t]"--" + ("Compile OR View" + ["PS" lasy-view-ps :active t] + ["PDF (pdflatex)" lasy-view-pdf-via-pdflatex :active t] + ["PDF (ps2pdf)" lasy-view-pdf-via-ps2pdf :active t]) + ("Compile AND View" + ["PS" asy-master-tex-view-ps-f :active t] + ["PDF (pdflatex)" asy-master-tex-view-pdflatex-f :active t] + ["PDF (ps2pdf)" asy-master-tex-view-ps2pdf-f :active t])"--" + ["Asymptote insinuates globally LaTeX" asy-insinuate-latex-globally :active (not asy-insinuate-latex-globally-p)] + ("Disable Asymptote insinuate Latex" + ["locally" asy-no-insinuate-locally :active t] + ["globally" asy-no-insinuate-globally :active t]) + ("Debugger Buffer" + ["Visible" (setq asy-compilation-buffer 'visible) :style radio :selected (eq asy-compilation-buffer 'visible) :active t] + ["Available" (setq asy-compilation-buffer 'available) :style radio :selected (eq asy-compilation-buffer 'available) :active t] + ["None" (setq asy-compilation-buffer 'none) :style radio :selected (eq asy-compilation-buffer 'none) :active t] + ["Never" (setq asy-compilation-buffer 'never) :style radio :selected (eq asy-compilation-buffer 'never) :active t]) + )) +(if running-xemacs-p + (setq asy-latex-menu-item (nconc '("Asymptote") asy-latex-menu-item)) + (setq asy-latex-menu-item (nconc '("Asymptote" :visible asy-insinuate-latex-p) asy-latex-menu-item))) + +(defun asy-insinuate-latex-maybe () + "This function is added to `LaTeX-mode-hook' to define the environment 'asy' +and, eventually, set its indentation. +For internal use only." + (when (or asy-insinuate-latex-globally-p + (save-excursion + (beginning-of-buffer) + (save-match-data + (search-forward "\\begin{asy}" nil t)))) + (asy-insinuate-latex)) + (LaTeX-add-environments + '("asy" (lambda (env &rest ignore) + (unless asy-insinuate-latex-p (asy-insinuate-latex)) + (LaTeX-insert-environment env))))) + +;; (add-hook 'after-init-hook +;; (lambda () +(eval-after-load "latex" + '(progn + (add-hook 'LaTeX-mode-hook 'asy-insinuate-latex-maybe) + (setq lasy-mode-map (copy-keymap LaTeX-mode-map)) + (setq LaTeX-mode-map-backup (copy-keymap LaTeX-mode-map)) + + (defadvice TeX-add-local-master (after asy-adjust-local-variable ()) + "Delete the line that defines the mode in a file .tex because two-mode-mode reread +the local variables after switching mode." + (when (string= (file-name-extension buffer-file-name) "tex") + (save-excursion + (goto-char (point-max)) + (delete-matching-lines + "mode: latex" + (re-search-backward "^\\([^\n]+\\)Local Variables:" + (- (point-max) 3000) t) + (re-search-forward (regexp-quote + (concat (match-string 1) + "End:"))) nil)))) + (ad-activate 'TeX-add-local-master) + ;; (ad-deactivate 'TeX-add-local-master) + + (when lasy-extra-key + (define-key lasy-mode-map (kbd "<C-return>") + (lambda () + (interactive) + (lasy-view-ps nil nil t))) + (define-key lasy-mode-map (kbd "<C-S-return>") + (lambda () + (interactive) + (lasy-view-ps t nil t))) + (define-key lasy-mode-map (kbd "<M-return>") + (lambda () + (interactive) + (lasy-view-pdf-via-pdflatex nil nil t))) + (define-key lasy-mode-map (kbd "<M-S-return>") + (lambda () + (interactive) + (lasy-view-pdf-via-pdflatex t nil t))) + (define-key lasy-mode-map (kbd "<C-M-return>") + (lambda () + (interactive) + (lasy-view-pdf-via-ps2pdf nil nil t))) + (define-key lasy-mode-map (kbd "<C-M-S-return>") + (lambda () + (interactive) + (lasy-view-pdf-via-ps2pdf t nil t))) + (define-key lasy-mode-map (kbd "<f4>") 'asy-goto-error)) + + (easy-menu-define asy-latex-mode-menu lasy-mode-map "Asymptote insinuates LaTeX" asy-latex-menu-item) + )) +;; )) + +(defvar asy-insinuate-latex-p nil + "Not nil when current buffer is insinuated by Asymptote. +May be a local variable. +For internal use.") + +(defvar asy-insinuate-latex-globally-p nil + "Not nil when all latex-mode buffers is insinuated by Asymptote. +For internal use.") + +(defun asy-set-latex-asy-indentation () + "Set the indentation of environnment 'asy' like the environnment 'verbatim' is." + ;; Regexp matching environments with indentation at col 0 for begin/end. + (set (make-local-variable 'LaTeX-verbatim-regexp) + (concat (default-value 'LaTeX-verbatim-regexp) "\\|asy")) + ;; Alist of environments with special indentation. + (make-local-variable 'LaTeX-indent-environment-list) + (add-to-list 'LaTeX-indent-environment-list + '("asy" current-indentation))) + +(defun asy-unset-latex-asy-indentation () + "Unset the indentation of environnment 'asy' like the environnment 'verbatim' is." + (set (make-local-variable 'LaTeX-verbatim-regexp) + (default-value 'LaTeX-verbatim-regexp)) + (set (make-local-variable 'LaTeX-indent-environment-list) + (default-value 'LaTeX-indent-environment-list))) + +(defun asy-no-insinuate-locally () + (interactive) + (set (make-local-variable 'asy-insinuate-latex-p) nil) + (setq asy-insinuate-latex-globally-p nil) + (asy-unset-latex-asy-indentation) + (if running-xemacs-p + (easy-menu-remove-item nil nil "Asymptote") + (menu-bar-update-buffers)) + (if (and (boundp 'two-mode-bool) two-mode-bool) + (lasy-mode)) + (use-local-map LaTeX-mode-map-backup)) + + +(defun asy-no-insinuate-globally () + (interactive) + (if running-xemacs-p + (easy-menu-remove-item nil nil "Asymptote") + (easy-menu-remove-item LaTeX-mode-map nil "Asymptote")) + (kill-local-variable asy-insinuate-latex-p) + (setq-default asy-insinuate-latex-p nil) + (setq asy-insinuate-latex-globally-p nil) + (if (not running-xemacs-p) + (menu-bar-update-buffers)) + (setq LaTeX-mode-map (copy-keymap LaTeX-mode-map-backup)) + ;;Disable lasy-mode in all latex-mode buffers. + (when (featurep 'two-mode-mode) + (mapc (lambda (buffer) + (with-current-buffer buffer + (when (and (buffer-file-name) (string= (file-name-extension (buffer-file-name)) "tex")) + (asy-unset-latex-asy-indentation) + (latex-mode) + (setq asy-insinuate-latex-p nil)))) + (buffer-list)))) + +;;;###autoload +(defun asy-insinuate-latex (&optional global) + "Add a menu bar in current 'latex-mode' buffer and activate asy keys bindings. +If the optional parameter (only for internal use) 'global' is 't' then all the FUTURE 'latex-mode' buffers are insinuated. +To insinuate all (current and future) 'latex-mode' buffers, use 'asy-insinuate-latex-globally' instead. +You can automate this feature for all the 'latex-mode' buffers by inserting the five following lines in your .emacs initialization file: + (eval-after-load \"latex\" + '(progn + ;; Add here your personal features for 'latex-mode': + (asy-insinuate-latex t) ;; Asymptote insinuates globally Latex. + ))" + (interactive) + (if (and (not asy-insinuate-latex-globally-p) (or global (string= major-mode "latex-mode"))) + (progn + (asy-set-latex-asy-indentation) + (if global + (progn + (setq asy-insinuate-latex-p t) + (setq asy-insinuate-latex-globally-p t) + (setq LaTeX-mode-map (copy-keymap lasy-mode-map)) + (if running-xemacs-p + (add-hook 'LaTeX-mode-hook + (lambda () + (if asy-insinuate-latex-globally-p + (easy-menu-add asy-latex-mode-menu lasy-mode-map)))))) + (progn + (use-local-map lasy-mode-map) + (easy-menu-add asy-latex-mode-menu lasy-mode-map) + (set (make-local-variable 'asy-insinuate-latex-p) t))) + ))) + +(defun asy-insinuate-latex-globally () + "Insinuates all (current and future) 'latex-mode' buffers. +See `asy-insinuate-latex'." + (interactive) + (asy-insinuate-latex t) + (if running-xemacs-p + (add-hook 'LaTeX-mode-hook + (lambda () + (if asy-insinuate-latex-globally-p + (easy-menu-add asy-latex-mode-menu lasy-mode-map))))) + (mapc (lambda (buffer) + (with-current-buffer buffer + (when (and + (buffer-file-name) + (string= (file-name-extension (buffer-file-name)) "tex")) + (setq asy-insinuate-latex-p t) + (use-local-map LaTeX-mode-map) + (use-local-map lasy-mode-map) + (asy-set-latex-asy-indentation) + (easy-menu-add asy-latex-mode-menu lasy-mode-map)))) + (buffer-list))) + +(defun lasy-inline-p() + "Return nil if the option 'inline' is not used or if `lasy-compilation-inline-auto-detection' value is nil." + (if lasy-compilation-inline-auto-detection + (save-excursion + (re-search-backward "^[^%]* *\\\\usepackage\\[ *inline *\\]{ *asymptote *}" 0 t)) + nil)) + +(defvar lasy-run-tex nil) +(defun lasy-asydef() + "Return the content between the tags \\begin{asydef} and \\end{asydef}." + (save-excursion + (if (re-search-backward "\\\\begin{asydef}" 0 t) + (buffer-substring + (progn (next-line)(beginning-of-line)(point)) + (progn (re-search-forward "\\\\end{asydef}") + (previous-line)(end-of-line) + (point))) + ""))) + +(defun lasy-compile-tex() + "Compile region between \\begin{asy}[text with backslash] and \\end{asy} through a reconstructed file .tex." + (interactive) + (setq lasy-run-tex t) + (save-excursion + (let* ((Filename (asy-get-temp-file-name t)) + (FilenameTex (concat Filename ".tex")) + (asydef (lasy-asydef))) + (save-excursion + (beginning-of-buffer) + (write-region (point) + (progn + (re-search-forward "\\\\begin{document}.*\n") + (point)) FilenameTex) + (write-region (concat "\\begin{asydef}\n" asydef "\n\\end{asydef}\n") 0 FilenameTex t)) + (re-search-backward "\\\\begin{asy}") + (write-region (point) (progn + (re-search-forward "\\\\end{asy}") + (point)) FilenameTex t) + (with-temp-file FilenameTex + (insert-file FilenameTex) + (end-of-buffer) + (insert "\n\\end{document}")) + (let ((default-directory asy-temp-dir)) + (lasy-view-ps t Filename))))) + +(defun lasy-compile() + "Compile region between \\begin{asy} and \\end{asy}." + (interactive) + (if (or (lasy-inline-p) (progn ;; find \begin{asy}[any backslash] + (save-excursion + (re-search-forward "\\\\end{asy}" (point-max) t) + (re-search-backward "\\\\begin{asy}.*\\(\\[.*\\\\.*\\]\\)" 0 t)) + (match-string 1))) + (progn + (lasy-compile-tex)) ;; a temporary TeX file must be reconstructed. + (progn + (setq lasy-run-tex nil) + (save-excursion + (let ((Filename (asy-get-temp-file-name)) + (asydef (lasy-asydef))) + (write-region (match-string 0) 0 Filename) + (re-search-backward "\\\\begin{asy}") + (write-region (point) (progn + (re-search-forward "\\\\end{asy}") + (point)) Filename) + (with-temp-file Filename + (insert-file-contents Filename) + (beginning-of-buffer) + (if (re-search-forward "\\\\begin{asy}\\[\\(.*\\)\\]" (point-max) t) + (let ((sz (match-string 1))) + (replace-match "") + (insert (concat asydef "\nsize(" sz ");"))) + (when (re-search-forward "\\\\begin{asy}" (point-max) t) + (replace-match "") + (insert asydef))) + (while (re-search-forward "\\\\end{asy}" (point-max) t) + (replace-match ""))) + (let* ((asy-compile-command + (concat asy-command-location + asy-command + (if (eq asy-compilation-buffer 'never) + " " " -wait ") + (asy-protect-file-name Filename)))) + (asy-internal-compile + asy-compile-command t + (not (eq asy-compilation-buffer 'never))))))))) + +(defun asy-set-master-tex () + "Set the local variable 'asy-TeX-master-file. +This variable is used by 'asy-master-tex-view-ps" + (interactive) + (set (make-local-variable 'asy-TeX-master-file) + (file-name-sans-extension + (file-relative-name + (expand-file-name + (read-file-name "TeX document: "))))) + (if (string= (concat default-directory asy-TeX-master-file) + (file-name-sans-extension buffer-file-name)) + (prog1 + (set (make-local-variable 'asy-TeX-master-file) nil) + (error "You should never give the same name to the TeX file and the Asymptote file")) + (save-excursion + (end-of-buffer) + (if (re-search-backward "asy-TeX-master-file\\(.\\)*$" 0 t) + (replace-match (concat "asy-TeX-master-file: \"" asy-TeX-master-file "\"")) + (insert (concat " +/// Local Variables: +/// asy-TeX-master-file: \"" asy-TeX-master-file "\" +/// End:")) t)))) + +(defun asy-unset-master-tex () + "Set the local variable 'asy-TeX-master-file to 'nil. +This variable is used by 'asy-master-tex-view-ps" + (interactive) + (set (make-local-variable 'asy-TeX-master-file) nil) + (save-excursion + (end-of-buffer) + (if (re-search-backward "^.*asy-TeX-master-file:.*\n" 0 t) + (replace-match "")))) + +(defun asy-master-tex-error () + "Asy-mode internal use..." + (if (y-or-n-p "You try to compile the TeX document that contains this picture. +You must set the local variable asy-TeX-master-file. +Do you want set this variable now ?") + (asy-set-master-tex) nil)) + +(defun asy-master-tex-view (Func-view &optional Force fromtex) + "Compile the LaTeX document that contains the picture of the current Asymptote code with the function Func-view. +Func-view can be one of 'lasy-view-ps, 'lasy-view-pdf-via-pdflatex, 'lasy-view-pdf-via-ps2pdf." + (interactive) + (if (or + (and (boundp two-mode-bool) two-mode-bool) + (string-match "latex" (downcase mode-name))) + (progn ;; Current mode is lasy-mode or latex-mode not asy-mode + (funcall Func-view Force nil fromtex)) + (if asy-TeX-master-file + (if (string= asy-TeX-master-file + (file-name-sans-extension buffer-file-name)) + (error "You should never give the same name to the TeX file and the Asymptote file") + (funcall Func-view Force asy-TeX-master-file fromtex)) + (if (asy-master-tex-error) + (funcall Func-view Force asy-TeX-master-file fromtex))))) + +(defvar asy-last-compilation-code nil + "Code returned by the last compilation with `compile'.") + +(defvar asy-compilation-auto-close nil + "Variable to communicate with `asy-compilation-finish-function'. +Do not set this variable in any fashion.") + +(defun asy-compilation-finish-function (buf msg) + "Function to automatically close the compilation buffer '*asy-compilation*' +when no error or warning occurs." + (when (string-match "*asy-compilation*" (buffer-name buf)) + (when (and asy-compilation-auto-close + (eq asy-compilation-buffer 'none)) + (setq asy-compilation-auto-close nil) + (if (not (string-match "exited abnormally" msg)) + (progn + (save-excursion + (set-buffer buf) + (beginning-of-buffer) + (if (not (search-forward-regexp "[wW]arning" nil t)) + (when (not (eq asy-compilation-buffer 'visible)) + ;;no errors/Warning, make the compilation window go away + (run-at-time 0.5 nil (lambda (buf_) + (delete-windows-on buf_) + (kill-buffer buf_)) buf) + (message (replace-regexp-in-string "\n" "" msg))) + (message "Compilation warnings...")))))))) + +(if running-xemacs-p + (setq compilation-finish-function 'asy-compilation-finish-function) + (add-to-list 'compilation-finish-functions + 'asy-compilation-finish-function)) + +(defun asy-compilation-wait(&optional pass auto-close) + "Wait for process in *asy-compilation* exits. +If pass is 't' don't wait. +If auto-close is 't' close the window if the process exit with success." + (setq asy-compilation-auto-close auto-close) + (let* ((buff (get-buffer "*asy-compilation*")) + (comp-proc (get-buffer-process buff))) + (while (and comp-proc + (not (eq (process-status comp-proc) 'exit)) + (not pass)) + (setq comp-proc (get-buffer-process buff)) + (sit-for 1) + (message "Waiting process...") ;; need message in Windows system + ) + (message "") ;; Erase previous message. + (if (and (not pass) comp-proc) + (setq asy-last-compilation-code (process-exit-status comp-proc)) + (setq asy-last-compilation-code 0)) + (when (and (eq asy-compilation-buffer 'available) + (zerop asy-last-compilation-code)) + (delete-windows-on buff)))) + + +(defun asy-internal-shell (command &optional pass) + "Execute 'command' in a inferior shell discarding output and +redirecting stderr in the file given by the command `asy-log-filename'. +`asy-internal-shell' waits for PROGRAM to terminate and returns a numeric exit status. +The variable `asy-last-compilation-code' is always set to the exit status. +The optional argument pass, for compatibility, is not used." + (let* ((log-file (asy-log-filename)) + (discard (if pass 0 nil)) + (status + (progn + (let ((view-inhibit-help-message t))(write-region "" 0 log-file nil)) + (message "%s" command) + (call-process shell-file-name nil (list nil log-file) nil shell-command-switch command)))) + (setq asy-last-compilation-code (if status status 0)) + (if status status nil))) + +;; (defun asy-internal-shell (command &optional pass) +;; "Execute 'command' in a inferior shell discarding output and +;; redirecting stderr in the file given by the command `asy-log-filename'. +;; pass non-nil means `asy-internal-shell' returns immediately with nil value. +;; Otherwise it waits for PROGRAM to terminate and returns a numeric exit status. +;; The variable `asy-last-compilation-code' is always set to the exit status or 0 if the +;; process returns immediately." +;; (let* ((log-file (asy-log-filename)) +;; (discard (if pass 0 nil)) +;; (status +;; (progn +;; (let ((inhibit-redisplay t))(write-region "" 0 log-file nil)) +;; (message "%s" command) +;; (call-process shell-file-name nil (list discard log-file) nil shell-command-switch command)))) +;; (setq asy-last-compilation-code (if status status 0)) +;; (when pass (sit-for 1)) +;; (if status status nil))) + +(defun asy-internal-compile (command &optional pass auto-close stderr) + "Execute command. +pass non-nil means don't wait the end of the process. +auto-close non-nil means automatically close the compilation buffer. +stderr non-nil means redirect the standard output error to the file +returned by `asy-log-filename'. +In this case command is running in an inferior shell without any output and +the parameter auto-close is not used (see `asy-internal-shell')." + (setq asy-last-compilation-code -1) + (let* ((compilation-buffer-name "*asy-compilation*") + (compilation-buffer-name-function (lambda (mj) compilation-buffer-name))) + (if (or stderr (eq asy-compilation-buffer 'never)) + (progn + (asy-internal-shell command pass) + (asy-error-message t)) + (progn + (let ((comp-proc (get-buffer-process compilation-buffer-name))) + (if comp-proc + (condition-case () + (progn + (interrupt-process comp-proc) + (sit-for 1) + (delete-process comp-proc) + (when (and asy-compilation-auto-close + (eq asy-compilation-buffer 'none) + (not (eq asy-compilation-buffer 'visible))) + (sit-for 0.6))) + (error "")) + )) + (let ((view-inhibit-help-message t)) + (write-region "" 0 (asy-log-filename) nil)) + (compile command)) + (asy-compilation-wait pass auto-close)))) + +(defun asy-open-file(Filename) + "Open the ps or pdf file Filename. +In unix-like system the variables `ps-view-command' and `pdf-view-command' are used. +In Windows the associated system file type is used instead." + (let ((command + (if running-unix-p + (let ((ext (file-name-extension Filename))) + (cond + ((string= ext "ps") ps-view-command) + ((string= ext "pdf") pdf-view-command) + (t (error "Extension Not Supported.")))) + (asy-protect-file-name (file-name-nondirectory Filename)))) + ) + (if running-unix-p + (start-process "" nil command Filename) + (call-process-shell-command command nil 0)))) + +(defun lasy-TeX-master-file () + "Return the file name of the master file for the current document. +The returned string contain the directory but does not contain the extension of the file." + (expand-file-name + (concat (TeX-master-directory) (TeX-master-file nil t)))) + +(defun lasy-must-compile-p (TeX-Master-File out-file &optional Force) + "" + (or Force + (file-newer-than-file-p + (concat TeX-Master-File ".tex") out-file) + (and (stringp (TeX-master-file)) ;; current buffer is not a mater tex file + (file-newer-than-file-p buffer-file-name out-file)))) + +(defun lasy-view-ps (&optional Force Filename fromtex) + "Compile a LaTeX document embedding Asymptote code with latex->asy->latex->dvips and/or view the PostScript output. +If optional argument Force is t then force compilation." + (interactive) + (setq lasy-run-tex t) + (setq lasy-compile-tex fromtex) + (if (buffer-modified-p) (save-buffer)) + (when (eq asy-compilation-buffer 'never) (write-region "" 0 (asy-log-filename) nil)) + (let* + ((b-b-n (if Filename Filename (lasy-TeX-master-file))) + (b-b-n-tex (asy-protect-file-name (concat b-b-n ".tex"))) + (b-b-n-ps (asy-protect-file-name (concat b-b-n ".ps"))) + (b-b-n-dvi (asy-protect-file-name (concat b-b-n ".dvi"))) + (b-b-n-asy (asy-protect-file-name (concat b-b-n ".asy"))) + (stderr (eq asy-compilation-buffer 'never))) + (if (lasy-must-compile-p b-b-n (concat b-b-n ".ps") Force) + (progn + (let ((default-directory (file-name-directory b-b-n))) + (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex)) + (when (and (zerop asy-last-compilation-code) (file-readable-p (concat b-b-n ".asy"))) + (asy-internal-compile (concat asy-command-location lasy-command " " b-b-n-asy) nil nil stderr) + (when (zerop asy-last-compilation-code) + (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex)))) + (when (zerop asy-last-compilation-code) + (asy-internal-compile (concat lasy-dvips-command " " b-b-n-dvi " -o " b-b-n-ps) nil t) + (when (zerop asy-last-compilation-code) + (asy-open-file (concat b-b-n ".ps")))))) + (asy-open-file (concat b-b-n ".ps"))))) + +(defun lasy-view-pdf-via-pdflatex (&optional Force Filename fromtex) + "Compile a LaTeX document embedding Asymptote code with pdflatex->asy->pdflatex and/or view the PDF output. +If optional argument Force is t then force compilation." + (interactive) + (setq lasy-run-tex t) + (setq lasy-compile-tex fromtex) + (if (buffer-modified-p) (save-buffer)) + (when (eq asy-compilation-buffer 'never) (write-region "" 0 (asy-log-filename) nil)) + (let* + ((b-b-n (if Filename Filename (lasy-TeX-master-file))) + (b-b-n-tex (asy-protect-file-name (concat b-b-n ".tex"))) + (b-b-n-pdf (asy-protect-file-name (concat b-b-n ".pdf"))) + (b-b-n-asy (asy-protect-file-name (concat b-b-n ".asy"))) + ;; (stderr (or (eq asy-compilation-buffer 'never) lasy-compile-tex))) + (stderr (eq asy-compilation-buffer 'never))) + (if (lasy-must-compile-p b-b-n (concat b-b-n ".pdf") Force) + (progn + (let ((default-directory (file-name-directory b-b-n))) + (asy-internal-compile (concat lasy-pdflatex-command " " b-b-n-tex)) + (when (and (zerop asy-last-compilation-code) (file-readable-p (concat b-b-n ".asy"))) + (asy-internal-compile (concat asy-command-location lasy-command " " b-b-n-asy) nil nil stderr) + (when (zerop asy-last-compilation-code) + (asy-internal-compile (concat lasy-pdflatex-command " " b-b-n-tex) t))) + (when (zerop asy-last-compilation-code) + (asy-open-file (concat b-b-n ".pdf"))))) + (asy-open-file (concat b-b-n ".pdf"))))) + +(defun lasy-view-pdf-via-ps2pdf (&optional Force Filename fromtex) + "Compile a LaTeX document embedding Asymptote code with latex->asy->latex->dvips->ps2pdf14 and/or view the PDF output. +If optional argument Force is t then force compilation." + (interactive) + (setq lasy-run-tex t) + (setq lasy-compile-tex fromtex) + (if (buffer-modified-p) (save-buffer)) + (when (eq asy-compilation-buffer 'never) (write-region "" 0 (asy-log-filename) nil)) + (let* + ((b-b-n (if Filename Filename (lasy-TeX-master-file))) + (b-b-n-tex (asy-protect-file-name (concat b-b-n ".tex"))) + (b-b-n-ps (asy-protect-file-name (concat b-b-n ".ps"))) + (b-b-n-dvi (asy-protect-file-name (concat b-b-n ".dvi"))) + (b-b-n-pdf (asy-protect-file-name (concat b-b-n ".pdf"))) + (b-b-n-asy (asy-protect-file-name (concat b-b-n ".asy"))) + ;; (stderr (or (eq asy-compilation-buffer 'never) lasy-compile-tex))) + (stderr (eq asy-compilation-buffer 'never))) + (if (lasy-must-compile-p b-b-n (concat b-b-n ".pdf") Force) + (progn + (let ((default-directory (file-name-directory b-b-n))) + (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex)) + (when (and (zerop asy-last-compilation-code) (file-readable-p (concat b-b-n ".asy"))) + (asy-internal-compile (concat asy-command-location lasy-command " " b-b-n-asy) nil nil stderr) + (when (zerop asy-last-compilation-code) + (asy-internal-compile (concat lasy-latex-command " " b-b-n-tex)))) + (when (zerop asy-last-compilation-code) + (asy-internal-compile (concat lasy-dvips-pre-pdf-command " " b-b-n-dvi " -o " b-b-n-ps)) + (when (zerop asy-last-compilation-code) + (asy-internal-compile (concat lasy-ps2pdf-command " " b-b-n-ps " " b-b-n-pdf) t) + (when (zerop asy-last-compilation-code) + (asy-open-file (concat b-b-n ".pdf"))))))) + (asy-open-file (concat b-b-n ".pdf"))))) + +;; Goto error of last compilation +(define-key asy-mode-map (kbd "<f4>") 'asy-goto-error) + +;; Save and compile the file with option -V +(define-key asy-mode-map (kbd "C-c C-c") 'asy-compile) + +;; Show the definitions of command at point +(define-key asy-mode-map (kbd "C-c ?") 'asy-show-function-at-point) + +;; new line and indent +(define-key asy-mode-map (kbd "RET") 'newline-and-indent) + +(defun asy-master-tex-view-ps () + "Look at `asy-master-tex-view'" + (interactive) + (asy-master-tex-view 'lasy-view-ps nil t)) +(define-key asy-mode-map (kbd "<C-return>") 'asy-master-tex-view-ps) + +(defun asy-master-tex-view-ps-f () + "Look at `asy-master-tex-view'" + (interactive) + (asy-master-tex-view 'lasy-view-ps t t)) +(define-key asy-mode-map (kbd "<C-S-return>") 'asy-master-tex-view-ps-f) + +(defun asy-master-tex-view-pdflatex () + "Look at `asy-master-tex-view'" + (interactive) + (asy-master-tex-view 'lasy-view-pdf-via-pdflatex nil t)) +(define-key asy-mode-map (kbd "<M-return>") 'asy-master-tex-view-pdflatex) + +(defun asy-master-tex-view-pdflatex-f () + "Look at `asy-master-tex-view'" + (interactive) + (asy-master-tex-view 'lasy-view-pdf-via-pdflatex t t)) +(define-key asy-mode-map (kbd "<M-S-return>") 'asy-master-tex-view-pdflatex-f) + +(defun asy-master-tex-view-ps2pdf () + "Look at `asy-master-tex-view'" + (interactive) + (asy-master-tex-view 'lasy-view-pdf-via-ps2pdf nil t)) +(define-key asy-mode-map (kbd "<C-M-return>") 'asy-master-tex-view-ps2pdf) + +(defun asy-master-tex-view-ps2pdf-f () + "Look at `asy-master-tex-view'" + (interactive) + (asy-master-tex-view 'lasy-view-pdf-via-ps2pdf t t)) +(define-key asy-mode-map (kbd "<C-M-S-return>") 'asy-master-tex-view-ps2pdf-f) + +(provide `asy-mode) +;;; asy-mode.el ends here diff --git a/Build/source/utils/asymptote/base/asy.vim b/Build/source/utils/asymptote/base/asy.vim new file mode 100644 index 00000000000..4cb897f1316 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/asy_filetype.vim b/Build/source/utils/asymptote/base/asy_filetype.vim new file mode 100644 index 00000000000..3b614edf12b --- /dev/null +++ b/Build/source/utils/asymptote/base/asy_filetype.vim @@ -0,0 +1,3 @@ +" Vim filetype detection file +" Language: Asymptote +au BufNewFile,BufRead *.asy setfiletype asy diff --git a/Build/source/utils/asymptote/base/asymptote.py b/Build/source/utils/asymptote/base/asymptote.py new file mode 100755 index 00000000000..1a7aebd0fd2 --- /dev/null +++ b/Build/source/utils/asymptote/base/asymptote.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +# Python module to feed Asymptote with commands +# (modified from gnuplot.py) +from subprocess import * +class asy: + def __init__(self): + self.session = Popen(['asy','-quiet','-inpipe=0','-outpipe=2'],stdin=PIPE) + self.help() + def send(self, cmd): + self.session.stdin.write(bytes(cmd+'\n','utf-8')) + 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') + input('press ENTER to continue') + g.erase() + del g diff --git a/Build/source/utils/asymptote/base/babel.asy b/Build/source/utils/asymptote/base/babel.asy new file mode 100644 index 00000000000..ada173c8a96 --- /dev/null +++ b/Build/source/utils/asymptote/base/babel.asy @@ -0,0 +1,4 @@ +void babel(string s) +{ + usepackage("babel",s); +} diff --git a/Build/source/utils/asymptote/base/bezulate.asy b/Build/source/utils/asymptote/base/bezulate.asy new file mode 100644 index 00000000000..e09b86ed25b --- /dev/null +++ b/Build/source/utils/asymptote/base/bezulate.asy @@ -0,0 +1,310 @@ +// Bezier triangulation routines written by Orest Shardt, 2008. + +private real fuzz=1e-6; +real duplicateFuzz=1e-3; // Work around font errors. +real maxrefinements=10; + +private real[][] intersections(pair a, pair b, path p) +{ + pair delta=fuzz*unit(b-a); + return intersections(a-delta--b+delta,p,fuzz); +} + +int countIntersections(path[] p, pair start, pair end) +{ + int intersects=0; + for(path q : p) + intersects += intersections(start,end,q).length; + return intersects; +} + +path[][] containmentTree(path[] paths) +{ + path[][] result; + for(path g : paths) { + // check if current curve contains or is contained in a group of curves + int j; + for(j=0; j < result.length; ++j) { + path[] resultj=result[j]; + int test=inside(g,resultj[0],zerowinding); + if(test == 1) { + // current curve contains group's toplevel curve; + // replace toplevel curve with current curve + resultj.insert(0,g); + // check to see if any other groups are contained within this curve + for(int k=j+1; k < result.length;) { + if(inside(g,result[k][0],zerowinding) == 1) { + resultj.append(result[k]); + result.delete(k); + } else ++k; + } + break; + } else if(test == -1) { + // current curve contained within group's toplevel curve + resultj.push(g); + break; + } + } + // create a new group if this curve does not belong to another group + if(j == result.length) + result.push(new path[] {g}); + } + return result; +} + +bool isDuplicate(pair a, pair b, real relSize) +{ + return abs(a-b) <= duplicateFuzz*relSize; +} + +path removeDuplicates(path p) +{ + real relSize = abs(max(p)-min(p)); + bool cyclic=cyclic(p); + for(int i=0; i < length(p); ++i) { + if(isDuplicate(point(p,i),point(p,i+1),relSize)) { + p=subpath(p,0,i)&subpath(p,i+1,length(p)); + --i; + } + } + return cyclic ? p&cycle : p; +} + +path section(path p, real t1, real t2, bool loop=false) +{ + if(t2 < t1 || loop && t1 == t2) + t2 += length(p); + return subpath(p,t1,t2); +} + +path uncycle(path p, real t) +{ + return subpath(p,t,t+length(p)); +} + +// returns outer paths +void connect(path[] paths, path[] result, path[] patch) +{ + path[][] tree=containmentTree(paths); + for(path[] group : tree) { + path outer = group[0]; + group.delete(0); + path[][] innerTree = containmentTree(group); + path[] remainingCurves; + path[] inners; + for(path[] innerGroup:innerTree) + { + inners.push(innerGroup[0]); + if(innerGroup.length>1) + remainingCurves.append(innerGroup[1:]); + } + connect(remainingCurves,result,patch); + real d=2*abs(max(outer)-min(outer)); + while(inners.length > 0) { + int curveIndex = 0; + //pair direction=I*dir(inners[curveIndex],0,1); // Use outgoing direction + //if(direction == 0) // Try a random direction + // direction=expi(2pi*unitrand()); + //pair start=point(inners[curveIndex],0); + + // find shortest distance between a node on the inner curve and a node + // on the outer curve + + real mindist = d; + int inner_i = 0; + int outer_i = 0; + for(int ni = 0; ni < length(inners[curveIndex]); ++ni) + { + for(int no = 0; no < length(outer); ++no) + { + real dist = abs(point(inners[curveIndex],ni)-point(outer,no)); + if(dist < mindist) + { + inner_i = ni; + outer_i = no; + mindist = dist; + } + } + } + pair start=point(inners[curveIndex],inner_i); + pair end = point(outer,outer_i); + + // find first intersection of line segment with outer curve + //real[][] ints=intersections(start,start+d*direction,outer); + real[][] ints=intersections(start,end,outer); + assert(ints.length != 0); + real endtime=ints[0][1]; // endtime is time on outer + end = point(outer,endtime); + // find first intersection of end--start with any inner curve + real starttime=inner_i; // starttime is time on inners[curveIndex] + real earliestTime=1; + for(int j=0; j < inners.length; ++j) { + real[][] ints=intersections(end,start,inners[j]); + + if(ints.length > 0 && ints[0][0] < earliestTime) { + earliestTime=ints[0][0]; // time on end--start + starttime=ints[0][1]; // time on inner curve + curveIndex=j; + } + } + start=point(inners[curveIndex],starttime); + + + bool found_forward = false; + real timeoffset_forward = 2; + path portion_forward; + path[] allCurves = {outer}; + allCurves.append(inners); + + while(!found_forward && timeoffset_forward > fuzz) { + timeoffset_forward /= 2; + if(countIntersections(allCurves,start, + point(outer,endtime+timeoffset_forward)) == 2) + { + portion_forward = subpath(outer,endtime,endtime+timeoffset_forward)--start--cycle; + + found_forward=true; + // check if an inner curve is inside the portion + for(int k = 0; found_forward && k < inners.length; ++k) + { + if(k!=curveIndex && + inside(portion_forward,point(inners[k],0),zerowinding)) + found_forward = false; + } + } + } + + bool found_backward = false; + real timeoffset_backward = -2; + path portion_backward; + while(!found_backward && timeoffset_backward < -fuzz) { + timeoffset_backward /= 2; + if(countIntersections(allCurves,start, + point(outer,endtime+timeoffset_backward))==2) + { + portion_backward = subpath(outer,endtime+timeoffset_backward,endtime)--start--cycle; + found_backward = true; + // check if an inner curve is inside the portion + for(int k = 0; found_backward && k < inners.length; ++k) + { + if(k!=curveIndex && + inside(portion_backward,point(inners[k],0),zerowinding)) + found_backward = false; + } + } + } + assert(found_forward || found_backward); + real timeoffset; + path portion; + if(found_forward && !found_backward) + { + timeoffset = timeoffset_forward; + portion = portion_forward; + } + else if(found_backward && !found_forward) + { + timeoffset = timeoffset_backward; + portion = portion_backward; + } + else // assert handles case of neither found + { + if(timeoffset_forward > -timeoffset_backward) + { + timeoffset = timeoffset_forward; + portion = portion_forward; + } + else + { + timeoffset = timeoffset_backward; + portion = portion_backward; + } + } + + endtime=min(endtime,endtime+timeoffset); + // or go from timeoffset+timeoffset_backward to timeoffset+timeoffset_forward? + timeoffset=abs(timeoffset); + + // depends on the curves having opposite orientations + path remainder=section(outer,endtime+timeoffset,endtime) + --uncycle(inners[curveIndex], + starttime)--cycle; + inners.delete(curveIndex); + outer = remainder; + patch.append(portion); + } + result.append(outer); + } +} + +bool checkSegment(path g, pair p, pair q) +{ + pair mid=0.5*(p+q); + return intersections(p,q,g).length == 2 && + inside(g,mid,zerowinding) && intersections(g,mid).length == 0; +} + +path subdivide(path p) +{ + path q; + int l=length(p); + for(int i=0; i < l; ++i) + q=q&(straight(p,i) ? subpath(p,i,i+1) : + 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 > maxrefinements) { + warning("subdivisions","too many subdivisions",position=true); + } else { + p=subdivide(p); + i=-1; + } + } + } + } + } + if(length(p) <= 4) + patch.append(p); + } + return patch; +} diff --git a/Build/source/utils/asymptote/base/binarytree.asy b/Build/source/utils/asymptote/base/binarytree.asy new file mode 100644 index 00000000000..4906c6ee58d --- /dev/null +++ b/Build/source/utils/asymptote/base/binarytree.asy @@ -0,0 +1,383 @@ +/* ********************************************************************** + * binarytree: An Asymptote module to draw binary trees * + * * + * Copyright(C) 2006 * + * Tobias Langner tobias[at]langner[dot]nightlabs[dot]de * + * * + * Modified by John Bowman * + * * + * Condensed mode: * + * Copyright(C) 2012 * + * Gerasimos Dimitriadis dimeg [at] intracom [dot] gr * + * * + ************************************************************************ + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 3 of the License, or(at your option) any later version. * + * * + * 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; + bool spans_calculated=false; + int left_span,total_left_span; + int right_span,total_right_span; + void update_spans(); + + // Get the horizontal span of the tree consisting of the current + // node plus the whole subtree that is rooted at the right child + // (condensed mode) + int getTotalRightSpan() { + if(spans_calculated == false) { + update_spans(); + } + + return total_right_span; + } + + // Get the horizontal span of the tree consisting of the current + // node plus the whole subtree that is rooted at the left child + // (condensed mode) + int getTotalLeftSpan() { + if(spans_calculated == false) { + update_spans(); + } + return total_left_span; + } + + // Get the horizontal distance between this node and its right child + // (condensed mode) + int getRightSpan() { + if(spans_calculated == false) { + update_spans(); + } + return right_span; + } + + // Get the horizontal distance between this node and its left child + // (condensed mode) + int getLeftSpan() { + if(spans_calculated == false) { + update_spans(); + } + return left_span; + } + + // Update all span figures for this node. + // condensed mode) + update_spans=new void() { + if(spans_calculated == true) + return; + + left_span=0; + total_left_span=0; + right_span=0; + total_right_span=0; + + if(left != null) { + left_span=left.getTotalRightSpan()+1; + total_left_span=left_span+left.getTotalLeftSpan(); + } + + if(right != null) { + right_span=right.getTotalLeftSpan()+1; + total_right_span=right_span+right.getTotalRightSpan(); + } + spans_calculated=true; + }; + + // set the left child of this node + void setLeft(binarytreeNode left) { + this.left=left; + this.left.parent=this; + } + + // set the right child of this node + void setRight(binarytreeNode right) { + this.right=right; + this.right.parent=this; + } + + // return a boolean indicating whether this node is the root + bool isRoot() { + return parent == null; + } + + // return the level of the subtree rooted at this node. + int getLevel() { + if(isRoot()) + return 1; + else + return parent.getLevel()+1; + } + + // set the children of this binarytreeNode + void setChildren(binarytreeNode left, binarytreeNode right) { + setLeft(left); + setRight(right); + } + + // create a new binarytreeNode with key <key> + static binarytreeNode binarytreeNode(int key) { + binarytreeNode toReturn=new binarytreeNode; + toReturn.key=key; + return toReturn; + } + + // returns the height of the subtree rooted at this node. + 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; + +// draw the tree rooted at the given <node> at the given position <pos>, with +// <height>=the height of the containing tree, +// <minDist>=the minimal horizontal distance of two nodes at the lowest level, +// <levelDist>=the vertical distance between two levels, +// <nodeDiameter>=the diameter of one node. +object draw(picture pic=currentpicture, binarytreeNode node, pair pos, + int height, real minDist, real levelDist, real nodeDiameter, + pen p=currentpen, bool condensed=false) { + Label label=Label(math((string) node.key),pos); + + binarytreeNode left=node.left; + binarytreeNode right=node.right; + + // return the distance for two nodes at the given <level> when the + // containing tree has height <height> + // and the minimal distance between two nodes is <minDist> . + real getDistance(int level, int height, real minDist) { + return(nodeDiameter+minDist)*2^(height-level); + } + + // return the horiontal distance between node <n> and its left child + // (condensed mode) + real getLeftDistance(binarytreeNode n) { + return(nodeDiameter+minDist) *(real)n.getLeftSpan() * 0.5; + } + + // return the horiontal distance between node <n> and its right child + // (condensed mode) + real getRightDistance(binarytreeNode n) { + return(nodeDiameter+minDist) *(real)n.getRightSpan() * 0.5; + } + + real dist=getDistance(node.getLevel(),height,minDist)/2; + + // draw the connection between the two nodes at the given positions + // by calculating the connection points and drawing the corresponding + // arrow. + void deferredDrawNodeConnection(pair parentPos, pair childPos) { + pic.add(new void(frame f, transform t) { + pair start,end; + // calculate connection path + transform T=shift(nodeDiameter/2*unit(t*childPos-t*parentPos)); + path arr=(T*t*parentPos)--(inverse(T)*t*childPos); + draw(f,PenMargin(arr,p).g,p,Arrow(5)); + }); + pic.addPoint(parentPos); + pic.addPoint(childPos); + } + + if(left != null) { + pair childPos; + if(condensed == false) { + childPos=pos-(0,levelDist)-(dist/2,0); + } + else { + childPos=pos-(0,levelDist)-((real)getLeftDistance(node),0); + } + draw(pic,left,childPos,height,minDist,levelDist,nodeDiameter,p,condensed); + deferredDrawNodeConnection(pos,childPos); + } + + if(right != null) { + pair childPos; + if(condensed == false) { + childPos=pos-(0,levelDist)+(dist/2,0); + } + else { + childPos=pos-(0,levelDist)+((real)getRightDistance(node),0); + } + draw(pic,right,childPos,height,minDist,levelDist,nodeDiameter,p,condensed); + deferredDrawNodeConnection(pos,childPos); + } + + 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; + + // add 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; + } + } + } + } + + // return the height of the tree + int getHeight() { + if(root == null) + return 0; + else + return root.getHeight(); + } + + // add 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; + } + + + // return 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; +} + +// draw the given binary tree. +void draw(picture pic=currentpicture, binarytree tree, + real minDist=minDistDefault, real nodeMargin=nodeMarginDefault, + pen p=currentpen, bool condensed=false) +{ + 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, + condensed); +} diff --git a/Build/source/utils/asymptote/base/bsp.asy b/Build/source/utils/asymptote/base/bsp.asy new file mode 100644 index 00000000000..526f264a772 --- /dev/null +++ b/Build/source/utils/asymptote/base/bsp.asy @@ -0,0 +1,209 @@ +private import math; +import three; + +real epsilon=10*realEpsilon; + +// Routines for hidden surface removal (via binary space partition): +// Structure face is derived from picture. +struct face { + picture pic; + transform t; + frame fit; + triple normal,point; + triple min,max; + void operator init(path3 p) { + this.normal=normal(p); + if(this.normal == O) abort("path is linear"); + this.point=point(p,0); + min=min(p); + max=max(p); + } + face copy() { + face f=new face; + f.pic=pic.copy(); + f.t=t; + f.normal=normal; + f.point=point; + f.min=min; + f.max=max; + add(f.fit,fit); + return f; + } +} + +picture operator cast(face f) {return f.pic;} +face operator cast(path3 p) {return face(p);} + +struct line { + triple point; + triple dir; +} + +private line intersection(face a, face b) +{ + line L; + L.point=intersectionpoint(a.normal,a.point,b.normal,b.point); + L.dir=unit(cross(a.normal,b.normal)); + return L; +} + +struct half { + pair[] left,right; + + // Sort the points in the pair array z according to whether they lie on the + // left or right side of the line L in the direction dir passing through P. + // Points exactly on L are considered to be on the right side. + // Also push any points of intersection of L with the path operator --(... z) + // onto each of the arrays left and right. + void operator init(pair dir, pair P ... pair[] z) { + pair lastz; + pair invdir=dir != 0 ? 1/dir : 0; + bool left,last; + for(int i=0; i < z.length; ++i) { + left=(invdir*z[i]).y > (invdir*P).y; + if(i > 0 && last != left) { + pair w=extension(P,P+dir,lastz,z[i]); + this.left.push(w); + this.right.push(w); + } + if(left) this.left.push(z[i]); + else this.right.push(z[i]); + last=left; + lastz=z[i]; + } + } +} + +struct splitface { + face back,front; +} + +// Return the pieces obtained by splitting face a by face cut. +splitface split(face a, face cut, projection P) +{ + splitface S; + + void nointersection() { + if(abs(dot(a.point-P.camera,a.normal)) >= + abs(dot(cut.point-P.camera,cut.normal))) { + S.back=a; + S.front=null; + } else { + S.back=null; + S.front=a; + } + } + + if(P.infinity) { + P=P.copy(); + static real factor=1/sqrtEpsilon; + P.camera *= factor*max(abs(a.min),abs(a.max), + abs(cut.min),abs(cut.max)); + } + + if((abs(a.normal-cut.normal) < epsilon || + abs(a.normal+cut.normal) < epsilon)) { + nointersection(); + return S; + } + + line L=intersection(a,cut); + + if(dot(P.camera-L.point,P.camera-P.target) < 0) { + nointersection(); + return S; + } + + pair point=a.t*project(L.point,P); + pair dir=a.t*project(L.point+L.dir,P)-point; + pair invdir=dir != 0 ? 1/dir : 0; + triple apoint=L.point+cross(L.dir,a.normal); + bool left=(invdir*(a.t*project(apoint,P))).y >= (invdir*point).y; + + real t=intersect(apoint,P.camera,cut.normal,cut.point); + bool rightfront=left ^ (t <= 0 || t >= 1); + + face back=a, front=a.copy(); + pair max=max(a.fit); + pair min=min(a.fit); + half h=half(dir,point,max,(min.x,max.y),min,(max.x,min.y),max); + if(h.right.length == 0) { + if(rightfront) front=null; + else back=null; + } else if(h.left.length == 0) { + if(rightfront) back=null; + else front=null; + } + if(front != null) + clip(front.fit,operator --(... rightfront ? h.right : h.left)--cycle, + zerowinding); + if(back != null) + clip(back.fit,operator --(... rightfront ? h.left : h.right)--cycle, + zerowinding); + S.back=back; + S.front=front; + return S; +} + +// A binary space partition +struct bsp +{ + bsp back; + bsp front; + face node; + + // Construct the bsp. + void operator init(face[] faces, projection P) { + if(faces.length != 0) { + this.node=faces.pop(); + face[] front,back; + for(int i=0; i < faces.length; ++i) { + splitface split=split(faces[i],this.node,P); + if(split.front != null) front.push(split.front); + if(split.back != null) back.push(split.back); + } + this.front=bsp(front,P); + this.back=bsp(back,P); + } + } + + // Draw from back to front. + void add(frame f) { + if(back != null) back.add(f); + add(f,node.fit,group=true); + if(labels(node.fit)) layer(f); // Draw over any existing TeX layers. + if(front != null) front.add(f); + } +} + +void add(picture pic=currentpicture, face[] faces, + projection P=currentprojection) +{ + int n=faces.length; + face[] Faces=new face[n]; + for(int i=0; i < n; ++i) + Faces[i]=faces[i].copy(); + + pic.add(new void (frame f, transform t, transform T, + pair m, pair M) { + // Fit all of the pictures so we know their exact sizes. + face[] faces=new face[n]; + for(int i=0; i < n; ++i) { + faces[i]=Faces[i].copy(); + face F=faces[i]; + F.t=t*T*F.pic.T; + F.fit=F.pic.fit(t,T*F.pic.T,m,M); + } + + bsp bsp=bsp(faces,P); + if(bsp != null) bsp.add(f); + }); + + for(int i=0; i < n; ++i) { + picture F=Faces[i].pic; + pic.userBox3(F.userMin3(), F.userMax3()); + pic.bounds.append(F.T, F.bounds); + // The above 2 lines should be replaced with a routine in picture which + // copies only sizing data from another picture. + } +} diff --git a/Build/source/utils/asymptote/base/colormap.asy b/Build/source/utils/asymptote/base/colormap.asy new file mode 100644 index 00000000000..8ab4e420d1f --- /dev/null +++ b/Build/source/utils/asymptote/base/colormap.asy @@ -0,0 +1,3890 @@ +// author: Fabian Hassler +// year: 2019 + +// This module implements a list of colormaps +// the code has been converted from the python library +// matplotlib 3.0.2 license under BSD +// Feel free to use or to modify the code + +// example: the generate a palette wistia +// pen[] Palette = wistia.palette() +// +// There are two types of palettes. For a complete list see below: +// +// 1) The segmented palettes can be used as +// <name>.palette(int NColors=256, real gamma=1.) +// NColors are the number of colors in the palette +// gamma is the gamma-factor +// +// 2) The listed palettes can only be used as +// <name>.palette() +// +// Both functions return pen[] that can be used as a palette in the +// module palette. + +// list of palettes +// see also https://matplotlib.org/tutorials/colors/colormaps.html + +// segmented palettes: +// CMRmap +// autumn +// binary +// bone +// cool +// coolwarm +// copper +// gist_earth +// gist_ncar +// gist_stern +// gray +// hot +// hsv +// jet +// nipy_spectral +// pink +// spring +// summer +// winter +// wistia + +// listed palettes: +// Accent +// Blues +// BrBG +// BuGn +// BuPu +// Dark2 +// GnBu +// Greens +// Greys +// OrRd +// Oranges +// PRGn +// Paired +// Pastel1 +// Pastel2 +// PiYG +// PuBuGn +// PuBu +// PuOr +// PuRd +// Purples +// RdBu +// RdGy +// RdPu +// RdYlBu +// RdYlGn +// Reds +// Set1 +// Set2 +// Set3 +// Spectral +// YlGnBu +// YlGn +// YlOrBr +// YlOrRd +// brg +// bwr +// seismic +// tab10 +// tab20 +// tab20b +// tab20c +// cividis +// inferno +// magma +// plasma +// twilight +// twilight_shifted +// viridis + + +// Example of usage: + +// import graph; +// import palette; +// import colormap; +// int NColors=5; +// pen[] Palette=spring.palette(NColors); +// palette(bounds(0,1),(0.,0),(500,50),Bottom,Palette); + +// +// SOURCE CODE +// +private real[] makeMappingArray(int N, triple[] data, real gamma=1.) { + real[] x; + real[] y0; + real[] y1; + + for (int i=0; i<data.length; ++i) { + x.push(data[i].x); + y0.push(data[i].y); + y1.push(data[i].z); + }; + + x = x*(N-1); + real[] lut = new real[N]; + real[] xind = (N - 1) * uniform(0, 1, N-1) ** gamma; + int[] ind = map(new int(real xi) {return search(x, xi);}, xind); + ind = ind[1:N-1]; // note that the index is shifted from python + + real[] dist = (xind[1:N-1] - x[ind])/(x[ind+1] - x[ind]); + + lut[1:N-1] = dist * (y0[ind+1] - y1[ind]) + y1[ind]; + lut[0] = y1[0]; + lut[N-1] = y0[y0.length-1]; + return lut; +} + +// struct for segmented data +struct seg_data { + private triple[] r; // red + private triple[] g; // green + private triple[] b; // blue + + void operator init(triple[] r, triple[] g, triple[] b) { + this.r=r; + this.g=g; + this.b=b; + } + + pen[] palette(int NColors=256, real gamma=1.) { + real[] red = makeMappingArray(NColors, this.r, gamma); + real[] green = makeMappingArray(NColors, this.g, gamma); + real[] blue = makeMappingArray(NColors, this.b, gamma); + + pen[] pal = + sequence(new pen(int i) {return rgb(red[i], green[i], blue[i]);}, + NColors); + + return pal; + } +} + +// struct for list data +struct list_data { + private pen[] data; + pen[] palette(){return data;} + void operator init(pen[] d) { + this.data=d; + } +} + +// +// DATA +// +list_data Accent = list_data(new pen[] { + rgb (0.4980392156862745, 0.788235294117647, 0.4980392156862745) , + rgb (0.7450980392156863, 0.6823529411764706, 0.8313725490196079) , + rgb (0.9921568627450981, 0.7529411764705882, 0.5254901960784314) , + rgb (1.0, 1.0, 0.6) , + rgb (0.2196078431372549, 0.4235294117647059, 0.6901960784313725) , + rgb (0.9411764705882353, 0.00784313725490196, 0.4980392156862745) , + rgb (0.7490196078431373, 0.3568627450980392, 0.09019607843137253) , + rgb (0.4, 0.4, 0.4) + }); + + +list_data Blues = list_data(new pen[] { + rgb (0.9686274509803922, 0.984313725490196, 1.0) , + rgb (0.8705882352941177, 0.9215686274509803, 0.9686274509803922) , + rgb (0.7764705882352941, 0.8588235294117647, 0.9372549019607843) , + rgb (0.6196078431372549, 0.792156862745098, 0.8823529411764706) , + rgb (0.4196078431372549, 0.6823529411764706, 0.8392156862745098) , + rgb (0.25882352941176473, 0.5725490196078431, 0.7764705882352941) , + rgb (0.12941176470588237, 0.44313725490196076, 0.7098039215686275) , + rgb (0.03137254901960784, 0.3176470588235294, 0.611764705882353) , + rgb (0.03137254901960784, 0.18823529411764706, 0.4196078431372549) + }); + + +list_data BrBG = list_data(new pen[] { + rgb (0.32941176470588235, 0.18823529411764706, 0.0196078431372549) , + rgb (0.5490196078431373, 0.3176470588235294, 0.0392156862745098) , + rgb (0.7490196078431373, 0.5058823529411764, 0.17647058823529413) , + rgb (0.8745098039215686, 0.7607843137254902, 0.49019607843137253) , + rgb (0.9647058823529412, 0.9098039215686274, 0.7647058823529411) , + rgb (0.9607843137254902, 0.9607843137254902, 0.9607843137254902) , + rgb (0.7803921568627451, 0.9176470588235294, 0.8980392156862745) , + rgb (0.5019607843137255, 0.803921568627451, 0.7568627450980392) , + rgb (0.20784313725490197, 0.592156862745098, 0.5607843137254902) , + rgb (0.00392156862745098, 0.4, 0.3686274509803922) , + rgb (0.0, 0.23529411764705882, 0.18823529411764706) + }); + + +list_data BuGn = list_data(new pen[] { + rgb (0.9686274509803922, 0.9882352941176471, 0.9921568627450981) , + rgb (0.8980392156862745, 0.9607843137254902, 0.9764705882352941) , + rgb (0.8, 0.9254901960784314, 0.9019607843137255) , + rgb (0.6, 0.8470588235294118, 0.788235294117647) , + rgb (0.4, 0.7607843137254902, 0.6431372549019608) , + rgb (0.2549019607843137, 0.6823529411764706, 0.4627450980392157) , + rgb (0.13725490196078433, 0.5450980392156862, 0.27058823529411763) , + rgb (0.0, 0.42745098039215684, 0.17254901960784313) , + rgb (0.0, 0.26666666666666666, 0.10588235294117647) + }); + + +list_data BuPu = list_data(new pen[] { + rgb (0.9686274509803922, 0.9882352941176471, 0.9921568627450981) , + rgb (0.8784313725490196, 0.9254901960784314, 0.9568627450980393) , + rgb (0.7490196078431373, 0.8274509803921568, 0.9019607843137255) , + rgb (0.6196078431372549, 0.7372549019607844, 0.8549019607843137) , + rgb (0.5490196078431373, 0.5882352941176471, 0.7764705882352941) , + rgb (0.5490196078431373, 0.4196078431372549, 0.6941176470588235) , + rgb (0.5333333333333333, 0.2549019607843137, 0.615686274509804) , + rgb (0.5058823529411764, 0.05882352941176471, 0.48627450980392156) , + rgb (0.30196078431372547, 0.0, 0.29411764705882354) + }); + + +seg_data CMRmap = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.125, 0.15, 0.15) , + (0.25, 0.3, 0.3) , + (0.375, 0.6, 0.6) , + (0.5, 1.0, 1.0) , + (0.625, 0.9, 0.9) , + (0.75, 0.9, 0.9) , + (0.875, 0.9, 0.9) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.125, 0.15, 0.15) , + (0.25, 0.15, 0.15) , + (0.375, 0.2, 0.2) , + (0.5, 0.25, 0.25) , + (0.625, 0.5, 0.5) , + (0.75, 0.75, 0.75) , + (0.875, 0.9, 0.9) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.125, 0.5, 0.5) , + (0.25, 0.75, 0.75) , + (0.375, 0.5, 0.5) , + (0.5, 0.15, 0.15) , + (0.625, 0.0, 0.0) , + (0.75, 0.1, 0.1) , + (0.875, 0.5, 0.5) , + (1.0, 1.0, 1.0) + } + ); + + +list_data Dark2 = list_data(new pen[] { + rgb (0.10588235294117647, 0.6196078431372549, 0.4666666666666667) , + rgb (0.8509803921568627, 0.37254901960784315, 0.00784313725490196) , + rgb (0.4588235294117647, 0.4392156862745098, 0.7019607843137254) , + rgb (0.9058823529411765, 0.1607843137254902, 0.5411764705882353) , + rgb (0.4, 0.6509803921568628, 0.11764705882352941) , + rgb (0.9019607843137255, 0.6705882352941176, 0.00784313725490196) , + rgb (0.6509803921568628, 0.4627450980392157, 0.11372549019607843) , + rgb (0.4, 0.4, 0.4) + }); + + +list_data GnBu = list_data(new pen[] { + rgb (0.9686274509803922, 0.9882352941176471, 0.9411764705882353) , + rgb (0.8784313725490196, 0.9529411764705882, 0.8588235294117647) , + rgb (0.8, 0.9215686274509803, 0.7725490196078432) , + rgb (0.6588235294117647, 0.8666666666666667, 0.7098039215686275) , + rgb (0.4823529411764706, 0.8, 0.7686274509803922) , + rgb (0.3058823529411765, 0.7019607843137254, 0.8274509803921568) , + rgb (0.16862745098039217, 0.5490196078431373, 0.7450980392156863) , + rgb (0.03137254901960784, 0.40784313725490196, 0.6745098039215687) , + rgb (0.03137254901960784, 0.25098039215686274, 0.5058823529411764) + }); + + +list_data Greens = list_data(new pen[] { + rgb (0.9686274509803922, 0.9882352941176471, 0.9607843137254902) , + rgb (0.8980392156862745, 0.9607843137254902, 0.8784313725490196) , + rgb (0.7803921568627451, 0.9137254901960784, 0.7529411764705882) , + rgb (0.6313725490196078, 0.8509803921568627, 0.6078431372549019) , + rgb (0.4549019607843137, 0.7686274509803922, 0.4627450980392157) , + rgb (0.2549019607843137, 0.6705882352941176, 0.36470588235294116) , + rgb (0.13725490196078433, 0.5450980392156862, 0.27058823529411763) , + rgb (0.0, 0.42745098039215684, 0.17254901960784313) , + rgb (0.0, 0.26666666666666666, 0.10588235294117647) + }); + + +list_data Greys = list_data(new pen[] { + rgb (1.0, 1.0, 1.0) , + rgb (0.9411764705882353, 0.9411764705882353, 0.9411764705882353) , + rgb (0.8509803921568627, 0.8509803921568627, 0.8509803921568627) , + rgb (0.7411764705882353, 0.7411764705882353, 0.7411764705882353) , + rgb (0.5882352941176471, 0.5882352941176471, 0.5882352941176471) , + rgb (0.45098039215686275, 0.45098039215686275, 0.45098039215686275) , + rgb (0.3215686274509804, 0.3215686274509804, 0.3215686274509804) , + rgb (0.1450980392156863, 0.1450980392156863, 0.1450980392156863) , + rgb (0.0, 0.0, 0.0) + }); + + +list_data OrRd = list_data(new pen[] { + rgb (1.0, 0.9686274509803922, 0.9254901960784314) , + rgb (0.996078431372549, 0.9098039215686274, 0.7843137254901961) , + rgb (0.9921568627450981, 0.8313725490196079, 0.6196078431372549) , + rgb (0.9921568627450981, 0.7333333333333333, 0.5176470588235295) , + rgb (0.9882352941176471, 0.5529411764705883, 0.34901960784313724) , + rgb (0.9372549019607843, 0.396078431372549, 0.2823529411764706) , + rgb (0.8431372549019608, 0.18823529411764706, 0.12156862745098039) , + rgb (0.7019607843137254, 0.0, 0.0) , + rgb (0.4980392156862745, 0.0, 0.0) + }); + + +list_data Oranges = list_data(new pen[] { + rgb (1.0, 0.9607843137254902, 0.9215686274509803) , + rgb (0.996078431372549, 0.9019607843137255, 0.807843137254902) , + rgb (0.9921568627450981, 0.8156862745098039, 0.6352941176470588) , + rgb (0.9921568627450981, 0.6823529411764706, 0.4196078431372549) , + rgb (0.9921568627450981, 0.5529411764705883, 0.23529411764705882) , + rgb (0.9450980392156862, 0.4117647058823529, 0.07450980392156863) , + rgb (0.8509803921568627, 0.2823529411764706, 0.00392156862745098) , + rgb (0.6509803921568628, 0.21176470588235294, 0.01176470588235294) , + rgb (0.4980392156862745, 0.15294117647058825, 0.01568627450980392) + }); + + +list_data PRGn = list_data(new pen[] { + rgb (0.25098039215686274, 0.0, 0.29411764705882354) , + rgb (0.4627450980392157, 0.16470588235294117, 0.5137254901960784) , + rgb (0.6, 0.4392156862745098, 0.6705882352941176) , + rgb (0.7607843137254902, 0.6470588235294118, 0.8117647058823529) , + rgb (0.9058823529411765, 0.8313725490196079, 0.9098039215686274) , + rgb (0.9686274509803922, 0.9686274509803922, 0.9686274509803922) , + rgb (0.8509803921568627, 0.9411764705882353, 0.8274509803921568) , + rgb (0.6509803921568628, 0.8588235294117647, 0.6274509803921569) , + rgb (0.35294117647058826, 0.6823529411764706, 0.3803921568627451) , + rgb (0.10588235294117647, 0.47058823529411764, 0.21568627450980393) , + rgb (0.0, 0.26666666666666666, 0.10588235294117647) + }); + + +list_data Paired = list_data(new pen[] { + rgb (0.6509803921568628, 0.807843137254902, 0.8901960784313725) , + rgb (0.12156862745098039, 0.47058823529411764, 0.7058823529411765) , + rgb (0.6980392156862745, 0.8745098039215686, 0.5411764705882353) , + rgb (0.2, 0.6274509803921569, 0.17254901960784313) , + rgb (0.984313725490196, 0.6039215686274509, 0.6) , + rgb (0.8901960784313725, 0.10196078431372549, 0.10980392156862745) , + rgb (0.9921568627450981, 0.7490196078431373, 0.43529411764705883) , + rgb (1.0, 0.4980392156862745, 0.0) , + rgb (0.792156862745098, 0.6980392156862745, 0.8392156862745098) , + rgb (0.41568627450980394, 0.23921568627450981, 0.6039215686274509) , + rgb (1.0, 1.0, 0.6) , + rgb (0.6941176470588235, 0.34901960784313724, 0.1568627450980392) + }); + + +list_data Pastel1 = list_data(new pen[] { + rgb (0.984313725490196, 0.7058823529411765, 0.6823529411764706) , + rgb (0.7019607843137254, 0.803921568627451, 0.8901960784313725) , + rgb (0.8, 0.9215686274509803, 0.7725490196078432) , + rgb (0.8705882352941177, 0.796078431372549, 0.8941176470588236) , + rgb (0.996078431372549, 0.8509803921568627, 0.6509803921568628) , + rgb (1.0, 1.0, 0.8) , + rgb (0.8980392156862745, 0.8470588235294118, 0.7411764705882353) , + rgb (0.9921568627450981, 0.8549019607843137, 0.9254901960784314) , + rgb (0.9490196078431372, 0.9490196078431372, 0.9490196078431372) + }); + + +list_data Pastel2 = list_data(new pen[] { + rgb (0.7019607843137254, 0.8862745098039215, 0.803921568627451) , + rgb (0.9921568627450981, 0.803921568627451, 0.6745098039215687) , + rgb (0.796078431372549, 0.8352941176470589, 0.9098039215686274) , + rgb (0.9568627450980393, 0.792156862745098, 0.8941176470588236) , + rgb (0.9019607843137255, 0.9607843137254902, 0.788235294117647) , + rgb (1.0, 0.9490196078431372, 0.6823529411764706) , + rgb (0.9450980392156862, 0.8862745098039215, 0.8) , + rgb (0.8, 0.8, 0.8) + }); + + +list_data PiYG = list_data(new pen[] { + rgb (0.5568627450980392, 0.00392156862745098, 0.3215686274509804) , + rgb (0.7725490196078432, 0.10588235294117647, 0.49019607843137253) , + rgb (0.8705882352941177, 0.4666666666666667, 0.6823529411764706) , + rgb (0.9450980392156862, 0.7137254901960784, 0.8549019607843137) , + rgb (0.9921568627450981, 0.8784313725490196, 0.9372549019607843) , + rgb (0.9686274509803922, 0.9686274509803922, 0.9686274509803922) , + rgb (0.9019607843137255, 0.9607843137254902, 0.8156862745098039) , + rgb (0.7215686274509804, 0.8823529411764706, 0.5254901960784314) , + rgb (0.4980392156862745, 0.7372549019607844, 0.2549019607843137) , + rgb (0.30196078431372547, 0.5725490196078431, 0.12941176470588237) , + rgb (0.15294117647058825, 0.39215686274509803, 0.09803921568627451) + }); + + +list_data PuBuGn = list_data(new pen[] { + rgb (1.0, 0.9686274509803922, 0.984313725490196) , + rgb (0.9254901960784314, 0.8862745098039215, 0.9411764705882353) , + rgb (0.8156862745098039, 0.8196078431372549, 0.9019607843137255) , + rgb (0.6509803921568628, 0.7411764705882353, 0.8588235294117647) , + rgb (0.403921568627451, 0.6627450980392157, 0.8117647058823529) , + rgb (0.21176470588235294, 0.5647058823529412, 0.7529411764705882) , + rgb (0.00784313725490196, 0.5058823529411764, 0.5411764705882353) , + rgb (0.00392156862745098, 0.4235294117647059, 0.34901960784313724) , + rgb (0.00392156862745098, 0.27450980392156865, 0.21176470588235294) + }); + + +list_data PuBu = list_data(new pen[] { + rgb (1.0, 0.9686274509803922, 0.984313725490196) , + rgb (0.9254901960784314, 0.9058823529411765, 0.9490196078431372) , + rgb (0.8156862745098039, 0.8196078431372549, 0.9019607843137255) , + rgb (0.6509803921568628, 0.7411764705882353, 0.8588235294117647) , + rgb (0.4549019607843137, 0.6627450980392157, 0.8117647058823529) , + rgb (0.21176470588235294, 0.5647058823529412, 0.7529411764705882) , + rgb (0.0196078431372549, 0.4392156862745098, 0.6901960784313725) , + rgb (0.01568627450980392, 0.35294117647058826, 0.5529411764705883) , + rgb (0.00784313725490196, 0.2196078431372549, 0.34509803921568627) + }); + + +list_data PuOr = list_data(new pen[] { + rgb (0.4980392156862745, 0.23137254901960785, 0.03137254901960784) , + rgb (0.7019607843137254, 0.34509803921568627, 0.02352941176470588) , + rgb (0.8784313725490196, 0.5098039215686274, 0.0784313725490196) , + rgb (0.9921568627450981, 0.7215686274509804, 0.38823529411764707) , + rgb (0.996078431372549, 0.8784313725490196, 0.7137254901960784) , + rgb (0.9686274509803922, 0.9686274509803922, 0.9686274509803922) , + rgb (0.8470588235294118, 0.8549019607843137, 0.9215686274509803) , + rgb (0.6980392156862745, 0.6705882352941176, 0.8235294117647058) , + rgb (0.5019607843137255, 0.45098039215686275, 0.6745098039215687) , + rgb (0.32941176470588235, 0.15294117647058825, 0.5333333333333333) , + rgb (0.17647058823529413, 0.0, 0.29411764705882354) + }); + + +list_data PuRd = list_data(new pen[] { + rgb (0.9686274509803922, 0.9568627450980393, 0.9764705882352941) , + rgb (0.9058823529411765, 0.8823529411764706, 0.9372549019607843) , + rgb (0.8313725490196079, 0.7254901960784313, 0.8549019607843137) , + rgb (0.788235294117647, 0.5803921568627451, 0.7803921568627451) , + rgb (0.8745098039215686, 0.396078431372549, 0.6901960784313725) , + rgb (0.9058823529411765, 0.1607843137254902, 0.5411764705882353) , + rgb (0.807843137254902, 0.07058823529411765, 0.33725490196078434) , + rgb (0.596078431372549, 0.0, 0.2627450980392157) , + rgb (0.403921568627451, 0.0, 0.12156862745098039) + }); + + +list_data Purples = list_data(new pen[] { + rgb (0.9882352941176471, 0.984313725490196, 0.9921568627450981) , + rgb (0.9372549019607843, 0.9294117647058824, 0.9607843137254902) , + rgb (0.8549019607843137, 0.8549019607843137, 0.9215686274509803) , + rgb (0.7372549019607844, 0.7411764705882353, 0.8627450980392157) , + rgb (0.6196078431372549, 0.6039215686274509, 0.7843137254901961) , + rgb (0.5019607843137255, 0.49019607843137253, 0.7294117647058823) , + rgb (0.41568627450980394, 0.3176470588235294, 0.6392156862745098) , + rgb (0.32941176470588235, 0.15294117647058825, 0.5607843137254902) , + rgb (0.24705882352941178, 0.0, 0.49019607843137253) + }); + + +list_data RdBu = list_data(new pen[] { + rgb (0.403921568627451, 0.0, 0.12156862745098039) , + rgb (0.6980392156862745, 0.09411764705882353, 0.16862745098039217) , + rgb (0.8392156862745098, 0.3764705882352941, 0.30196078431372547) , + rgb (0.9568627450980393, 0.6470588235294118, 0.5098039215686274) , + rgb (0.9921568627450981, 0.8588235294117647, 0.7803921568627451) , + rgb (0.9686274509803922, 0.9686274509803922, 0.9686274509803922) , + rgb (0.8196078431372549, 0.8980392156862745, 0.9411764705882353) , + rgb (0.5725490196078431, 0.7725490196078432, 0.8705882352941177) , + rgb (0.2627450980392157, 0.5764705882352941, 0.7647058823529411) , + rgb (0.12941176470588237, 0.4, 0.6745098039215687) , + rgb (0.0196078431372549, 0.18823529411764706, 0.3803921568627451) + }); + + +list_data RdGy = list_data(new pen[] { + rgb (0.403921568627451, 0.0, 0.12156862745098039) , + rgb (0.6980392156862745, 0.09411764705882353, 0.16862745098039217) , + rgb (0.8392156862745098, 0.3764705882352941, 0.30196078431372547) , + rgb (0.9568627450980393, 0.6470588235294118, 0.5098039215686274) , + rgb (0.9921568627450981, 0.8588235294117647, 0.7803921568627451) , + rgb (1.0, 1.0, 1.0) , + rgb (0.8784313725490196, 0.8784313725490196, 0.8784313725490196) , + rgb (0.7294117647058823, 0.7294117647058823, 0.7294117647058823) , + rgb (0.5294117647058824, 0.5294117647058824, 0.5294117647058824) , + rgb (0.30196078431372547, 0.30196078431372547, 0.30196078431372547) , + rgb (0.10196078431372549, 0.10196078431372549, 0.10196078431372549) + }); + + +list_data RdPu = list_data(new pen[] { + rgb (1.0, 0.9686274509803922, 0.9529411764705882) , + rgb (0.9921568627450981, 0.8784313725490196, 0.8666666666666667) , + rgb (0.9882352941176471, 0.7725490196078432, 0.7529411764705882) , + rgb (0.9803921568627451, 0.6235294117647059, 0.7098039215686275) , + rgb (0.9686274509803922, 0.40784313725490196, 0.6313725490196078) , + rgb (0.8666666666666667, 0.20392156862745098, 0.592156862745098) , + rgb (0.6823529411764706, 0.00392156862745098, 0.49411764705882355) , + rgb (0.47843137254901963, 0.00392156862745098, 0.4666666666666667) , + rgb (0.28627450980392155, 0.0, 0.41568627450980394) + }); + + +list_data RdYlBu = list_data(new pen[] { + rgb (0.6470588235294118, 0.0, 0.14901960784313725) , + rgb (0.8431372549019608, 0.18823529411764706, 0.15294117647058825) , + rgb (0.9568627450980393, 0.42745098039215684, 0.2627450980392157) , + rgb (0.9921568627450981, 0.6823529411764706, 0.3803921568627451) , + rgb (0.996078431372549, 0.8784313725490196, 0.5647058823529412) , + rgb (1.0, 1.0, 0.7490196078431373) , + rgb (0.8784313725490196, 0.9529411764705882, 0.9725490196078431) , + rgb (0.6705882352941176, 0.8509803921568627, 0.9137254901960784) , + rgb (0.4549019607843137, 0.6784313725490196, 0.8196078431372549) , + rgb (0.27058823529411763, 0.4588235294117647, 0.7058823529411765) , + rgb (0.19215686274509805, 0.21176470588235294, 0.5843137254901961) + }); + + +list_data RdYlGn = list_data(new pen[] { + rgb (0.6470588235294118, 0.0, 0.14901960784313725) , + rgb (0.8431372549019608, 0.18823529411764706, 0.15294117647058825) , + rgb (0.9568627450980393, 0.42745098039215684, 0.2627450980392157) , + rgb (0.9921568627450981, 0.6823529411764706, 0.3803921568627451) , + rgb (0.996078431372549, 0.8784313725490196, 0.5450980392156862) , + rgb (1.0, 1.0, 0.7490196078431373) , + rgb (0.8509803921568627, 0.9372549019607843, 0.5450980392156862) , + rgb (0.6509803921568628, 0.8509803921568627, 0.41568627450980394) , + rgb (0.4, 0.7411764705882353, 0.38823529411764707) , + rgb (0.10196078431372549, 0.596078431372549, 0.3137254901960784) , + rgb (0.0, 0.40784313725490196, 0.21568627450980393) + }); + + +list_data Reds = list_data(new pen[] { + rgb (1.0, 0.9607843137254902, 0.9411764705882353) , + rgb (0.996078431372549, 0.8784313725490196, 0.8235294117647058) , + rgb (0.9882352941176471, 0.7333333333333333, 0.6313725490196078) , + rgb (0.9882352941176471, 0.5725490196078431, 0.4470588235294118) , + rgb (0.984313725490196, 0.41568627450980394, 0.2901960784313726) , + rgb (0.9372549019607843, 0.23137254901960785, 0.17254901960784313) , + rgb (0.796078431372549, 0.09411764705882353, 0.11372549019607843) , + rgb (0.6470588235294118, 0.058823529411764705, 0.08235294117647057) , + rgb (0.403921568627451, 0.0, 0.05098039215686274) + }); + + +list_data Set1 = list_data(new pen[] { + rgb (0.8941176470588236, 0.10196078431372549, 0.10980392156862745) , + rgb (0.21568627450980393, 0.49411764705882355, 0.7215686274509804) , + rgb (0.30196078431372547, 0.6862745098039216, 0.2901960784313726) , + rgb (0.596078431372549, 0.3058823529411765, 0.6392156862745098) , + rgb (1.0, 0.4980392156862745, 0.0) , + rgb (1.0, 1.0, 0.2) , + rgb (0.6509803921568628, 0.33725490196078434, 0.1568627450980392) , + rgb (0.9686274509803922, 0.5058823529411764, 0.7490196078431373) , + rgb (0.6, 0.6, 0.6) + }); + + +list_data Set2 = list_data(new pen[] { + rgb (0.4, 0.7607843137254902, 0.6470588235294118) , + rgb (0.9882352941176471, 0.5529411764705883, 0.3843137254901961) , + rgb (0.5529411764705883, 0.6274509803921569, 0.796078431372549) , + rgb (0.9058823529411765, 0.5411764705882353, 0.7647058823529411) , + rgb (0.6509803921568628, 0.8470588235294118, 0.32941176470588235) , + rgb (1.0, 0.8509803921568627, 0.1843137254901961) , + rgb (0.8980392156862745, 0.7686274509803922, 0.5803921568627451) , + rgb (0.7019607843137254, 0.7019607843137254, 0.7019607843137254) + }); + + +list_data Set3 = list_data(new pen[] { + rgb (0.5529411764705883, 0.8274509803921568, 0.7803921568627451) , + rgb (1.0, 1.0, 0.7019607843137254) , + rgb (0.7450980392156863, 0.7294117647058823, 0.8549019607843137) , + rgb (0.984313725490196, 0.5019607843137255, 0.4470588235294118) , + rgb (0.5019607843137255, 0.6941176470588235, 0.8274509803921568) , + rgb (0.9921568627450981, 0.7058823529411765, 0.3843137254901961) , + rgb (0.7019607843137254, 0.8705882352941177, 0.4117647058823529) , + rgb (0.9882352941176471, 0.803921568627451, 0.8980392156862745) , + rgb (0.8509803921568627, 0.8509803921568627, 0.8509803921568627) , + rgb (0.7372549019607844, 0.5019607843137255, 0.7411764705882353) , + rgb (0.8, 0.9215686274509803, 0.7725490196078432) , + rgb (1.0, 0.9294117647058824, 0.43529411764705883) + }); + + +list_data Spectral = list_data(new pen[] { + rgb (0.6196078431372549, 0.00392156862745098, 0.25882352941176473) , + rgb (0.8352941176470589, 0.24313725490196078, 0.30980392156862746) , + rgb (0.9568627450980393, 0.42745098039215684, 0.2627450980392157) , + rgb (0.9921568627450981, 0.6823529411764706, 0.3803921568627451) , + rgb (0.996078431372549, 0.8784313725490196, 0.5450980392156862) , + rgb (1.0, 1.0, 0.7490196078431373) , + rgb (0.9019607843137255, 0.9607843137254902, 0.596078431372549) , + rgb (0.6705882352941176, 0.8666666666666667, 0.6431372549019608) , + rgb (0.4, 0.7607843137254902, 0.6470588235294118) , + rgb (0.19607843137254902, 0.5333333333333333, 0.7411764705882353) , + rgb (0.3686274509803922, 0.30980392156862746, 0.6352941176470588) + }); + + +list_data YlGnBu = list_data(new pen[] { + rgb (1.0, 1.0, 0.8509803921568627) , + rgb (0.9294117647058824, 0.9725490196078431, 0.6941176470588235) , + rgb (0.7803921568627451, 0.9137254901960784, 0.7058823529411765) , + rgb (0.4980392156862745, 0.803921568627451, 0.7333333333333333) , + rgb (0.2549019607843137, 0.7137254901960784, 0.7686274509803922) , + rgb (0.11372549019607843, 0.5686274509803921, 0.7529411764705882) , + rgb (0.13333333333333333, 0.3686274509803922, 0.6588235294117647) , + rgb (0.1450980392156863, 0.20392156862745098, 0.5803921568627451) , + rgb (0.03137254901960784, 0.11372549019607843, 0.34509803921568627) + }); + + +list_data YlGn = list_data(new pen[] { + rgb (1.0, 1.0, 0.8980392156862745) , + rgb (0.9686274509803922, 0.9882352941176471, 0.7254901960784313) , + rgb (0.8509803921568627, 0.9411764705882353, 0.6392156862745098) , + rgb (0.6784313725490196, 0.8666666666666667, 0.5568627450980392) , + rgb (0.47058823529411764, 0.7764705882352941, 0.4745098039215686) , + rgb (0.2549019607843137, 0.6705882352941176, 0.36470588235294116) , + rgb (0.13725490196078433, 0.5176470588235295, 0.2627450980392157) , + rgb (0.0, 0.40784313725490196, 0.21568627450980393) , + rgb (0.0, 0.27058823529411763, 0.1607843137254902) + }); + + +list_data YlOrBr = list_data(new pen[] { + rgb (1.0, 1.0, 0.8980392156862745) , + rgb (1.0, 0.9686274509803922, 0.7372549019607844) , + rgb (0.996078431372549, 0.8901960784313725, 0.5686274509803921) , + rgb (0.996078431372549, 0.7686274509803922, 0.30980392156862746) , + rgb (0.996078431372549, 0.6, 0.1607843137254902) , + rgb (0.9254901960784314, 0.4392156862745098, 0.0784313725490196) , + rgb (0.8, 0.2980392156862745, 0.00784313725490196) , + rgb (0.6, 0.20392156862745098, 0.01568627450980392) , + rgb (0.4, 0.1450980392156863, 0.02352941176470588) + }); + + +list_data YlOrRd = list_data(new pen[] { + rgb (1.0, 1.0, 0.8) , + rgb (1.0, 0.9294117647058824, 0.6274509803921569) , + rgb (0.996078431372549, 0.8509803921568627, 0.4627450980392157) , + rgb (0.996078431372549, 0.6980392156862745, 0.2980392156862745) , + rgb (0.9921568627450981, 0.5529411764705883, 0.23529411764705882) , + rgb (0.9882352941176471, 0.3058823529411765, 0.16470588235294117) , + rgb (0.8901960784313725, 0.10196078431372549, 0.10980392156862745) , + rgb (0.7411764705882353, 0.0, 0.14901960784313725) , + rgb (0.5019607843137255, 0.0, 0.14901960784313725) + }); + + +seg_data autumn = seg_data( + new triple[] { // red + (0.0, 1.0, 1.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (1.0, 0.0, 0.0) + } + ); + + +seg_data binary = seg_data( + new triple[] { // red + (0.0, 1.0, 1.0) , + (1.0, 0.0, 0.0) + }, + new triple[] { // green + (0.0, 1.0, 1.0) , + (1.0, 0.0, 0.0) + }, + new triple[] { // blue + (0.0, 1.0, 1.0) , + (1.0, 0.0, 0.0) + } + ); + + +seg_data bone = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.746032, 0.652778, 0.652778) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.365079, 0.319444, 0.319444) , + (0.746032, 0.777778, 0.777778) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.365079, 0.444444, 0.444444) , + (1.0, 1.0, 1.0) + } + ); + + +list_data brg = list_data(new pen[] { + rgb (0.0, 0.0, 1.0) , + rgb (1.0, 0.0, 0.0) , + rgb (0.0, 1.0, 0.0) + }); + + +list_data bwr = list_data(new pen[] { + rgb (0.0, 0.0, 1.0) , + rgb (1.0, 1.0, 1.0) , + rgb (1.0, 0.0, 0.0) + }); + + +seg_data cool = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 1.0, 1.0) , + (1.0, 0.0, 0.0) + }, + new triple[] { // blue + (0.0, 1.0, 1.0) , + (1.0, 1.0, 1.0) + } + ); + + +seg_data coolwarm = seg_data( + new triple[] { // red + (0.0, 0.2298057, 0.2298057) , + (0.03125, 0.26623388, 0.26623388) , + (0.0625, 0.30386891, 0.30386891) , + (0.09375, 0.342804478, 0.342804478) , + (0.125, 0.38301334, 0.38301334) , + (0.15625, 0.424369608, 0.424369608) , + (0.1875, 0.46666708, 0.46666708) , + (0.21875, 0.509635204, 0.509635204) , + (0.25, 0.552953156, 0.552953156) , + (0.28125, 0.596262162, 0.596262162) , + (0.3125, 0.639176211, 0.639176211) , + (0.34375, 0.681291281, 0.681291281) , + (0.375, 0.722193294, 0.722193294) , + (0.40625, 0.761464949, 0.761464949) , + (0.4375, 0.798691636, 0.798691636) , + (0.46875, 0.833466556, 0.833466556) , + (0.5, 0.865395197, 0.865395197) , + (0.53125, 0.897787179, 0.897787179) , + (0.5625, 0.924127593, 0.924127593) , + (0.59375, 0.944468518, 0.944468518) , + (0.625, 0.958852946, 0.958852946) , + (0.65625, 0.96732803, 0.96732803) , + (0.6875, 0.969954137, 0.969954137) , + (0.71875, 0.966811177, 0.966811177) , + (0.75, 0.958003065, 0.958003065) , + (0.78125, 0.943660866, 0.943660866) , + (0.8125, 0.923944917, 0.923944917) , + (0.84375, 0.89904617, 0.89904617) , + (0.875, 0.869186849, 0.869186849) , + (0.90625, 0.834620542, 0.834620542) , + (0.9375, 0.795631745, 0.795631745) , + (0.96875, 0.752534934, 0.752534934) , + (1.0, 0.705673158, 0.705673158) + }, + new triple[] { // green + (0.0, 0.298717966, 0.298717966) , + (0.03125, 0.353094838, 0.353094838) , + (0.0625, 0.406535296, 0.406535296) , + (0.09375, 0.458757618, 0.458757618) , + (0.125, 0.50941904, 0.50941904) , + (0.15625, 0.558148092, 0.558148092) , + (0.1875, 0.604562568, 0.604562568) , + (0.21875, 0.648280772, 0.648280772) , + (0.25, 0.688929332, 0.688929332) , + (0.28125, 0.726149107, 0.726149107) , + (0.3125, 0.759599947, 0.759599947) , + (0.34375, 0.788964712, 0.788964712) , + (0.375, 0.813952739, 0.813952739) , + (0.40625, 0.834302879, 0.834302879) , + (0.4375, 0.849786142, 0.849786142) , + (0.46875, 0.860207984, 0.860207984) , + (0.5, 0.86541021, 0.86541021) , + (0.53125, 0.848937047, 0.848937047) , + (0.5625, 0.827384882, 0.827384882) , + (0.59375, 0.800927443, 0.800927443) , + (0.625, 0.769767752, 0.769767752) , + (0.65625, 0.734132809, 0.734132809) , + (0.6875, 0.694266682, 0.694266682) , + (0.71875, 0.650421156, 0.650421156) , + (0.75, 0.602842431, 0.602842431) , + (0.78125, 0.551750968, 0.551750968) , + (0.8125, 0.49730856, 0.49730856) , + (0.84375, 0.439559467, 0.439559467) , + (0.875, 0.378313092, 0.378313092) , + (0.90625, 0.312874446, 0.312874446) , + (0.9375, 0.24128379, 0.24128379) , + (0.96875, 0.157246067, 0.157246067) , + (1.0, 0.01555616, 0.01555616) + }, + new triple[] { // blue + (0.0, 0.753683153, 0.753683153) , + (0.03125, 0.801466763, 0.801466763) , + (0.0625, 0.84495867, 0.84495867) , + (0.09375, 0.883725899, 0.883725899) , + (0.125, 0.917387822, 0.917387822) , + (0.15625, 0.945619588, 0.945619588) , + (0.1875, 0.968154911, 0.968154911) , + (0.21875, 0.98478814, 0.98478814) , + (0.25, 0.995375608, 0.995375608) , + (0.28125, 0.999836203, 0.999836203) , + (0.3125, 0.998151185, 0.998151185) , + (0.34375, 0.990363227, 0.990363227) , + (0.375, 0.976574709, 0.976574709) , + (0.40625, 0.956945269, 0.956945269) , + (0.4375, 0.931688648, 0.931688648) , + (0.46875, 0.901068838, 0.901068838) , + (0.5, 0.865395561, 0.865395561) , + (0.53125, 0.820880546, 0.820880546) , + (0.5625, 0.774508472, 0.774508472) , + (0.59375, 0.726736146, 0.726736146) , + (0.625, 0.678007945, 0.678007945) , + (0.65625, 0.628751763, 0.628751763) , + (0.6875, 0.579375448, 0.579375448) , + (0.71875, 0.530263762, 0.530263762) , + (0.75, 0.481775914, 0.481775914) , + (0.78125, 0.434243684, 0.434243684) , + (0.8125, 0.387970225, 0.387970225) , + (0.84375, 0.343229596, 0.343229596) , + (0.875, 0.300267182, 0.300267182) , + (0.90625, 0.259301199, 0.259301199) , + (0.9375, 0.220525627, 0.220525627) , + (0.96875, 0.184115123, 0.184115123) , + (1.0, 0.150232812, 0.150232812) + } + ); + + +seg_data copper = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.809524, 1.0, 1.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (1.0, 0.7812, 0.7812) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (1.0, 0.4975, 0.4975) + } + ); + + +seg_data gist_earth = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.2824, 0.1882, 0.1882) , + (0.4588, 0.2714, 0.2714) , + (0.549, 0.4719, 0.4719) , + (0.698, 0.7176, 0.7176) , + (0.7882, 0.7553, 0.7553) , + (1.0, 0.9922, 0.9922) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.0275, 0.0, 0.0) , + (0.1098, 0.1893, 0.1893) , + (0.1647, 0.3035, 0.3035) , + (0.2078, 0.3841, 0.3841) , + (0.2824, 0.502, 0.502) , + (0.5216, 0.6397, 0.6397) , + (0.698, 0.7171, 0.7171) , + (0.7882, 0.6392, 0.6392) , + (0.7922, 0.6413, 0.6413) , + (0.8, 0.6447, 0.6447) , + (0.8078, 0.6481, 0.6481) , + (0.8157, 0.6549, 0.6549) , + (0.8667, 0.6991, 0.6991) , + (0.8745, 0.7103, 0.7103) , + (0.8824, 0.7216, 0.7216) , + (0.8902, 0.7323, 0.7323) , + (0.898, 0.743, 0.743) , + (0.9412, 0.8275, 0.8275) , + (0.9569, 0.8635, 0.8635) , + (0.9647, 0.8816, 0.8816) , + (0.9961, 0.9733, 0.9733) , + (1.0, 0.9843, 0.9843) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.0039, 0.1684, 0.1684) , + (0.0078, 0.2212, 0.2212) , + (0.0275, 0.4329, 0.4329) , + (0.0314, 0.4549, 0.4549) , + (0.2824, 0.5004, 0.5004) , + (0.4667, 0.2748, 0.2748) , + (0.5451, 0.3205, 0.3205) , + (0.7843, 0.3961, 0.3961) , + (0.8941, 0.6651, 0.6651) , + (1.0, 0.9843, 0.9843) + } + ); + + +seg_data gist_ncar = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.3098, 0.0, 0.0) , + (0.3725, 0.3993, 0.3993) , + (0.4235, 0.5003, 0.5003) , + (0.5333, 1.0, 1.0) , + (0.7922, 1.0, 1.0) , + (0.8471, 0.6218, 0.6218) , + (0.898, 0.9235, 0.9235) , + (1.0, 0.9961, 0.9961) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.051, 0.3722, 0.3722) , + (0.1059, 0.0, 0.0) , + (0.1569, 0.7202, 0.7202) , + (0.1608, 0.7537, 0.7537) , + (0.1647, 0.7752, 0.7752) , + (0.2157, 1.0, 1.0) , + (0.2588, 0.9804, 0.9804) , + (0.2706, 0.9804, 0.9804) , + (0.3176, 1.0, 1.0) , + (0.3686, 0.8081, 0.8081) , + (0.4275, 1.0, 1.0) , + (0.5216, 1.0, 1.0) , + (0.6314, 0.7292, 0.7292) , + (0.6863, 0.2796, 0.2796) , + (0.7451, 0.0, 0.0) , + (0.7922, 0.0, 0.0) , + (0.8431, 0.1753, 0.1753) , + (0.898, 0.5, 0.5) , + (1.0, 0.9725, 0.9725) + }, + new triple[] { // blue + (0.0, 0.502, 0.502) , + (0.051, 0.0222, 0.0222) , + (0.1098, 1.0, 1.0) , + (0.2039, 1.0, 1.0) , + (0.2627, 0.6145, 0.6145) , + (0.3216, 0.0, 0.0) , + (0.4157, 0.0, 0.0) , + (0.4745, 0.2342, 0.2342) , + (0.5333, 0.0, 0.0) , + (0.5804, 0.0, 0.0) , + (0.6314, 0.0549, 0.0549) , + (0.6902, 0.0, 0.0) , + (0.7373, 0.0, 0.0) , + (0.7922, 0.9738, 0.9738) , + (0.8, 1.0, 1.0) , + (0.8431, 1.0, 1.0) , + (0.898, 0.9341, 0.9341) , + (1.0, 0.9961, 0.9961) + } + ); + + +seg_data gist_stern = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.0547, 1.0, 1.0) , + (0.25, 0.027, 0.25) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0, 0, 0) , + (1, 1, 1) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.5, 1.0, 1.0) , + (0.735, 0.0, 0.0) , + (1.0, 1.0, 1.0) + } + ); + + +seg_data gray = seg_data( + new triple[] { // red + (0.0, 0, 0) , + (1.0, 1, 1) + }, + new triple[] { // green + (0.0, 0, 0) , + (1.0, 1, 1) + }, + new triple[] { // blue + (0.0, 0, 0) , + (1.0, 1, 1) + } + ); + + +seg_data hot = seg_data( + new triple[] { // red + (0.0, 0.0416, 0.0416) , + (0.365079, 1.0, 1.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.365079, 0.0, 0.0) , + (0.746032, 1.0, 1.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.746032, 0.0, 0.0) , + (1.0, 1.0, 1.0) + } + ); + + +seg_data hsv = seg_data( + new triple[] { // red + (0.0, 1.0, 1.0) , + (0.15873, 1.0, 1.0) , + (0.174603, 0.96875, 0.96875) , + (0.333333, 0.03125, 0.03125) , + (0.349206, 0.0, 0.0) , + (0.666667, 0.0, 0.0) , + (0.68254, 0.03125, 0.03125) , + (0.84127, 0.96875, 0.96875) , + (0.857143, 1.0, 1.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.15873, 0.9375, 0.9375) , + (0.174603, 1.0, 1.0) , + (0.507937, 1.0, 1.0) , + (0.666667, 0.0625, 0.0625) , + (0.68254, 0.0, 0.0) , + (1.0, 0.0, 0.0) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.333333, 0.0, 0.0) , + (0.349206, 0.0625, 0.0625) , + (0.507937, 1.0, 1.0) , + (0.84127, 1.0, 1.0) , + (0.857143, 0.9375, 0.9375) , + (1.0, 0.09375, 0.09375) + } + ); + + +seg_data jet = seg_data( + new triple[] { // red + (0.0, 0, 0) , + (0.35, 0, 0) , + (0.66, 1, 1) , + (0.89, 1, 1) , + (1, 0.5, 0.5) + }, + new triple[] { // green + (0.0, 0, 0) , + (0.125, 0, 0) , + (0.375, 1, 1) , + (0.64, 1, 1) , + (0.91, 0, 0) , + (1, 0, 0) + }, + new triple[] { // blue + (0.0, 0.5, 0.5) , + (0.11, 1, 1) , + (0.34, 1, 1) , + (0.65, 0, 0) , + (1, 0, 0) + } + ); + + +seg_data nipy_spectral = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (0.05, 0.4667, 0.4667) , + (0.1, 0.5333, 0.5333) , + (0.15, 0.0, 0.0) , + (0.2, 0.0, 0.0) , + (0.25, 0.0, 0.0) , + (0.3, 0.0, 0.0) , + (0.35, 0.0, 0.0) , + (0.4, 0.0, 0.0) , + (0.45, 0.0, 0.0) , + (0.5, 0.0, 0.0) , + (0.55, 0.0, 0.0) , + (0.6, 0.0, 0.0) , + (0.65, 0.7333, 0.7333) , + (0.7, 0.9333, 0.9333) , + (0.75, 1.0, 1.0) , + (0.8, 1.0, 1.0) , + (0.85, 1.0, 1.0) , + (0.9, 0.8667, 0.8667) , + (0.95, 0.8, 0.8) , + (1.0, 0.8, 0.8) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.05, 0.0, 0.0) , + (0.1, 0.0, 0.0) , + (0.15, 0.0, 0.0) , + (0.2, 0.0, 0.0) , + (0.25, 0.4667, 0.4667) , + (0.3, 0.6, 0.6) , + (0.35, 0.6667, 0.6667) , + (0.4, 0.6667, 0.6667) , + (0.45, 0.6, 0.6) , + (0.5, 0.7333, 0.7333) , + (0.55, 0.8667, 0.8667) , + (0.6, 1.0, 1.0) , + (0.65, 1.0, 1.0) , + (0.7, 0.9333, 0.9333) , + (0.75, 0.8, 0.8) , + (0.8, 0.6, 0.6) , + (0.85, 0.0, 0.0) , + (0.9, 0.0, 0.0) , + (0.95, 0.0, 0.0) , + (1.0, 0.8, 0.8) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.05, 0.5333, 0.5333) , + (0.1, 0.6, 0.6) , + (0.15, 0.6667, 0.6667) , + (0.2, 0.8667, 0.8667) , + (0.25, 0.8667, 0.8667) , + (0.3, 0.8667, 0.8667) , + (0.35, 0.6667, 0.6667) , + (0.4, 0.5333, 0.5333) , + (0.45, 0.0, 0.0) , + (0.5, 0.0, 0.0) , + (0.55, 0.0, 0.0) , + (0.6, 0.0, 0.0) , + (0.65, 0.0, 0.0) , + (0.7, 0.0, 0.0) , + (0.75, 0.0, 0.0) , + (0.8, 0.0, 0.0) , + (0.85, 0.0, 0.0) , + (0.9, 0.0, 0.0) , + (0.95, 0.0, 0.0) , + (1.0, 0.8, 0.8) + } + ); + + +seg_data pink = seg_data( + new triple[] { // red + (0.0, 0.1178, 0.1178) , + (0.015873, 0.195857, 0.195857) , + (0.031746, 0.250661, 0.250661) , + (0.047619, 0.295468, 0.295468) , + (0.063492, 0.334324, 0.334324) , + (0.079365, 0.369112, 0.369112) , + (0.095238, 0.400892, 0.400892) , + (0.111111, 0.430331, 0.430331) , + (0.126984, 0.457882, 0.457882) , + (0.142857, 0.483867, 0.483867) , + (0.15873, 0.508525, 0.508525) , + (0.174603, 0.532042, 0.532042) , + (0.190476, 0.554563, 0.554563) , + (0.206349, 0.576204, 0.576204) , + (0.222222, 0.597061, 0.597061) , + (0.238095, 0.617213, 0.617213) , + (0.253968, 0.636729, 0.636729) , + (0.269841, 0.655663, 0.655663) , + (0.285714, 0.674066, 0.674066) , + (0.301587, 0.69198, 0.69198) , + (0.31746, 0.709441, 0.709441) , + (0.333333, 0.726483, 0.726483) , + (0.349206, 0.743134, 0.743134) , + (0.365079, 0.759421, 0.759421) , + (0.380952, 0.766356, 0.766356) , + (0.396825, 0.773229, 0.773229) , + (0.412698, 0.780042, 0.780042) , + (0.428571, 0.786796, 0.786796) , + (0.444444, 0.793492, 0.793492) , + (0.460317, 0.800132, 0.800132) , + (0.47619, 0.806718, 0.806718) , + (0.492063, 0.81325, 0.81325) , + (0.507937, 0.81973, 0.81973) , + (0.52381, 0.82616, 0.82616) , + (0.539683, 0.832539, 0.832539) , + (0.555556, 0.83887, 0.83887) , + (0.571429, 0.845154, 0.845154) , + (0.587302, 0.851392, 0.851392) , + (0.603175, 0.857584, 0.857584) , + (0.619048, 0.863731, 0.863731) , + (0.634921, 0.869835, 0.869835) , + (0.650794, 0.875897, 0.875897) , + (0.666667, 0.881917, 0.881917) , + (0.68254, 0.887896, 0.887896) , + (0.698413, 0.893835, 0.893835) , + (0.714286, 0.899735, 0.899735) , + (0.730159, 0.905597, 0.905597) , + (0.746032, 0.911421, 0.911421) , + (0.761905, 0.917208, 0.917208) , + (0.777778, 0.922958, 0.922958) , + (0.793651, 0.928673, 0.928673) , + (0.809524, 0.934353, 0.934353) , + (0.825397, 0.939999, 0.939999) , + (0.84127, 0.945611, 0.945611) , + (0.857143, 0.95119, 0.95119) , + (0.873016, 0.956736, 0.956736) , + (0.888889, 0.96225, 0.96225) , + (0.904762, 0.967733, 0.967733) , + (0.920635, 0.973185, 0.973185) , + (0.936508, 0.978607, 0.978607) , + (0.952381, 0.983999, 0.983999) , + (0.968254, 0.989361, 0.989361) , + (0.984127, 0.994695, 0.994695) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (0.015873, 0.102869, 0.102869) , + (0.031746, 0.145479, 0.145479) , + (0.047619, 0.178174, 0.178174) , + (0.063492, 0.205738, 0.205738) , + (0.079365, 0.230022, 0.230022) , + (0.095238, 0.251976, 0.251976) , + (0.111111, 0.272166, 0.272166) , + (0.126984, 0.290957, 0.290957) , + (0.142857, 0.308607, 0.308607) , + (0.15873, 0.3253, 0.3253) , + (0.174603, 0.341178, 0.341178) , + (0.190476, 0.356348, 0.356348) , + (0.206349, 0.370899, 0.370899) , + (0.222222, 0.3849, 0.3849) , + (0.238095, 0.39841, 0.39841) , + (0.253968, 0.411476, 0.411476) , + (0.269841, 0.424139, 0.424139) , + (0.285714, 0.436436, 0.436436) , + (0.301587, 0.448395, 0.448395) , + (0.31746, 0.460044, 0.460044) , + (0.333333, 0.471405, 0.471405) , + (0.349206, 0.482498, 0.482498) , + (0.365079, 0.493342, 0.493342) , + (0.380952, 0.517549, 0.517549) , + (0.396825, 0.540674, 0.540674) , + (0.412698, 0.562849, 0.562849) , + (0.428571, 0.584183, 0.584183) , + (0.444444, 0.604765, 0.604765) , + (0.460317, 0.624669, 0.624669) , + (0.47619, 0.643958, 0.643958) , + (0.492063, 0.662687, 0.662687) , + (0.507937, 0.6809, 0.6809) , + (0.52381, 0.698638, 0.698638) , + (0.539683, 0.715937, 0.715937) , + (0.555556, 0.732828, 0.732828) , + (0.571429, 0.749338, 0.749338) , + (0.587302, 0.765493, 0.765493) , + (0.603175, 0.781313, 0.781313) , + (0.619048, 0.796819, 0.796819) , + (0.634921, 0.812029, 0.812029) , + (0.650794, 0.82696, 0.82696) , + (0.666667, 0.841625, 0.841625) , + (0.68254, 0.85604, 0.85604) , + (0.698413, 0.870216, 0.870216) , + (0.714286, 0.884164, 0.884164) , + (0.730159, 0.897896, 0.897896) , + (0.746032, 0.911421, 0.911421) , + (0.761905, 0.917208, 0.917208) , + (0.777778, 0.922958, 0.922958) , + (0.793651, 0.928673, 0.928673) , + (0.809524, 0.934353, 0.934353) , + (0.825397, 0.939999, 0.939999) , + (0.84127, 0.945611, 0.945611) , + (0.857143, 0.95119, 0.95119) , + (0.873016, 0.956736, 0.956736) , + (0.888889, 0.96225, 0.96225) , + (0.904762, 0.967733, 0.967733) , + (0.920635, 0.973185, 0.973185) , + (0.936508, 0.978607, 0.978607) , + (0.952381, 0.983999, 0.983999) , + (0.968254, 0.989361, 0.989361) , + (0.984127, 0.994695, 0.994695) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 0.0, 0.0) , + (0.015873, 0.102869, 0.102869) , + (0.031746, 0.145479, 0.145479) , + (0.047619, 0.178174, 0.178174) , + (0.063492, 0.205738, 0.205738) , + (0.079365, 0.230022, 0.230022) , + (0.095238, 0.251976, 0.251976) , + (0.111111, 0.272166, 0.272166) , + (0.126984, 0.290957, 0.290957) , + (0.142857, 0.308607, 0.308607) , + (0.15873, 0.3253, 0.3253) , + (0.174603, 0.341178, 0.341178) , + (0.190476, 0.356348, 0.356348) , + (0.206349, 0.370899, 0.370899) , + (0.222222, 0.3849, 0.3849) , + (0.238095, 0.39841, 0.39841) , + (0.253968, 0.411476, 0.411476) , + (0.269841, 0.424139, 0.424139) , + (0.285714, 0.436436, 0.436436) , + (0.301587, 0.448395, 0.448395) , + (0.31746, 0.460044, 0.460044) , + (0.333333, 0.471405, 0.471405) , + (0.349206, 0.482498, 0.482498) , + (0.365079, 0.493342, 0.493342) , + (0.380952, 0.503953, 0.503953) , + (0.396825, 0.514344, 0.514344) , + (0.412698, 0.524531, 0.524531) , + (0.428571, 0.534522, 0.534522) , + (0.444444, 0.544331, 0.544331) , + (0.460317, 0.553966, 0.553966) , + (0.47619, 0.563436, 0.563436) , + (0.492063, 0.57275, 0.57275) , + (0.507937, 0.581914, 0.581914) , + (0.52381, 0.590937, 0.590937) , + (0.539683, 0.599824, 0.599824) , + (0.555556, 0.608581, 0.608581) , + (0.571429, 0.617213, 0.617213) , + (0.587302, 0.625727, 0.625727) , + (0.603175, 0.634126, 0.634126) , + (0.619048, 0.642416, 0.642416) , + (0.634921, 0.6506, 0.6506) , + (0.650794, 0.658682, 0.658682) , + (0.666667, 0.666667, 0.666667) , + (0.68254, 0.674556, 0.674556) , + (0.698413, 0.682355, 0.682355) , + (0.714286, 0.690066, 0.690066) , + (0.730159, 0.697691, 0.697691) , + (0.746032, 0.705234, 0.705234) , + (0.761905, 0.727166, 0.727166) , + (0.777778, 0.748455, 0.748455) , + (0.793651, 0.769156, 0.769156) , + (0.809524, 0.789314, 0.789314) , + (0.825397, 0.808969, 0.808969) , + (0.84127, 0.828159, 0.828159) , + (0.857143, 0.846913, 0.846913) , + (0.873016, 0.865261, 0.865261) , + (0.888889, 0.883229, 0.883229) , + (0.904762, 0.900837, 0.900837) , + (0.920635, 0.918109, 0.918109) , + (0.936508, 0.935061, 0.935061) , + (0.952381, 0.951711, 0.951711) , + (0.968254, 0.968075, 0.968075) , + (0.984127, 0.984167, 0.984167) , + (1.0, 1.0, 1.0) + } + ); + + +list_data seismic = list_data(new pen[] { + rgb (0.0, 0.0, 0.3) , + rgb (0.0, 0.0, 1.0) , + rgb (1.0, 1.0, 1.0) , + rgb (1.0, 0.0, 0.0) , + rgb (0.5, 0.0, 0.0) + }); + + +seg_data spring = seg_data( + new triple[] { // red + (0.0, 1.0, 1.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 1.0, 1.0) , + (1.0, 0.0, 0.0) + } + ); + + +seg_data summer = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // green + (0.0, 0.5, 0.5) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 0.4, 0.4) , + (1.0, 0.4, 0.4) + } + ); + + +list_data tab10 = list_data(new pen[] { + rgb (0.12156862745098039, 0.4666666666666667, 0.7058823529411765) , + rgb (1.0, 0.4980392156862745, 0.054901960784313725) , + rgb (0.17254901960784313, 0.6274509803921569, 0.17254901960784313) , + rgb (0.8392156862745098, 0.15294117647058825, 0.1568627450980392) , + rgb (0.5803921568627451, 0.403921568627451, 0.7411764705882353) , + rgb (0.5490196078431373, 0.33725490196078434, 0.29411764705882354) , + rgb (0.8901960784313725, 0.4666666666666667, 0.7607843137254902) , + rgb (0.4980392156862745, 0.4980392156862745, 0.4980392156862745) , + rgb (0.7372549019607844, 0.7411764705882353, 0.13333333333333333) , + rgb (0.09019607843137255, 0.7450980392156863, 0.8117647058823529) + }); + + +list_data tab20 = list_data(new pen[] { + rgb (0.12156862745098039, 0.4666666666666667, 0.7058823529411765) , + rgb (0.6823529411764706, 0.7803921568627451, 0.9098039215686274) , + rgb (1.0, 0.4980392156862745, 0.054901960784313725) , + rgb (1.0, 0.7333333333333333, 0.47058823529411764) , + rgb (0.17254901960784313, 0.6274509803921569, 0.17254901960784313) , + rgb (0.596078431372549, 0.8745098039215686, 0.5411764705882353) , + rgb (0.8392156862745098, 0.15294117647058825, 0.1568627450980392) , + rgb (1.0, 0.596078431372549, 0.5882352941176471) , + rgb (0.5803921568627451, 0.403921568627451, 0.7411764705882353) , + rgb (0.7725490196078432, 0.6901960784313725, 0.8352941176470589) , + rgb (0.5490196078431373, 0.33725490196078434, 0.29411764705882354) , + rgb (0.7686274509803922, 0.611764705882353, 0.5803921568627451) , + rgb (0.8901960784313725, 0.4666666666666667, 0.7607843137254902) , + rgb (0.9686274509803922, 0.7137254901960784, 0.8235294117647058) , + rgb (0.4980392156862745, 0.4980392156862745, 0.4980392156862745) , + rgb (0.7803921568627451, 0.7803921568627451, 0.7803921568627451) , + rgb (0.7372549019607844, 0.7411764705882353, 0.13333333333333333) , + rgb (0.8588235294117647, 0.8588235294117647, 0.5529411764705883) , + rgb (0.09019607843137255, 0.7450980392156863, 0.8117647058823529) , + rgb (0.6196078431372549, 0.8549019607843137, 0.8980392156862745) + }); + + +list_data tab20b = list_data(new pen[] { + rgb (0.2235294117647059, 0.23137254901960785, 0.4745098039215686) , + rgb (0.3215686274509804, 0.32941176470588235, 0.6392156862745098) , + rgb (0.4196078431372549, 0.43137254901960786, 0.8117647058823529) , + rgb (0.611764705882353, 0.6196078431372549, 0.8705882352941177) , + rgb (0.38823529411764707, 0.4745098039215686, 0.2235294117647059) , + rgb (0.5490196078431373, 0.6352941176470588, 0.3215686274509804) , + rgb (0.7098039215686275, 0.8117647058823529, 0.4196078431372549) , + rgb (0.807843137254902, 0.8588235294117647, 0.611764705882353) , + rgb (0.5490196078431373, 0.42745098039215684, 0.19215686274509805) , + rgb (0.7411764705882353, 0.6196078431372549, 0.2235294117647059) , + rgb (0.9058823529411765, 0.7294117647058823, 0.3215686274509804) , + rgb (0.9058823529411765, 0.796078431372549, 0.5803921568627451) , + rgb (0.5176470588235295, 0.23529411764705882, 0.2235294117647059) , + rgb (0.6784313725490196, 0.28627450980392155, 0.2901960784313726) , + rgb (0.8392156862745098, 0.3803921568627451, 0.4196078431372549) , + rgb (0.9058823529411765, 0.5882352941176471, 0.611764705882353) , + rgb (0.4823529411764706, 0.2549019607843137, 0.45098039215686275) , + rgb (0.6470588235294118, 0.3176470588235294, 0.5803921568627451) , + rgb (0.807843137254902, 0.42745098039215684, 0.7411764705882353) , + rgb (0.8705882352941177, 0.6196078431372549, 0.8392156862745098) + }); + + +list_data tab20c = list_data(new pen[] { + rgb (0.19215686274509805, 0.5098039215686274, 0.7411764705882353) , + rgb (0.4196078431372549, 0.6823529411764706, 0.8392156862745098) , + rgb (0.6196078431372549, 0.792156862745098, 0.8823529411764706) , + rgb (0.7764705882352941, 0.8588235294117647, 0.9372549019607843) , + rgb (0.9019607843137255, 0.3333333333333333, 0.050980392156862744) , + rgb (0.9921568627450981, 0.5529411764705883, 0.23529411764705882) , + rgb (0.9921568627450981, 0.6823529411764706, 0.4196078431372549) , + rgb (0.9921568627450981, 0.8156862745098039, 0.6352941176470588) , + rgb (0.19215686274509805, 0.6392156862745098, 0.32941176470588235) , + rgb (0.4549019607843137, 0.7686274509803922, 0.4627450980392157) , + rgb (0.6313725490196078, 0.8509803921568627, 0.6078431372549019) , + rgb (0.7803921568627451, 0.9137254901960784, 0.7529411764705882) , + rgb (0.4588235294117647, 0.4196078431372549, 0.6941176470588235) , + rgb (0.6196078431372549, 0.6039215686274509, 0.7843137254901961) , + rgb (0.7372549019607844, 0.7411764705882353, 0.8627450980392157) , + rgb (0.8549019607843137, 0.8549019607843137, 0.9215686274509803) , + rgb (0.38823529411764707, 0.38823529411764707, 0.38823529411764707) , + rgb (0.5882352941176471, 0.5882352941176471, 0.5882352941176471) , + rgb (0.7411764705882353, 0.7411764705882353, 0.7411764705882353) , + rgb (0.8509803921568627, 0.8509803921568627, 0.8509803921568627) + }); + + +seg_data winter = seg_data( + new triple[] { // red + (0.0, 0.0, 0.0) , + (1.0, 0.0, 0.0) + }, + new triple[] { // green + (0.0, 0.0, 0.0) , + (1.0, 1.0, 1.0) + }, + new triple[] { // blue + (0.0, 1.0, 1.0) , + (1.0, 0.5, 0.5) + } + ); + + +seg_data wistia = seg_data( + new triple[] { // red + (0.0, 0.8941176470588236, 0.8941176470588236) , + (0.25, 1.0, 1.0) , + (0.5, 1.0, 1.0) , + (0.75, 1.0, 1.0) , + (1.0, 0.9882352941176471, 0.9882352941176471) + }, + new triple[] { // green + (0.0, 1.0, 1.0) , + (0.25, 0.9098039215686274, 0.9098039215686274) , + (0.5, 0.7411764705882353, 0.7411764705882353) , + (0.75, 0.6274509803921569, 0.6274509803921569) , + (1.0, 0.4980392156862745, 0.4980392156862745) + }, + new triple[] { // blue + (0.0, 0.47843137254901963, 0.47843137254901963) , + (0.25, 0.10196078431372549, 0.10196078431372549) , + (0.5, 0.0, 0.0) , + (0.75, 0.0, 0.0) , + (1.0, 0.0, 0.0) + } + ); + + +list_data cividis = list_data(new pen[] { + rgb (0.0, 0.135112, 0.304751) , + rgb (0.0, 0.138068, 0.311105) , + rgb (0.0, 0.141013, 0.317579) , + rgb (0.0, 0.143951, 0.323982) , + rgb (0.0, 0.146877, 0.330479) , + rgb (0.0, 0.149791, 0.337065) , + rgb (0.0, 0.152673, 0.343704) , + rgb (0.0, 0.155377, 0.3505) , + rgb (0.0, 0.157932, 0.357521) , + rgb (0.0, 0.160495, 0.364534) , + rgb (0.0, 0.163058, 0.371608) , + rgb (0.0, 0.165621, 0.378769) , + rgb (0.0, 0.168204, 0.385902) , + rgb (0.0, 0.1708, 0.3931) , + rgb (0.0, 0.17342, 0.400353) , + rgb (0.0, 0.176082, 0.407577) , + rgb (0.0, 0.178802, 0.414764) , + rgb (0.0, 0.18161, 0.421859) , + rgb (0.0, 0.18455, 0.428802) , + rgb (0.0, 0.186915, 0.435532) , + rgb (0.0, 0.188769, 0.439563) , + rgb (0.0, 0.19095, 0.441085) , + rgb (0.0, 0.193366, 0.441561) , + rgb (0.003602, 0.195911, 0.441564) , + rgb (0.017852, 0.198528, 0.441248) , + rgb (0.03211, 0.201199, 0.440785) , + rgb (0.046205, 0.203903, 0.440196) , + rgb (0.058378, 0.206629, 0.439531) , + rgb (0.068968, 0.209372, 0.438863) , + rgb (0.078624, 0.212122, 0.438105) , + rgb (0.087465, 0.214879, 0.437342) , + rgb (0.095645, 0.217643, 0.436593) , + rgb (0.103401, 0.220406, 0.43579) , + rgb (0.110658, 0.22317, 0.435067) , + rgb (0.117612, 0.225935, 0.434308) , + rgb (0.124291, 0.228697, 0.433547) , + rgb (0.130669, 0.231458, 0.43284) , + rgb (0.13683, 0.234216, 0.432148) , + rgb (0.142852, 0.236972, 0.431404) , + rgb (0.148638, 0.239724, 0.430752) , + rgb (0.154261, 0.242475, 0.43012) , + rgb (0.159733, 0.245221, 0.429528) , + rgb (0.165113, 0.247965, 0.428908) , + rgb (0.170362, 0.250707, 0.428325) , + rgb (0.17549, 0.253444, 0.42779) , + rgb (0.180503, 0.25618, 0.427299) , + rgb (0.185453, 0.258914, 0.426788) , + rgb (0.190303, 0.261644, 0.426329) , + rgb (0.195057, 0.264372, 0.425924) , + rgb (0.199764, 0.267099, 0.425497) , + rgb (0.204385, 0.269823, 0.425126) , + rgb (0.208926, 0.272546, 0.424809) , + rgb (0.213431, 0.275266, 0.42448) , + rgb (0.217863, 0.277985, 0.424206) , + rgb (0.222264, 0.280702, 0.423914) , + rgb (0.226598, 0.283419, 0.423678) , + rgb (0.230871, 0.286134, 0.423498) , + rgb (0.23512, 0.288848, 0.423304) , + rgb (0.239312, 0.291562, 0.423167) , + rgb (0.243485, 0.294274, 0.423014) , + rgb (0.247605, 0.296986, 0.422917) , + rgb (0.251675, 0.299698, 0.422873) , + rgb (0.255731, 0.302409, 0.422814) , + rgb (0.25974, 0.30512, 0.42281) , + rgb (0.263738, 0.307831, 0.422789) , + rgb (0.267693, 0.310542, 0.422821) , + rgb (0.271639, 0.313253, 0.422837) , + rgb (0.275513, 0.315965, 0.422979) , + rgb (0.279411, 0.318677, 0.423031) , + rgb (0.28324, 0.32139, 0.423211) , + rgb (0.287065, 0.324103, 0.423373) , + rgb (0.290884, 0.326816, 0.423517) , + rgb (0.294669, 0.329531, 0.423716) , + rgb (0.298421, 0.332247, 0.423973) , + rgb (0.302169, 0.334963, 0.424213) , + rgb (0.305886, 0.337681, 0.424512) , + rgb (0.309601, 0.340399, 0.42479) , + rgb (0.313287, 0.34312, 0.42512) , + rgb (0.316941, 0.345842, 0.425512) , + rgb (0.320595, 0.348565, 0.425889) , + rgb (0.32425, 0.351289, 0.42625) , + rgb (0.327875, 0.354016, 0.42667) , + rgb (0.331474, 0.356744, 0.427144) , + rgb (0.335073, 0.359474, 0.427605) , + rgb (0.338673, 0.362206, 0.428053) , + rgb (0.342246, 0.364939, 0.428559) , + rgb (0.345793, 0.367676, 0.429127) , + rgb (0.349341, 0.370414, 0.429685) , + rgb (0.352892, 0.373153, 0.430226) , + rgb (0.356418, 0.375896, 0.430823) , + rgb (0.359916, 0.378641, 0.431501) , + rgb (0.363446, 0.381388, 0.432075) , + rgb (0.366923, 0.384139, 0.432796) , + rgb (0.37043, 0.38689, 0.433428) , + rgb (0.373884, 0.389646, 0.434209) , + rgb (0.377371, 0.392404, 0.43489) , + rgb (0.38083, 0.395164, 0.435653) , + rgb (0.384268, 0.397928, 0.436475) , + rgb (0.387705, 0.400694, 0.437305) , + rgb (0.391151, 0.403464, 0.438096) , + rgb (0.394568, 0.406236, 0.438986) , + rgb (0.397991, 0.409011, 0.439848) , + rgb (0.401418, 0.41179, 0.440708) , + rgb (0.40482, 0.414572, 0.441642) , + rgb (0.408226, 0.417357, 0.44257) , + rgb (0.411607, 0.420145, 0.443577) , + rgb (0.414992, 0.422937, 0.444578) , + rgb (0.418383, 0.425733, 0.44556) , + rgb (0.421748, 0.428531, 0.44664) , + rgb (0.42512, 0.431334, 0.447692) , + rgb (0.428462, 0.43414, 0.448864) , + rgb (0.431817, 0.43695, 0.449982) , + rgb (0.435168, 0.439763, 0.451134) , + rgb (0.438504, 0.44258, 0.452341) , + rgb (0.44181, 0.445402, 0.453659) , + rgb (0.445148, 0.448226, 0.454885) , + rgb (0.448447, 0.451053, 0.456264) , + rgb (0.451759, 0.453887, 0.457582) , + rgb (0.455072, 0.456718, 0.458976) , + rgb (0.458366, 0.459552, 0.460457) , + rgb (0.461616, 0.462405, 0.461969) , + rgb (0.464947, 0.465241, 0.463395) , + rgb (0.468254, 0.468083, 0.464908) , + rgb (0.471501, 0.47096, 0.466357) , + rgb (0.474812, 0.473832, 0.467681) , + rgb (0.478186, 0.476699, 0.468845) , + rgb (0.481622, 0.479573, 0.469767) , + rgb (0.485141, 0.482451, 0.470384) , + rgb (0.488697, 0.485318, 0.471008) , + rgb (0.492278, 0.488198, 0.471453) , + rgb (0.495913, 0.491076, 0.471751) , + rgb (0.499552, 0.49396, 0.472032) , + rgb (0.503185, 0.496851, 0.472305) , + rgb (0.506866, 0.499743, 0.472432) , + rgb (0.51054, 0.502643, 0.47255) , + rgb (0.514226, 0.505546, 0.47264) , + rgb (0.51792, 0.508454, 0.472707) , + rgb (0.521643, 0.511367, 0.472639) , + rgb (0.525348, 0.514285, 0.47266) , + rgb (0.529086, 0.517207, 0.472543) , + rgb (0.532829, 0.520135, 0.472401) , + rgb (0.536553, 0.523067, 0.472352) , + rgb (0.540307, 0.526005, 0.472163) , + rgb (0.544069, 0.528948, 0.471947) , + rgb (0.54784, 0.531895, 0.471704) , + rgb (0.551612, 0.534849, 0.471439) , + rgb (0.555393, 0.537807, 0.471147) , + rgb (0.559181, 0.540771, 0.470829) , + rgb (0.562972, 0.543741, 0.470488) , + rgb (0.566802, 0.546715, 0.469988) , + rgb (0.570607, 0.549695, 0.469593) , + rgb (0.574417, 0.552682, 0.469172) , + rgb (0.578236, 0.555673, 0.468724) , + rgb (0.582087, 0.55867, 0.468118) , + rgb (0.585916, 0.561674, 0.467618) , + rgb (0.589753, 0.564682, 0.46709) , + rgb (0.593622, 0.567697, 0.466401) , + rgb (0.597469, 0.570718, 0.465821) , + rgb (0.601354, 0.573743, 0.465074) , + rgb (0.605211, 0.576777, 0.464441) , + rgb (0.609105, 0.579816, 0.463638) , + rgb (0.612977, 0.582861, 0.46295) , + rgb (0.616852, 0.585913, 0.462237) , + rgb (0.620765, 0.58897, 0.461351) , + rgb (0.624654, 0.592034, 0.460583) , + rgb (0.628576, 0.595104, 0.459641) , + rgb (0.632506, 0.59818, 0.458668) , + rgb (0.636412, 0.601264, 0.457818) , + rgb (0.640352, 0.604354, 0.456791) , + rgb (0.64427, 0.60745, 0.455886) , + rgb (0.648222, 0.610553, 0.454801) , + rgb (0.652178, 0.613664, 0.453689) , + rgb (0.656114, 0.61678, 0.452702) , + rgb (0.660082, 0.619904, 0.451534) , + rgb (0.664055, 0.623034, 0.450338) , + rgb (0.668008, 0.626171, 0.44927) , + rgb (0.671991, 0.629316, 0.448018) , + rgb (0.675981, 0.632468, 0.446736) , + rgb (0.679979, 0.635626, 0.445424) , + rgb (0.68395, 0.638793, 0.444251) , + rgb (0.687957, 0.641966, 0.442886) , + rgb (0.691971, 0.645145, 0.441491) , + rgb (0.695985, 0.648334, 0.440072) , + rgb (0.700008, 0.651529, 0.438624) , + rgb (0.704037, 0.654731, 0.437147) , + rgb (0.708067, 0.657942, 0.435647) , + rgb (0.712105, 0.66116, 0.434117) , + rgb (0.716177, 0.664384, 0.432386) , + rgb (0.720222, 0.667618, 0.430805) , + rgb (0.724274, 0.670859, 0.429194) , + rgb (0.728334, 0.674107, 0.427554) , + rgb (0.732422, 0.677364, 0.425717) , + rgb (0.736488, 0.680629, 0.424028) , + rgb (0.740589, 0.6839, 0.422131) , + rgb (0.744664, 0.687181, 0.420393) , + rgb (0.748772, 0.69047, 0.418448) , + rgb (0.752886, 0.693766, 0.416472) , + rgb (0.756975, 0.697071, 0.414659) , + rgb (0.761096, 0.700384, 0.412638) , + rgb (0.765223, 0.703705, 0.410587) , + rgb (0.769353, 0.707035, 0.408516) , + rgb (0.773486, 0.710373, 0.406422) , + rgb (0.777651, 0.713719, 0.404112) , + rgb (0.781795, 0.717074, 0.401966) , + rgb (0.785965, 0.720438, 0.399613) , + rgb (0.790116, 0.72381, 0.397423) , + rgb (0.794298, 0.72719, 0.395016) , + rgb (0.79848, 0.73058, 0.392597) , + rgb (0.802667, 0.733978, 0.390153) , + rgb (0.806859, 0.737385, 0.387684) , + rgb (0.811054, 0.740801, 0.385198) , + rgb (0.815274, 0.744226, 0.382504) , + rgb (0.819499, 0.747659, 0.379785) , + rgb (0.823729, 0.751101, 0.377043) , + rgb (0.827959, 0.754553, 0.374292) , + rgb (0.832192, 0.758014, 0.371529) , + rgb (0.836429, 0.761483, 0.368747) , + rgb (0.840693, 0.764962, 0.365746) , + rgb (0.844957, 0.76845, 0.362741) , + rgb (0.849223, 0.771947, 0.359729) , + rgb (0.853515, 0.775454, 0.3565) , + rgb (0.857809, 0.778969, 0.353259) , + rgb (0.862105, 0.782494, 0.350011) , + rgb (0.866421, 0.786028, 0.346571) , + rgb (0.870717, 0.789572, 0.343333) , + rgb (0.875057, 0.793125, 0.339685) , + rgb (0.879378, 0.796687, 0.336241) , + rgb (0.88372, 0.800258, 0.332599) , + rgb (0.888081, 0.803839, 0.32877) , + rgb (0.89244, 0.80743, 0.324968) , + rgb (0.896818, 0.81103, 0.320982) , + rgb (0.901195, 0.814639, 0.317021) , + rgb (0.905589, 0.818257, 0.312889) , + rgb (0.91, 0.821885, 0.308594) , + rgb (0.914407, 0.825522, 0.304348) , + rgb (0.918828, 0.829168, 0.29996) , + rgb (0.923279, 0.832822, 0.295244) , + rgb (0.927724, 0.836486, 0.290611) , + rgb (0.93218, 0.840159, 0.28588) , + rgb (0.93666, 0.843841, 0.280876) , + rgb (0.941147, 0.84753, 0.275815) , + rgb (0.945654, 0.851228, 0.270532) , + rgb (0.950178, 0.854933, 0.265085) , + rgb (0.954725, 0.858646, 0.259365) , + rgb (0.959284, 0.862365, 0.253563) , + rgb (0.963872, 0.866089, 0.247445) , + rgb (0.968469, 0.869819, 0.24131) , + rgb (0.973114, 0.87355, 0.234677) , + rgb (0.97778, 0.877281, 0.227954) , + rgb (0.982497, 0.881008, 0.220878) , + rgb (0.987293, 0.884718, 0.213336) , + rgb (0.992218, 0.888385, 0.205468) , + rgb (0.994847, 0.892954, 0.203445) , + rgb (0.995249, 0.898384, 0.207561) , + rgb (0.995503, 0.903866, 0.21237) , + rgb (0.995737, 0.909344, 0.217772) + }); + + +list_data inferno = list_data(new pen[] { + rgb (0.001462, 0.000466, 0.013866) , + rgb (0.002267, 0.00127, 0.01857) , + rgb (0.003299, 0.002249, 0.024239) , + rgb (0.004547, 0.003392, 0.030909) , + rgb (0.006006, 0.004692, 0.038558) , + rgb (0.007676, 0.006136, 0.046836) , + rgb (0.009561, 0.007713, 0.055143) , + rgb (0.011663, 0.009417, 0.06346) , + rgb (0.013995, 0.011225, 0.071862) , + rgb (0.016561, 0.013136, 0.080282) , + rgb (0.019373, 0.015133, 0.088767) , + rgb (0.022447, 0.017199, 0.097327) , + rgb (0.025793, 0.019331, 0.10593) , + rgb (0.029432, 0.021503, 0.114621) , + rgb (0.033385, 0.023702, 0.123397) , + rgb (0.037668, 0.025921, 0.132232) , + rgb (0.042253, 0.028139, 0.141141) , + rgb (0.046915, 0.030324, 0.150164) , + rgb (0.051644, 0.032474, 0.159254) , + rgb (0.056449, 0.034569, 0.168414) , + rgb (0.06134, 0.03659, 0.177642) , + rgb (0.066331, 0.038504, 0.186962) , + rgb (0.071429, 0.040294, 0.196354) , + rgb (0.076637, 0.041905, 0.205799) , + rgb (0.081962, 0.043328, 0.215289) , + rgb (0.087411, 0.044556, 0.224813) , + rgb (0.09299, 0.045583, 0.234358) , + rgb (0.098702, 0.046402, 0.243904) , + rgb (0.104551, 0.047008, 0.25343) , + rgb (0.110536, 0.047399, 0.262912) , + rgb (0.116656, 0.047574, 0.272321) , + rgb (0.122908, 0.047536, 0.281624) , + rgb (0.129285, 0.047293, 0.290788) , + rgb (0.135778, 0.046856, 0.299776) , + rgb (0.142378, 0.046242, 0.308553) , + rgb (0.149073, 0.045468, 0.317085) , + rgb (0.15585, 0.044559, 0.325338) , + rgb (0.162689, 0.043554, 0.333277) , + rgb (0.169575, 0.042489, 0.340874) , + rgb (0.176493, 0.041402, 0.348111) , + rgb (0.183429, 0.040329, 0.354971) , + rgb (0.190367, 0.039309, 0.361447) , + rgb (0.197297, 0.0384, 0.367535) , + rgb (0.204209, 0.037632, 0.373238) , + rgb (0.211095, 0.03703, 0.378563) , + rgb (0.217949, 0.036615, 0.383522) , + rgb (0.224763, 0.036405, 0.388129) , + rgb (0.231538, 0.036405, 0.3924) , + rgb (0.238273, 0.036621, 0.396353) , + rgb (0.244967, 0.037055, 0.400007) , + rgb (0.25162, 0.037705, 0.403378) , + rgb (0.258234, 0.038571, 0.406485) , + rgb (0.26481, 0.039647, 0.409345) , + rgb (0.271347, 0.040922, 0.411976) , + rgb (0.27785, 0.042353, 0.414392) , + rgb (0.284321, 0.043933, 0.416608) , + rgb (0.290763, 0.045644, 0.418637) , + rgb (0.297178, 0.04747, 0.420491) , + rgb (0.303568, 0.049396, 0.422182) , + rgb (0.309935, 0.051407, 0.423721) , + rgb (0.316282, 0.05349, 0.425116) , + rgb (0.32261, 0.055634, 0.426377) , + rgb (0.328921, 0.057827, 0.427511) , + rgb (0.335217, 0.06006, 0.428524) , + rgb (0.3415, 0.062325, 0.429425) , + rgb (0.347771, 0.064616, 0.430217) , + rgb (0.354032, 0.066925, 0.430906) , + rgb (0.360284, 0.069247, 0.431497) , + rgb (0.366529, 0.071579, 0.431994) , + rgb (0.372768, 0.073915, 0.4324) , + rgb (0.379001, 0.076253, 0.432719) , + rgb (0.385228, 0.078591, 0.432955) , + rgb (0.391453, 0.080927, 0.433109) , + rgb (0.397674, 0.083257, 0.433183) , + rgb (0.403894, 0.08558, 0.433179) , + rgb (0.410113, 0.087896, 0.433098) , + rgb (0.416331, 0.090203, 0.432943) , + rgb (0.422549, 0.092501, 0.432714) , + rgb (0.428768, 0.09479, 0.432412) , + rgb (0.434987, 0.097069, 0.432039) , + rgb (0.441207, 0.099338, 0.431594) , + rgb (0.447428, 0.101597, 0.43108) , + rgb (0.453651, 0.103848, 0.430498) , + rgb (0.459875, 0.106089, 0.429846) , + rgb (0.4661, 0.108322, 0.429125) , + rgb (0.472328, 0.110547, 0.428334) , + rgb (0.478558, 0.112764, 0.427475) , + rgb (0.484789, 0.114974, 0.426548) , + rgb (0.491022, 0.117179, 0.425552) , + rgb (0.497257, 0.119379, 0.424488) , + rgb (0.503493, 0.121575, 0.423356) , + rgb (0.50973, 0.123769, 0.422156) , + rgb (0.515967, 0.12596, 0.420887) , + rgb (0.522206, 0.12815, 0.419549) , + rgb (0.528444, 0.130341, 0.418142) , + rgb (0.534683, 0.132534, 0.416667) , + rgb (0.54092, 0.134729, 0.415123) , + rgb (0.547157, 0.136929, 0.413511) , + rgb (0.553392, 0.139134, 0.411829) , + rgb (0.559624, 0.141346, 0.410078) , + rgb (0.565854, 0.143567, 0.408258) , + rgb (0.572081, 0.145797, 0.406369) , + rgb (0.578304, 0.148039, 0.404411) , + rgb (0.584521, 0.150294, 0.402385) , + rgb (0.590734, 0.152563, 0.40029) , + rgb (0.59694, 0.154848, 0.398125) , + rgb (0.603139, 0.157151, 0.395891) , + rgb (0.60933, 0.159474, 0.393589) , + rgb (0.615513, 0.161817, 0.391219) , + rgb (0.621685, 0.164184, 0.388781) , + rgb (0.627847, 0.166575, 0.386276) , + rgb (0.633998, 0.168992, 0.383704) , + rgb (0.640135, 0.171438, 0.381065) , + rgb (0.64626, 0.173914, 0.378359) , + rgb (0.652369, 0.176421, 0.375586) , + rgb (0.658463, 0.178962, 0.372748) , + rgb (0.66454, 0.181539, 0.369846) , + rgb (0.670599, 0.184153, 0.366879) , + rgb (0.676638, 0.186807, 0.363849) , + rgb (0.682656, 0.189501, 0.360757) , + rgb (0.688653, 0.192239, 0.357603) , + rgb (0.694627, 0.195021, 0.354388) , + rgb (0.700576, 0.197851, 0.351113) , + rgb (0.7065, 0.200728, 0.347777) , + rgb (0.712396, 0.203656, 0.344383) , + rgb (0.718264, 0.206636, 0.340931) , + rgb (0.724103, 0.20967, 0.337424) , + rgb (0.729909, 0.212759, 0.333861) , + rgb (0.735683, 0.215906, 0.330245) , + rgb (0.741423, 0.219112, 0.326576) , + rgb (0.747127, 0.222378, 0.322856) , + rgb (0.752794, 0.225706, 0.319085) , + rgb (0.758422, 0.229097, 0.315266) , + rgb (0.76401, 0.232554, 0.311399) , + rgb (0.769556, 0.236077, 0.307485) , + rgb (0.775059, 0.239667, 0.303526) , + rgb (0.780517, 0.243327, 0.299523) , + rgb (0.785929, 0.247056, 0.295477) , + rgb (0.791293, 0.250856, 0.29139) , + rgb (0.796607, 0.254728, 0.287264) , + rgb (0.801871, 0.258674, 0.283099) , + rgb (0.807082, 0.262692, 0.278898) , + rgb (0.812239, 0.266786, 0.274661) , + rgb (0.817341, 0.270954, 0.27039) , + rgb (0.822386, 0.275197, 0.266085) , + rgb (0.827372, 0.279517, 0.26175) , + rgb (0.832299, 0.283913, 0.257383) , + rgb (0.837165, 0.288385, 0.252988) , + rgb (0.841969, 0.292933, 0.248564) , + rgb (0.846709, 0.297559, 0.244113) , + rgb (0.851384, 0.30226, 0.239636) , + rgb (0.855992, 0.307038, 0.235133) , + rgb (0.860533, 0.311892, 0.230606) , + rgb (0.865006, 0.316822, 0.226055) , + rgb (0.869409, 0.321827, 0.221482) , + rgb (0.873741, 0.326906, 0.216886) , + rgb (0.878001, 0.33206, 0.212268) , + rgb (0.882188, 0.337287, 0.207628) , + rgb (0.886302, 0.342586, 0.202968) , + rgb (0.890341, 0.347957, 0.198286) , + rgb (0.894305, 0.353399, 0.193584) , + rgb (0.898192, 0.358911, 0.18886) , + rgb (0.902003, 0.364492, 0.184116) , + rgb (0.905735, 0.37014, 0.17935) , + rgb (0.90939, 0.375856, 0.174563) , + rgb (0.912966, 0.381636, 0.169755) , + rgb (0.916462, 0.387481, 0.164924) , + rgb (0.919879, 0.393389, 0.16007) , + rgb (0.923215, 0.399359, 0.155193) , + rgb (0.92647, 0.405389, 0.150292) , + rgb (0.929644, 0.411479, 0.145367) , + rgb (0.932737, 0.417627, 0.140417) , + rgb (0.935747, 0.423831, 0.13544) , + rgb (0.938675, 0.430091, 0.130438) , + rgb (0.941521, 0.436405, 0.125409) , + rgb (0.944285, 0.442772, 0.120354) , + rgb (0.946965, 0.449191, 0.115272) , + rgb (0.949562, 0.45566, 0.110164) , + rgb (0.952075, 0.462178, 0.105031) , + rgb (0.954506, 0.468744, 0.099874) , + rgb (0.956852, 0.475356, 0.094695) , + rgb (0.959114, 0.482014, 0.089499) , + rgb (0.961293, 0.488716, 0.084289) , + rgb (0.963387, 0.495462, 0.079073) , + rgb (0.965397, 0.502249, 0.073859) , + rgb (0.967322, 0.509078, 0.068659) , + rgb (0.969163, 0.515946, 0.063488) , + rgb (0.970919, 0.522853, 0.058367) , + rgb (0.97259, 0.529798, 0.053324) , + rgb (0.974176, 0.53678, 0.048392) , + rgb (0.975677, 0.543798, 0.043618) , + rgb (0.977092, 0.55085, 0.03905) , + rgb (0.978422, 0.557937, 0.034931) , + rgb (0.979666, 0.565057, 0.031409) , + rgb (0.980824, 0.572209, 0.028508) , + rgb (0.981895, 0.579392, 0.02625) , + rgb (0.982881, 0.586606, 0.024661) , + rgb (0.983779, 0.593849, 0.02377) , + rgb (0.984591, 0.601122, 0.023606) , + rgb (0.985315, 0.608422, 0.024202) , + rgb (0.985952, 0.61575, 0.025592) , + rgb (0.986502, 0.623105, 0.027814) , + rgb (0.986964, 0.630485, 0.030908) , + rgb (0.987337, 0.63789, 0.034916) , + rgb (0.987622, 0.64532, 0.039886) , + rgb (0.987819, 0.652773, 0.045581) , + rgb (0.987926, 0.66025, 0.05175) , + rgb (0.987945, 0.667748, 0.058329) , + rgb (0.987874, 0.675267, 0.065257) , + rgb (0.987714, 0.682807, 0.072489) , + rgb (0.987464, 0.690366, 0.07999) , + rgb (0.987124, 0.697944, 0.087731) , + rgb (0.986694, 0.70554, 0.095694) , + rgb (0.986175, 0.713153, 0.103863) , + rgb (0.985566, 0.720782, 0.112229) , + rgb (0.984865, 0.728427, 0.120785) , + rgb (0.984075, 0.736087, 0.129527) , + rgb (0.983196, 0.743758, 0.138453) , + rgb (0.982228, 0.751442, 0.147565) , + rgb (0.981173, 0.759135, 0.156863) , + rgb (0.980032, 0.766837, 0.166353) , + rgb (0.978806, 0.774545, 0.176037) , + rgb (0.977497, 0.782258, 0.185923) , + rgb (0.976108, 0.789974, 0.196018) , + rgb (0.974638, 0.797692, 0.206332) , + rgb (0.973088, 0.805409, 0.216877) , + rgb (0.971468, 0.813122, 0.227658) , + rgb (0.969783, 0.820825, 0.238686) , + rgb (0.968041, 0.828515, 0.249972) , + rgb (0.966243, 0.836191, 0.261534) , + rgb (0.964394, 0.843848, 0.273391) , + rgb (0.962517, 0.851476, 0.285546) , + rgb (0.960626, 0.859069, 0.29801) , + rgb (0.95872, 0.866624, 0.31082) , + rgb (0.956834, 0.874129, 0.323974) , + rgb (0.954997, 0.881569, 0.337475) , + rgb (0.953215, 0.888942, 0.351369) , + rgb (0.951546, 0.896226, 0.365627) , + rgb (0.950018, 0.903409, 0.380271) , + rgb (0.948683, 0.910473, 0.395289) , + rgb (0.947594, 0.917399, 0.410665) , + rgb (0.946809, 0.924168, 0.426373) , + rgb (0.946392, 0.930761, 0.442367) , + rgb (0.946403, 0.937159, 0.458592) , + rgb (0.946903, 0.943348, 0.47497) , + rgb (0.947937, 0.949318, 0.491426) , + rgb (0.949545, 0.955063, 0.50786) , + rgb (0.95174, 0.960587, 0.524203) , + rgb (0.954529, 0.965896, 0.540361) , + rgb (0.957896, 0.971003, 0.556275) , + rgb (0.961812, 0.975924, 0.571925) , + rgb (0.966249, 0.980678, 0.587206) , + rgb (0.971162, 0.985282, 0.602154) , + rgb (0.976511, 0.989753, 0.61676) , + rgb (0.982257, 0.994109, 0.631017) , + rgb (0.988362, 0.998364, 0.644924) + }); + + +list_data magma = list_data(new pen[] { + rgb (0.001462, 0.000466, 0.013866) , + rgb (0.002258, 0.001295, 0.018331) , + rgb (0.003279, 0.002305, 0.023708) , + rgb (0.004512, 0.00349, 0.029965) , + rgb (0.00595, 0.004843, 0.03713) , + rgb (0.007588, 0.006356, 0.044973) , + rgb (0.009426, 0.008022, 0.052844) , + rgb (0.011465, 0.009828, 0.06075) , + rgb (0.013708, 0.011771, 0.068667) , + rgb (0.016156, 0.01384, 0.076603) , + rgb (0.018815, 0.016026, 0.084584) , + rgb (0.021692, 0.01832, 0.09261) , + rgb (0.024792, 0.020715, 0.100676) , + rgb (0.028123, 0.023201, 0.108787) , + rgb (0.031696, 0.025765, 0.116965) , + rgb (0.03552, 0.028397, 0.125209) , + rgb (0.039608, 0.03109, 0.133515) , + rgb (0.04383, 0.03383, 0.141886) , + rgb (0.048062, 0.036607, 0.150327) , + rgb (0.05232, 0.039407, 0.158841) , + rgb (0.056615, 0.04216, 0.167446) , + rgb (0.060949, 0.044794, 0.176129) , + rgb (0.06533, 0.047318, 0.184892) , + rgb (0.069764, 0.049726, 0.193735) , + rgb (0.074257, 0.052017, 0.20266) , + rgb (0.078815, 0.054184, 0.211667) , + rgb (0.083446, 0.056225, 0.220755) , + rgb (0.088155, 0.058133, 0.229922) , + rgb (0.092949, 0.059904, 0.239164) , + rgb (0.097833, 0.061531, 0.248477) , + rgb (0.102815, 0.06301, 0.257854) , + rgb (0.107899, 0.064335, 0.267289) , + rgb (0.113094, 0.065492, 0.276784) , + rgb (0.118405, 0.066479, 0.286321) , + rgb (0.123833, 0.067295, 0.295879) , + rgb (0.12938, 0.067935, 0.305443) , + rgb (0.135053, 0.068391, 0.315) , + rgb (0.140858, 0.068654, 0.324538) , + rgb (0.146785, 0.068738, 0.334011) , + rgb (0.152839, 0.068637, 0.343404) , + rgb (0.159018, 0.068354, 0.352688) , + rgb (0.165308, 0.067911, 0.361816) , + rgb (0.171713, 0.067305, 0.370771) , + rgb (0.178212, 0.066576, 0.379497) , + rgb (0.184801, 0.065732, 0.387973) , + rgb (0.19146, 0.064818, 0.396152) , + rgb (0.198177, 0.063862, 0.404009) , + rgb (0.204935, 0.062907, 0.411514) , + rgb (0.211718, 0.061992, 0.418647) , + rgb (0.218512, 0.061158, 0.425392) , + rgb (0.225302, 0.060445, 0.431742) , + rgb (0.232077, 0.059889, 0.437695) , + rgb (0.238826, 0.059517, 0.443256) , + rgb (0.245543, 0.059352, 0.448436) , + rgb (0.25222, 0.059415, 0.453248) , + rgb (0.258857, 0.059706, 0.45771) , + rgb (0.265447, 0.060237, 0.46184) , + rgb (0.271994, 0.060994, 0.46566) , + rgb (0.278493, 0.061978, 0.46919) , + rgb (0.284951, 0.063168, 0.472451) , + rgb (0.291366, 0.064553, 0.475462) , + rgb (0.29774, 0.066117, 0.478243) , + rgb (0.304081, 0.067835, 0.480812) , + rgb (0.310382, 0.069702, 0.483186) , + rgb (0.316654, 0.07169, 0.48538) , + rgb (0.322899, 0.073782, 0.487408) , + rgb (0.329114, 0.075972, 0.489287) , + rgb (0.335308, 0.078236, 0.491024) , + rgb (0.341482, 0.080564, 0.492631) , + rgb (0.347636, 0.082946, 0.494121) , + rgb (0.353773, 0.085373, 0.495501) , + rgb (0.359898, 0.087831, 0.496778) , + rgb (0.366012, 0.090314, 0.49796) , + rgb (0.372116, 0.092816, 0.499053) , + rgb (0.378211, 0.095332, 0.500067) , + rgb (0.384299, 0.097855, 0.501002) , + rgb (0.390384, 0.100379, 0.501864) , + rgb (0.396467, 0.102902, 0.502658) , + rgb (0.402548, 0.10542, 0.503386) , + rgb (0.408629, 0.10793, 0.504052) , + rgb (0.414709, 0.110431, 0.504662) , + rgb (0.420791, 0.11292, 0.505215) , + rgb (0.426877, 0.115395, 0.505714) , + rgb (0.432967, 0.117855, 0.50616) , + rgb (0.439062, 0.120298, 0.506555) , + rgb (0.445163, 0.122724, 0.506901) , + rgb (0.451271, 0.125132, 0.507198) , + rgb (0.457386, 0.127522, 0.507448) , + rgb (0.463508, 0.129893, 0.507652) , + rgb (0.46964, 0.132245, 0.507809) , + rgb (0.47578, 0.134577, 0.507921) , + rgb (0.481929, 0.136891, 0.507989) , + rgb (0.488088, 0.139186, 0.508011) , + rgb (0.494258, 0.141462, 0.507988) , + rgb (0.500438, 0.143719, 0.50792) , + rgb (0.506629, 0.145958, 0.507806) , + rgb (0.512831, 0.148179, 0.507648) , + rgb (0.519045, 0.150383, 0.507443) , + rgb (0.52527, 0.152569, 0.507192) , + rgb (0.531507, 0.154739, 0.506895) , + rgb (0.537755, 0.156894, 0.506551) , + rgb (0.544015, 0.159033, 0.506159) , + rgb (0.550287, 0.161158, 0.505719) , + rgb (0.556571, 0.163269, 0.50523) , + rgb (0.562866, 0.165368, 0.504692) , + rgb (0.569172, 0.167454, 0.504105) , + rgb (0.57549, 0.16953, 0.503466) , + rgb (0.581819, 0.171596, 0.502777) , + rgb (0.588158, 0.173652, 0.502035) , + rgb (0.594508, 0.175701, 0.501241) , + rgb (0.600868, 0.177743, 0.500394) , + rgb (0.607238, 0.179779, 0.499492) , + rgb (0.613617, 0.181811, 0.498536) , + rgb (0.620005, 0.18384, 0.497524) , + rgb (0.626401, 0.185867, 0.496456) , + rgb (0.632805, 0.187893, 0.495332) , + rgb (0.639216, 0.189921, 0.49415) , + rgb (0.645633, 0.191952, 0.49291) , + rgb (0.652056, 0.193986, 0.491611) , + rgb (0.658483, 0.196027, 0.490253) , + rgb (0.664915, 0.198075, 0.488836) , + rgb (0.671349, 0.200133, 0.487358) , + rgb (0.677786, 0.202203, 0.485819) , + rgb (0.684224, 0.204286, 0.484219) , + rgb (0.690661, 0.206384, 0.482558) , + rgb (0.697098, 0.208501, 0.480835) , + rgb (0.703532, 0.210638, 0.479049) , + rgb (0.709962, 0.212797, 0.477201) , + rgb (0.716387, 0.214982, 0.47529) , + rgb (0.722805, 0.217194, 0.473316) , + rgb (0.729216, 0.219437, 0.471279) , + rgb (0.735616, 0.221713, 0.46918) , + rgb (0.742004, 0.224025, 0.467018) , + rgb (0.748378, 0.226377, 0.464794) , + rgb (0.754737, 0.228772, 0.462509) , + rgb (0.761077, 0.231214, 0.460162) , + rgb (0.767398, 0.233705, 0.457755) , + rgb (0.773695, 0.236249, 0.455289) , + rgb (0.779968, 0.238851, 0.452765) , + rgb (0.786212, 0.241514, 0.450184) , + rgb (0.792427, 0.244242, 0.447543) , + rgb (0.798608, 0.24704, 0.444848) , + rgb (0.804752, 0.249911, 0.442102) , + rgb (0.810855, 0.252861, 0.439305) , + rgb (0.816914, 0.255895, 0.436461) , + rgb (0.822926, 0.259016, 0.433573) , + rgb (0.828886, 0.262229, 0.430644) , + rgb (0.834791, 0.26554, 0.427671) , + rgb (0.840636, 0.268953, 0.424666) , + rgb (0.846416, 0.272473, 0.421631) , + rgb (0.852126, 0.276106, 0.418573) , + rgb (0.857763, 0.279857, 0.415496) , + rgb (0.86332, 0.283729, 0.412403) , + rgb (0.868793, 0.287728, 0.409303) , + rgb (0.874176, 0.291859, 0.406205) , + rgb (0.879464, 0.296125, 0.403118) , + rgb (0.884651, 0.30053, 0.400047) , + rgb (0.889731, 0.305079, 0.397002) , + rgb (0.8947, 0.309773, 0.393995) , + rgb (0.899552, 0.314616, 0.391037) , + rgb (0.904281, 0.31961, 0.388137) , + rgb (0.908884, 0.324755, 0.385308) , + rgb (0.913354, 0.330052, 0.382563) , + rgb (0.917689, 0.3355, 0.379915) , + rgb (0.921884, 0.341098, 0.377376) , + rgb (0.925937, 0.346844, 0.374959) , + rgb (0.929845, 0.352734, 0.372677) , + rgb (0.933606, 0.358764, 0.370541) , + rgb (0.937221, 0.364929, 0.368567) , + rgb (0.940687, 0.371224, 0.366762) , + rgb (0.944006, 0.377643, 0.365136) , + rgb (0.94718, 0.384178, 0.363701) , + rgb (0.95021, 0.39082, 0.362468) , + rgb (0.953099, 0.397563, 0.361438) , + rgb (0.955849, 0.4044, 0.360619) , + rgb (0.958464, 0.411324, 0.360014) , + rgb (0.960949, 0.418323, 0.35963) , + rgb (0.96331, 0.42539, 0.359469) , + rgb (0.965549, 0.432519, 0.359529) , + rgb (0.967671, 0.439703, 0.35981) , + rgb (0.96968, 0.446936, 0.360311) , + rgb (0.971582, 0.45421, 0.36103) , + rgb (0.973381, 0.46152, 0.361965) , + rgb (0.975082, 0.468861, 0.363111) , + rgb (0.97669, 0.476226, 0.364466) , + rgb (0.97821, 0.483612, 0.366025) , + rgb (0.979645, 0.491014, 0.367783) , + rgb (0.981, 0.498428, 0.369734) , + rgb (0.982279, 0.505851, 0.371874) , + rgb (0.983485, 0.51328, 0.374198) , + rgb (0.984622, 0.520713, 0.376698) , + rgb (0.985693, 0.528148, 0.379371) , + rgb (0.9867, 0.535582, 0.38221) , + rgb (0.987646, 0.543015, 0.38521) , + rgb (0.988533, 0.550446, 0.388365) , + rgb (0.989363, 0.557873, 0.391671) , + rgb (0.990138, 0.565296, 0.395122) , + rgb (0.990871, 0.572706, 0.398714) , + rgb (0.991558, 0.580107, 0.402441) , + rgb (0.992196, 0.587502, 0.406299) , + rgb (0.992785, 0.594891, 0.410283) , + rgb (0.993326, 0.602275, 0.41439) , + rgb (0.993834, 0.609644, 0.418613) , + rgb (0.994309, 0.616999, 0.42295) , + rgb (0.994738, 0.62435, 0.427397) , + rgb (0.995122, 0.631696, 0.431951) , + rgb (0.99548, 0.639027, 0.436607) , + rgb (0.99581, 0.646344, 0.441361) , + rgb (0.996096, 0.653659, 0.446213) , + rgb (0.996341, 0.660969, 0.45116) , + rgb (0.99658, 0.668256, 0.456192) , + rgb (0.996775, 0.675541, 0.461314) , + rgb (0.996925, 0.682828, 0.466526) , + rgb (0.997077, 0.690088, 0.471811) , + rgb (0.997186, 0.697349, 0.477182) , + rgb (0.997254, 0.704611, 0.482635) , + rgb (0.997325, 0.711848, 0.488154) , + rgb (0.997351, 0.719089, 0.493755) , + rgb (0.997351, 0.726324, 0.499428) , + rgb (0.997341, 0.733545, 0.505167) , + rgb (0.997285, 0.740772, 0.510983) , + rgb (0.997228, 0.747981, 0.516859) , + rgb (0.997138, 0.75519, 0.522806) , + rgb (0.997019, 0.762398, 0.528821) , + rgb (0.996898, 0.769591, 0.534892) , + rgb (0.996727, 0.776795, 0.541039) , + rgb (0.996571, 0.783977, 0.547233) , + rgb (0.996369, 0.791167, 0.553499) , + rgb (0.996162, 0.798348, 0.55982) , + rgb (0.995932, 0.805527, 0.566202) , + rgb (0.99568, 0.812706, 0.572645) , + rgb (0.995424, 0.819875, 0.57914) , + rgb (0.995131, 0.827052, 0.585701) , + rgb (0.994851, 0.834213, 0.592307) , + rgb (0.994524, 0.841387, 0.598983) , + rgb (0.994222, 0.84854, 0.605696) , + rgb (0.993866, 0.855711, 0.612482) , + rgb (0.993545, 0.862859, 0.619299) , + rgb (0.99317, 0.870024, 0.626189) , + rgb (0.992831, 0.877168, 0.633109) , + rgb (0.99244, 0.88433, 0.640099) , + rgb (0.992089, 0.89147, 0.647116) , + rgb (0.991688, 0.898627, 0.654202) , + rgb (0.991332, 0.905763, 0.661309) , + rgb (0.99093, 0.912915, 0.668481) , + rgb (0.99057, 0.920049, 0.675675) , + rgb (0.990175, 0.927196, 0.682926) , + rgb (0.989815, 0.934329, 0.690198) , + rgb (0.989434, 0.94147, 0.697519) , + rgb (0.989077, 0.948604, 0.704863) , + rgb (0.988717, 0.955742, 0.712242) , + rgb (0.988367, 0.962878, 0.719649) , + rgb (0.988033, 0.970012, 0.727077) , + rgb (0.987691, 0.977154, 0.734536) , + rgb (0.987387, 0.984288, 0.742002) , + rgb (0.987053, 0.991438, 0.749504) + }); + + +list_data plasma = list_data(new pen[] { + rgb (0.050383, 0.029803, 0.527975) , + rgb (0.063536, 0.028426, 0.533124) , + rgb (0.075353, 0.027206, 0.538007) , + rgb (0.086222, 0.026125, 0.542658) , + rgb (0.096379, 0.025165, 0.547103) , + rgb (0.10598, 0.024309, 0.551368) , + rgb (0.115124, 0.023556, 0.555468) , + rgb (0.123903, 0.022878, 0.559423) , + rgb (0.132381, 0.022258, 0.56325) , + rgb (0.140603, 0.021687, 0.566959) , + rgb (0.148607, 0.021154, 0.570562) , + rgb (0.156421, 0.020651, 0.574065) , + rgb (0.16407, 0.020171, 0.577478) , + rgb (0.171574, 0.019706, 0.580806) , + rgb (0.17895, 0.019252, 0.584054) , + rgb (0.186213, 0.018803, 0.587228) , + rgb (0.193374, 0.018354, 0.59033) , + rgb (0.200445, 0.017902, 0.593364) , + rgb (0.207435, 0.017442, 0.596333) , + rgb (0.21435, 0.016973, 0.599239) , + rgb (0.221197, 0.016497, 0.602083) , + rgb (0.227983, 0.016007, 0.604867) , + rgb (0.234715, 0.015502, 0.607592) , + rgb (0.241396, 0.014979, 0.610259) , + rgb (0.248032, 0.014439, 0.612868) , + rgb (0.254627, 0.013882, 0.615419) , + rgb (0.261183, 0.013308, 0.617911) , + rgb (0.267703, 0.012716, 0.620346) , + rgb (0.274191, 0.012109, 0.622722) , + rgb (0.280648, 0.011488, 0.625038) , + rgb (0.287076, 0.010855, 0.627295) , + rgb (0.293478, 0.010213, 0.62949) , + rgb (0.299855, 0.009561, 0.631624) , + rgb (0.30621, 0.008902, 0.633694) , + rgb (0.312543, 0.008239, 0.6357) , + rgb (0.318856, 0.007576, 0.63764) , + rgb (0.32515, 0.006915, 0.639512) , + rgb (0.331426, 0.006261, 0.641316) , + rgb (0.337683, 0.005618, 0.643049) , + rgb (0.343925, 0.004991, 0.64471) , + rgb (0.35015, 0.004382, 0.646298) , + rgb (0.356359, 0.003798, 0.64781) , + rgb (0.362553, 0.003243, 0.649245) , + rgb (0.368733, 0.002724, 0.650601) , + rgb (0.374897, 0.002245, 0.651876) , + rgb (0.381047, 0.001814, 0.653068) , + rgb (0.387183, 0.001434, 0.654177) , + rgb (0.393304, 0.001114, 0.655199) , + rgb (0.399411, 0.000859, 0.656133) , + rgb (0.405503, 0.000678, 0.656977) , + rgb (0.41158, 0.000577, 0.65773) , + rgb (0.417642, 0.000564, 0.65839) , + rgb (0.423689, 0.000646, 0.658956) , + rgb (0.429719, 0.000831, 0.659425) , + rgb (0.435734, 0.001127, 0.659797) , + rgb (0.441732, 0.00154, 0.660069) , + rgb (0.447714, 0.00208, 0.66024) , + rgb (0.453677, 0.002755, 0.66031) , + rgb (0.459623, 0.003574, 0.660277) , + rgb (0.46555, 0.004545, 0.660139) , + rgb (0.471457, 0.005678, 0.659897) , + rgb (0.477344, 0.00698, 0.659549) , + rgb (0.48321, 0.00846, 0.659095) , + rgb (0.489055, 0.010127, 0.658534) , + rgb (0.494877, 0.01199, 0.657865) , + rgb (0.500678, 0.014055, 0.657088) , + rgb (0.506454, 0.016333, 0.656202) , + rgb (0.512206, 0.018833, 0.655209) , + rgb (0.517933, 0.021563, 0.654109) , + rgb (0.523633, 0.024532, 0.652901) , + rgb (0.529306, 0.027747, 0.651586) , + rgb (0.534952, 0.031217, 0.650165) , + rgb (0.54057, 0.03495, 0.64864) , + rgb (0.546157, 0.038954, 0.64701) , + rgb (0.551715, 0.043136, 0.645277) , + rgb (0.557243, 0.047331, 0.643443) , + rgb (0.562738, 0.051545, 0.641509) , + rgb (0.568201, 0.055778, 0.639477) , + rgb (0.573632, 0.060028, 0.637349) , + rgb (0.579029, 0.064296, 0.635126) , + rgb (0.584391, 0.068579, 0.632812) , + rgb (0.589719, 0.072878, 0.630408) , + rgb (0.595011, 0.07719, 0.627917) , + rgb (0.600266, 0.081516, 0.625342) , + rgb (0.605485, 0.085854, 0.622686) , + rgb (0.610667, 0.090204, 0.619951) , + rgb (0.615812, 0.094564, 0.61714) , + rgb (0.620919, 0.098934, 0.614257) , + rgb (0.625987, 0.103312, 0.611305) , + rgb (0.631017, 0.107699, 0.608287) , + rgb (0.636008, 0.112092, 0.605205) , + rgb (0.640959, 0.116492, 0.602065) , + rgb (0.645872, 0.120898, 0.598867) , + rgb (0.650746, 0.125309, 0.595617) , + rgb (0.65558, 0.129725, 0.592317) , + rgb (0.660374, 0.134144, 0.588971) , + rgb (0.665129, 0.138566, 0.585582) , + rgb (0.669845, 0.142992, 0.582154) , + rgb (0.674522, 0.147419, 0.578688) , + rgb (0.67916, 0.151848, 0.575189) , + rgb (0.683758, 0.156278, 0.57166) , + rgb (0.688318, 0.160709, 0.568103) , + rgb (0.69284, 0.165141, 0.564522) , + rgb (0.697324, 0.169573, 0.560919) , + rgb (0.701769, 0.174005, 0.557296) , + rgb (0.706178, 0.178437, 0.553657) , + rgb (0.710549, 0.182868, 0.550004) , + rgb (0.714883, 0.187299, 0.546338) , + rgb (0.719181, 0.191729, 0.542663) , + rgb (0.723444, 0.196158, 0.538981) , + rgb (0.72767, 0.200586, 0.535293) , + rgb (0.731862, 0.205013, 0.531601) , + rgb (0.736019, 0.209439, 0.527908) , + rgb (0.740143, 0.213864, 0.524216) , + rgb (0.744232, 0.218288, 0.520524) , + rgb (0.748289, 0.222711, 0.516834) , + rgb (0.752312, 0.227133, 0.513149) , + rgb (0.756304, 0.231555, 0.509468) , + rgb (0.760264, 0.235976, 0.505794) , + rgb (0.764193, 0.240396, 0.502126) , + rgb (0.76809, 0.244817, 0.498465) , + rgb (0.771958, 0.249237, 0.494813) , + rgb (0.775796, 0.253658, 0.491171) , + rgb (0.779604, 0.258078, 0.487539) , + rgb (0.783383, 0.2625, 0.483918) , + rgb (0.787133, 0.266922, 0.480307) , + rgb (0.790855, 0.271345, 0.476706) , + rgb (0.794549, 0.27577, 0.473117) , + rgb (0.798216, 0.280197, 0.469538) , + rgb (0.801855, 0.284626, 0.465971) , + rgb (0.805467, 0.289057, 0.462415) , + rgb (0.809052, 0.293491, 0.45887) , + rgb (0.812612, 0.297928, 0.455338) , + rgb (0.816144, 0.302368, 0.451816) , + rgb (0.819651, 0.306812, 0.448306) , + rgb (0.823132, 0.311261, 0.444806) , + rgb (0.826588, 0.315714, 0.441316) , + rgb (0.830018, 0.320172, 0.437836) , + rgb (0.833422, 0.324635, 0.434366) , + rgb (0.836801, 0.329105, 0.430905) , + rgb (0.840155, 0.33358, 0.427455) , + rgb (0.843484, 0.338062, 0.424013) , + rgb (0.846788, 0.342551, 0.420579) , + rgb (0.850066, 0.347048, 0.417153) , + rgb (0.853319, 0.351553, 0.413734) , + rgb (0.856547, 0.356066, 0.410322) , + rgb (0.85975, 0.360588, 0.406917) , + rgb (0.862927, 0.365119, 0.403519) , + rgb (0.866078, 0.36966, 0.400126) , + rgb (0.869203, 0.374212, 0.396738) , + rgb (0.872303, 0.378774, 0.393355) , + rgb (0.875376, 0.383347, 0.389976) , + rgb (0.878423, 0.387932, 0.3866) , + rgb (0.881443, 0.392529, 0.383229) , + rgb (0.884436, 0.397139, 0.37986) , + rgb (0.887402, 0.401762, 0.376494) , + rgb (0.89034, 0.406398, 0.37313) , + rgb (0.89325, 0.411048, 0.369768) , + rgb (0.896131, 0.415712, 0.366407) , + rgb (0.898984, 0.420392, 0.363047) , + rgb (0.901807, 0.425087, 0.359688) , + rgb (0.904601, 0.429797, 0.356329) , + rgb (0.907365, 0.434524, 0.35297) , + rgb (0.910098, 0.439268, 0.34961) , + rgb (0.9128, 0.444029, 0.346251) , + rgb (0.915471, 0.448807, 0.34289) , + rgb (0.918109, 0.453603, 0.339529) , + rgb (0.920714, 0.458417, 0.336166) , + rgb (0.923287, 0.463251, 0.332801) , + rgb (0.925825, 0.468103, 0.329435) , + rgb (0.928329, 0.472975, 0.326067) , + rgb (0.930798, 0.477867, 0.322697) , + rgb (0.933232, 0.48278, 0.319325) , + rgb (0.93563, 0.487712, 0.315952) , + rgb (0.93799, 0.492667, 0.312575) , + rgb (0.940313, 0.497642, 0.309197) , + rgb (0.942598, 0.502639, 0.305816) , + rgb (0.944844, 0.507658, 0.302433) , + rgb (0.947051, 0.512699, 0.299049) , + rgb (0.949217, 0.517763, 0.295662) , + rgb (0.951344, 0.52285, 0.292275) , + rgb (0.953428, 0.52796, 0.288883) , + rgb (0.95547, 0.533093, 0.28549) , + rgb (0.957469, 0.53825, 0.282096) , + rgb (0.959424, 0.543431, 0.278701) , + rgb (0.961336, 0.548636, 0.275305) , + rgb (0.963203, 0.553865, 0.271909) , + rgb (0.965024, 0.559118, 0.268513) , + rgb (0.966798, 0.564396, 0.265118) , + rgb (0.968526, 0.5697, 0.261721) , + rgb (0.970205, 0.575028, 0.258325) , + rgb (0.971835, 0.580382, 0.254931) , + rgb (0.973416, 0.585761, 0.25154) , + rgb (0.974947, 0.591165, 0.248151) , + rgb (0.976428, 0.596595, 0.244767) , + rgb (0.977856, 0.602051, 0.241387) , + rgb (0.979233, 0.607532, 0.238013) , + rgb (0.980556, 0.613039, 0.234646) , + rgb (0.981826, 0.618572, 0.231287) , + rgb (0.983041, 0.624131, 0.227937) , + rgb (0.984199, 0.629718, 0.224595) , + rgb (0.985301, 0.63533, 0.221265) , + rgb (0.986345, 0.640969, 0.217948) , + rgb (0.987332, 0.646633, 0.214648) , + rgb (0.98826, 0.652325, 0.211364) , + rgb (0.989128, 0.658043, 0.2081) , + rgb (0.989935, 0.663787, 0.204859) , + rgb (0.990681, 0.669558, 0.201642) , + rgb (0.991365, 0.675355, 0.198453) , + rgb (0.991985, 0.681179, 0.195295) , + rgb (0.992541, 0.68703, 0.19217) , + rgb (0.993032, 0.692907, 0.189084) , + rgb (0.993456, 0.69881, 0.186041) , + rgb (0.993814, 0.704741, 0.183043) , + rgb (0.994103, 0.710698, 0.180097) , + rgb (0.994324, 0.716681, 0.177208) , + rgb (0.994474, 0.722691, 0.174381) , + rgb (0.994553, 0.728728, 0.171622) , + rgb (0.994561, 0.734791, 0.168938) , + rgb (0.994495, 0.74088, 0.166335) , + rgb (0.994355, 0.746995, 0.163821) , + rgb (0.994141, 0.753137, 0.161404) , + rgb (0.993851, 0.759304, 0.159092) , + rgb (0.993482, 0.765499, 0.156891) , + rgb (0.993033, 0.77172, 0.154808) , + rgb (0.992505, 0.777967, 0.152855) , + rgb (0.991897, 0.784239, 0.151042) , + rgb (0.991209, 0.790537, 0.149377) , + rgb (0.990439, 0.796859, 0.14787) , + rgb (0.989587, 0.803205, 0.146529) , + rgb (0.988648, 0.809579, 0.145357) , + rgb (0.987621, 0.815978, 0.144363) , + rgb (0.986509, 0.822401, 0.143557) , + rgb (0.985314, 0.828846, 0.142945) , + rgb (0.984031, 0.835315, 0.142528) , + rgb (0.982653, 0.841812, 0.142303) , + rgb (0.98119, 0.848329, 0.142279) , + rgb (0.979644, 0.854866, 0.142453) , + rgb (0.977995, 0.861432, 0.142808) , + rgb (0.976265, 0.868016, 0.143351) , + rgb (0.974443, 0.874622, 0.144061) , + rgb (0.97253, 0.88125, 0.144923) , + rgb (0.970533, 0.887896, 0.145919) , + rgb (0.968443, 0.894564, 0.147014) , + rgb (0.966271, 0.901249, 0.14818) , + rgb (0.964021, 0.90795, 0.14937) , + rgb (0.961681, 0.914672, 0.15052) , + rgb (0.959276, 0.921407, 0.151566) , + rgb (0.956808, 0.928152, 0.152409) , + rgb (0.954287, 0.934908, 0.152921) , + rgb (0.951726, 0.941671, 0.152925) , + rgb (0.949151, 0.948435, 0.152178) , + rgb (0.946602, 0.95519, 0.150328) , + rgb (0.944152, 0.961916, 0.146861) , + rgb (0.941896, 0.96859, 0.140956) , + rgb (0.940015, 0.975158, 0.131326) + }); + + +list_data twilight = list_data(new pen[] { + rgb (0.8857501584075443, 0.8500092494306783, 0.8879736506427196) , + rgb (0.8837852019553906, 0.8507294054031063, 0.8872322209694989) , + rgb (0.8817223105928579, 0.8512759407765347, 0.8863805692551482) , + rgb (0.8795410528270573, 0.8516567540749572, 0.8854143767924102) , + rgb (0.8772488085896548, 0.8518702833887027, 0.8843412038131143) , + rgb (0.8748534750857597, 0.8519152612302319, 0.8831692696761383) , + rgb (0.8723313408512408, 0.8518016547808089, 0.8818970435500162) , + rgb (0.8697047485350982, 0.8515240300479789, 0.8805388339000336) , + rgb (0.8669601550533358, 0.8510896085314068, 0.8790976697717334) , + rgb (0.86408985081464, 0.8505039116750779, 0.8775792578489263) , + rgb (0.8611024543689985, 0.8497675485700126, 0.8759924292343957) , + rgb (0.8579825924567037, 0.8488893481028184, 0.8743403855344628) , + rgb (0.8547259318925698, 0.8478748812467282, 0.8726282980930582) , + rgb (0.8513371457085719, 0.8467273579611647, 0.8708608165735044) , + rgb (0.8478071070257792, 0.8454546229209523, 0.8690403678369444) , + rgb (0.8441261828674842, 0.8440648271103739, 0.8671697332269007) , + rgb (0.8403042080595778, 0.8425605950855084, 0.865250882410458) , + rgb (0.8363403180919118, 0.8409479651895194, 0.8632852800107016) , + rgb (0.8322270571293441, 0.8392349062775448, 0.8612756350042788) , + rgb (0.8279689431601354, 0.837426007513952, 0.8592239945130679) , + rgb (0.8235742968025285, 0.8355248776479544, 0.8571319132851495) , + rgb (0.8190465467793753, 0.8335364929949034, 0.855002062870101) , + rgb (0.8143898212114309, 0.8314655869419785, 0.8528375906214702) , + rgb (0.8095999819094809, 0.8293189667350546, 0.8506444160105037) , + rgb (0.8046916442981458, 0.8270983878056066, 0.8484244929697402) , + rgb (0.79967075421268, 0.8248078181208093, 0.8461821002957853) , + rgb (0.7945430508923111, 0.8224511622630462, 0.8439218478682798) , + rgb (0.7893144556460892, 0.8200321318870201, 0.8416486380471222) , + rgb (0.7839910104276492, 0.8175542640053343, 0.8393674746403673) , + rgb (0.7785789200822759, 0.8150208937874255, 0.8370834463093898) , + rgb (0.7730841659017094, 0.8124352473546601, 0.8348017295057968) , + rgb (0.7675110850441786, 0.8098007598713145, 0.8325281663805967) , + rgb (0.7618690793798029, 0.8071194938764749, 0.830266486168872) , + rgb (0.7561644358438198, 0.8043940873347794, 0.8280213899472) , + rgb (0.750403467654067, 0.8016269900896532, 0.8257973785108242) , + rgb (0.7445924777189017, 0.7988204771958325, 0.8235986758615652) , + rgb (0.7387377170049494, 0.7959766573503101, 0.8214292278043301) , + rgb (0.7328454364552346, 0.7930974646884407, 0.8192926338423038) , + rgb (0.726921775128297, 0.7901846863592763, 0.8171921746672638) , + rgb (0.7209728066553678, 0.7872399592345264, 0.8151307392087926) , + rgb (0.7150040307625213, 0.7842648709158119, 0.8131111655994991) , + rgb (0.709020781345393, 0.7812608871607091, 0.8111359185511793) , + rgb (0.7030297722540817, 0.7782290497335813, 0.8092061884805697) , + rgb (0.6970365443886174, 0.7751705000806606, 0.8073233538006345) , + rgb (0.691046410093091, 0.7720862946067809, 0.8054884169067907) , + rgb (0.6850644615439593, 0.7689774029354699, 0.8037020626717691) , + rgb (0.6790955449988215, 0.765844721313959, 0.8019646617300199) , + rgb (0.6731442255942621, 0.7626890873389048, 0.8002762854580953) , + rgb (0.6672147980375281, 0.7595112803730375, 0.7986367465453776) , + rgb (0.6613112930078745, 0.7563120270871903, 0.7970456043491897) , + rgb (0.6554369232645472, 0.7530920875676843, 0.7955027112903105) , + rgb (0.6495957300425348, 0.7498520122194177, 0.7940067402149911) , + rgb (0.6437910831099849, 0.7465923800833657, 0.7925565320130605) , + rgb (0.6380258682854598, 0.7433137671403319, 0.7911510045957317) , + rgb (0.6323027138710603, 0.740016721601314, 0.7897889276264043) , + rgb (0.6266240202260459, 0.7367017540369944, 0.7884690131633456) , + rgb (0.6209919306481755, 0.733369347989232, 0.7871899462469658) , + rgb (0.6154084641177048, 0.7300199523273969, 0.7859502270675048) , + rgb (0.6098754317609306, 0.7266539875975829, 0.7847483573269471) , + rgb (0.6043943420027486, 0.7232718614323369, 0.7835829559353559) , + rgb (0.5989665814482068, 0.7198739489224673, 0.7824525989934664) , + rgb (0.5935933569683722, 0.7164606049658685, 0.781355882376401) , + rgb (0.588275797805555, 0.7130321464645814, 0.7802914140563652) , + rgb (0.5830148703693241, 0.7095888767699747, 0.7792578182047659) , + rgb (0.5778116438998202, 0.7061310615715398, 0.7782534512102552) , + rgb (0.5726668948158774, 0.7026589535425779, 0.7772770268091199) , + rgb (0.5675811785386197, 0.6991727930264627, 0.776327485342753) , + rgb (0.5625551535721934, 0.6956727838162965, 0.7754035914230984) , + rgb (0.5575894041960517, 0.6921591145825405, 0.7745041337932782) , + rgb (0.5526845058934713, 0.6886319451516638, 0.7736279426902245) , + rgb (0.5478409815301863, 0.6850914221850988, 0.7727738647344087) , + rgb (0.5430593242401823, 0.6815376725306588, 0.7719407969783508) , + rgb (0.5383401557517628, 0.677970811290954, 0.7711273443905772) , + rgb (0.533683891477284, 0.6743909370521273, 0.7703325054879735) , + rgb (0.529090861832473, 0.6707981230280622, 0.7695555229231313) , + rgb (0.5245615147059358, 0.6671924299614223, 0.7687954171423095) , + rgb (0.5200962739223556, 0.6635739143403039, 0.768051194033441) , + rgb (0.5156955988596057, 0.65994260812898, 0.7673219148959617) , + rgb (0.5113599254160193, 0.6562985398183186, 0.7666066378064533) , + rgb (0.5070896957645166, 0.6526417240314645, 0.7659044566083585) , + rgb (0.5028853540415561, 0.6489721673409526, 0.7652144671817491) , + rgb (0.4987473366135607, 0.6452898684900934, 0.7645357873418008) , + rgb (0.4946761847863938, 0.6415948411950443, 0.7638671900213091) , + rgb (0.4906722493856122, 0.6378870485884708, 0.7632081276316384) , + rgb (0.4867359599430568, 0.6341664625110051, 0.7625578008592404) , + rgb (0.4828677867260272, 0.6304330455306234, 0.761915371498953) , + rgb (0.47906816236197386, 0.6266867625186013, 0.7612800037566242) , + rgb (0.47533752394906287, 0.6229275728383581, 0.7606508557181775) , + rgb (0.4716762951887709, 0.6191554324288464, 0.7600270922788305) , + rgb (0.46808490970531597, 0.6153702869579029, 0.7594078989109274) , + rgb (0.4645637671630393, 0.6115720882286415, 0.7587924262302581) , + rgb (0.4611132664702388, 0.607760777169989, 0.7581798643680714) , + rgb (0.45773377230160567, 0.6039363004658646, 0.7575693690185916) , + rgb (0.45442563977552913, 0.6000985950385866, 0.7569601366060649) , + rgb (0.45118918687617743, 0.5962476205135354, 0.7563512064324664) , + rgb (0.4480247093358917, 0.5923833145214658, 0.7557417647410792) , + rgb (0.4449324685421538, 0.5885055998308617, 0.7551311041857901) , + rgb (0.441912717666964, 0.5846144110017557, 0.7545183888441067) , + rgb (0.43896563958048396, 0.5807096924109849, 0.7539027620828594) , + rgb (0.4360913895835637, 0.5767913799818608, 0.7532834105961016) , + rgb (0.43329008867358393, 0.5728594162560667, 0.7526594653256667) , + rgb (0.4305617907305757, 0.5689137457245718, 0.752030080993127) , + rgb (0.42790652284925834, 0.5649543060909209, 0.7513944352191484) , + rgb (0.42532423665011354, 0.560981049599503, 0.7507516498900512) , + rgb (0.4228148567577266, 0.5569939212699658, 0.7501008698822764) , + rgb (0.42037822361396326, 0.5529928715810817, 0.7494412559451894) , + rgb (0.4180141407923363, 0.5489778542188889, 0.7487719316700112) , + rgb (0.4157223260454232, 0.544948827153504, 0.7480920445900052) , + rgb (0.4135024574331473, 0.5409057477109848, 0.7474007329754309) , + rgb (0.4113541469730457, 0.5368485776500593, 0.7466971285506578) , + rgb (0.4092768899914751, 0.5327773017713032, 0.7459803063570782) , + rgb (0.4072701869421907, 0.5286918801105741, 0.7452494263758127) , + rgb (0.4053334378930318, 0.5245922817498312, 0.7445036583670813) , + rgb (0.40346600333905397, 0.5204784765384003, 0.7437421522356709) , + rgb (0.40166714010896104, 0.5163504496968876, 0.7429640345324835) , + rgb (0.39993606933454834, 0.5122081814321852, 0.7421684457131799) , + rgb (0.3982719152586337, 0.5080516653927614, 0.7413545091809972) , + rgb (0.3966737490566561, 0.5038808905384797, 0.7405213858051674) , + rgb (0.3951405880820763, 0.4996958532637776, 0.7396682021171571) , + rgb (0.39367135736822567, 0.4954965577745118, 0.738794102296364) , + rgb (0.39226494876209317, 0.4912830033289926, 0.7378982478447508) , + rgb (0.390920175719949, 0.4870552025122304, 0.7369797713388125) , + rgb (0.38963580160340855, 0.48281316715123496, 0.7360378254693274) , + rgb (0.3884105330084243, 0.47855691131792805, 0.7350715764115726) , + rgb (0.3872430145933025, 0.4742864593363539, 0.7340801678785439) , + rgb (0.386131841788921, 0.4700018340988123, 0.7330627749243106) , + rgb (0.3850755679365139, 0.46570306719930193, 0.732018540336905) , + rgb (0.38407269378943537, 0.46139018782416635, 0.7309466543290268) , + rgb (0.3831216808440275, 0.457063235814072, 0.7298462679135326) , + rgb (0.38222094988570376, 0.45272225034283325, 0.7287165614400378) , + rgb (0.3813688793045416, 0.4483672766927786, 0.7275567131714135) , + rgb (0.3805638069656562, 0.4439983720863372, 0.7263658704513531) , + rgb (0.3798040374484875, 0.4396155882122263, 0.7251432377876109) , + rgb (0.3790878928311076, 0.43521897612544935, 0.7238879869132313) , + rgb (0.378413635091359, 0.43080859411413064, 0.7225993199306104) , + rgb (0.3777794975351373, 0.4263845142616835, 0.7212763999353023) , + rgb (0.3771837184425123, 0.4219468022345483, 0.7199184152447577) , + rgb (0.37662448930806297, 0.41749553747893614, 0.7185245473617611) , + rgb (0.37610001286385814, 0.4130307995247706, 0.7170939691992023) , + rgb (0.375608469194424, 0.40855267638072096, 0.7156258509158755) , + rgb (0.37514802505380473, 0.4040612609993941, 0.7141193695725726) , + rgb (0.3747168601930223, 0.3995566498711684, 0.7125736851650046) , + rgb (0.3743131319931234, 0.3950389482828331, 0.7109879652237746) , + rgb (0.3739349933047578, 0.3905082752937583, 0.7093613429347845) , + rgb (0.3735806215098284, 0.3859647438605754, 0.7076929760731058) , + rgb (0.37324816143326384, 0.38140848555753937, 0.7059820097480604) , + rgb (0.3729357864666503, 0.3768396383521984, 0.7042275578058994) , + rgb (0.37264166757849604, 0.3722583500483685, 0.7024287314570723) , + rgb (0.37236397858465387, 0.36766477862108266, 0.7005846349652077) , + rgb (0.3721008970244382, 0.3630590973698238, 0.6986943461507372) , + rgb (0.3718506155898596, 0.3584414828587522, 0.6967569581025654) , + rgb (0.3716113323440048, 0.3538121372967869, 0.6947714991938089) , + rgb (0.37138124223736607, 0.34917126878479027, 0.6927370347192883) , + rgb (0.37115856636209105, 0.3445191141023017, 0.6906525358646499) , + rgb (0.3709415155133733, 0.33985591488818123, 0.6885170337950512) , + rgb (0.3707283327942267, 0.33518193808489577, 0.6863294816960677) , + rgb (0.37051738634484427, 0.3304974124430785, 0.6840888878885721) , + rgb (0.37030682071842685, 0.32580269697872455, 0.6817941168448668) , + rgb (0.37009487130772695, 0.3210981375964933, 0.6794440539905685) , + rgb (0.3698798032902536, 0.31638410101153364, 0.6770375543809057) , + rgb (0.36965987626565955, 0.3116609876295197, 0.6745734474341955) , + rgb (0.3694333459127623, 0.3069292355186234, 0.6720505284912062) , + rgb (0.36919847837592484, 0.3021893217650707, 0.6694675433161452) , + rgb (0.3689535530659678, 0.29744175492366276, 0.6668232208982426) , + rgb (0.3686968223189527, 0.292687098561501, 0.6641162529823691) , + rgb (0.36842655638020444, 0.2879259643777846, 0.661345269109446) , + rgb (0.3681410147989972, 0.2831590122118299, 0.6585088880697231) , + rgb (0.3678384369653108, 0.2783869718129776, 0.655605668384537) , + rgb (0.36751707094367697, 0.2736106331709098, 0.6526341171161864) , + rgb (0.36717513650699446, 0.26883085667326956, 0.6495927229789225) , + rgb (0.3668108554010799, 0.26404857724525643, 0.6464799165290824) , + rgb (0.3664224325155063, 0.25926481158628106, 0.6432940914076554) , + rgb (0.36600853966739794, 0.25448043878086224, 0.6400336180336859) , + rgb (0.3655669837353898, 0.24969683475296395, 0.6366967518748858) , + rgb (0.3650957984588681, 0.24491536803550484, 0.6332817352005559) , + rgb (0.3645930889012501, 0.24013747024823828, 0.629786801550261) , + rgb (0.3640569302208851, 0.23536470386204195, 0.6262101345195302) , + rgb (0.36348537610385145, 0.2305987621839642, 0.6225498862239288) , + rgb (0.3628764356004103, 0.2258414929328703, 0.6188041741082302) , + rgb (0.36222809558295926, 0.22109488427338303, 0.6149711234609613) , + rgb (0.36153829010998356, 0.21636111429594002, 0.6110488067964093) , + rgb (0.36080493826624654, 0.21164251793458128, 0.6070353217206471) , + rgb (0.36002681809096376, 0.20694122817889948, 0.6029284543191687) , + rgb (0.35920088560930186, 0.20226037920758122, 0.5987265295935138) , + rgb (0.3583248996661781, 0.197602942459778, 0.5944276851750107) , + rgb (0.35739663292915563, 0.1929720819784246, 0.5900301125106313) , + rgb (0.35641381143126327, 0.18837119869242164, 0.5855320765920552) , + rgb (0.3553741530690672, 0.18380392577704466, 0.580931914318328) , + rgb (0.3542753496066376, 0.17927413271618647, 0.5762280966066872) , + rgb (0.35311574421123737, 0.17478570377561287, 0.5714187152355529) , + rgb (0.3518924860887379, 0.17034320478524959, 0.5665028491121665) , + rgb (0.3506030444193101, 0.1659512998472086, 0.5614796470399323) , + rgb (0.34924513554955644, 0.16161477763045118, 0.5563483747416378) , + rgb (0.3478165323877778, 0.1573386351115298, 0.5511085345270326) , + rgb (0.3463150717579309, 0.15312802296627787, 0.5457599924248665) , + rgb (0.34473901574536375, 0.1489882058982641, 0.5403024592040654) , + rgb (0.34308600291572294, 0.14492465359918028, 0.534737042820671) , + rgb (0.34135411074506483, 0.1409427920655632, 0.5290650094033675) , + rgb (0.33954168752669694, 0.1370480189671817, 0.5232879753508524) , + rgb (0.3376473209067111, 0.13324562282438077, 0.5174080757397947) , + rgb (0.33566978565015315, 0.12954074251271822, 0.5114280721516895) , + rgb (0.33360804901486, 0.1259381830100592, 0.505351647966549) , + rgb (0.33146154891145124, 0.12244245263391232, 0.4991827458843107) , + rgb (0.3292300520323141, 0.11905764321981127, 0.49292595612342666) , + rgb (0.3269137124539796, 0.1157873496841953, 0.4865864649569746) , + rgb (0.32451307931207785, 0.11263459791730848, 0.48017007211645196) , + rgb (0.3220288227606932, 0.10960114111258401, 0.4736849472572688) , + rgb (0.31946262395497965, 0.1066887988239266, 0.46713728801395243) , + rgb (0.316816480890235, 0.10389861387653518, 0.46053414662739794) , + rgb (0.3140927841475553, 0.10123077676403242, 0.45388335612058467) , + rgb (0.31129434479712365, 0.0986847719340522, 0.4471931371516162) , + rgb (0.30842444457210105, 0.09625938534057774, 0.44047194882050544) , + rgb (0.30548675819945936, 0.09395276484082374, 0.4337284999936111) , + rgb (0.3024853636457425, 0.0917611873973036, 0.42697404043749887) , + rgb (0.2994248396021477, 0.08968225371675004, 0.42021619665853854) , + rgb (0.2963100038890529, 0.08771325096046395, 0.41346259134143476) , + rgb (0.2931459309698525, 0.08585065688962071, 0.40672178082365834) , + rgb (0.2899379244517661, 0.08409078829085731, 0.40000214725256295) , + rgb (0.28669151388283165, 0.08242987384848069, 0.39331182532243375) , + rgb (0.28341239797185225, 0.08086415336549937, 0.38665868550105914) , + rgb (0.2801063857697547, 0.07938999480226153, 0.38005028528138707) , + rgb (0.2767793961581559, 0.07800394103378822, 0.37349382846504675) , + rgb (0.2734373934245081, 0.07670280023749607, 0.36699616136347685) , + rgb (0.2700863774911405, 0.07548367558427554, 0.36056376228111864) , + rgb (0.26673233211995284, 0.0743440180285462, 0.3542027606624096) , + rgb (0.26338121807151404, 0.07328165793989708, 0.34791888996380105) , + rgb (0.26003895187439957, 0.0722947810433622, 0.3417175669546984) , + rgb (0.256711916510839, 0.07138010624208224, 0.3356064898460009) , + rgb (0.25340685873736807, 0.07053358292685183, 0.3295945757321303) , + rgb (0.2501284530619938, 0.06975820642910699, 0.32368100685760637) , + rgb (0.24688226237959, 0.06905363944920445, 0.31786993834254956) , + rgb (0.24367372557466271, 0.06841985515092269, 0.3121652405088837) , + rgb (0.2405081333229594, 0.0678571038148556, 0.3065705449367832) , + rgb (0.23739062429054825, 0.06736588805055552, 0.3010892218406587) , + rgb (0.23433055727563878, 0.0669355996616394, 0.295740099298676) , + rgb (0.23132955273021344, 0.06657618693909059, 0.29051361067988485) , + rgb (0.2283917709422868, 0.06628997924139618, 0.28541074411068496) , + rgb (0.22552164337737857, 0.0660781731193956, 0.28043398847505197) , + rgb (0.22272706739121817, 0.06593379067565194, 0.275597146520537) , + rgb (0.22001251100779617, 0.0658579189189076, 0.2709027999432586) , + rgb (0.21737845072382705, 0.06585966123356204, 0.2663420934966951) , + rgb (0.21482843531473683, 0.06594038561377849, 0.26191675992376573) , + rgb (0.21237411048541005, 0.06608502466175845, 0.2576516509356954) , + rgb (0.21001214221188125, 0.06630857391894718, 0.2535289048041211) , + rgb (0.2077442377448806, 0.06661453200418091, 0.24954644291943817) , + rgb (0.20558051999470117, 0.06699046239786874, 0.24572497420147632) , + rgb (0.20352007949514977, 0.06744417961242422, 0.24205576625191821) , + rgb (0.2015613376412984, 0.06798327102620025, 0.23852974228695395) , + rgb (0.19971571438603364, 0.06859271055370472, 0.23517094067076993) , + rgb (0.19794834061899208, 0.06931406607166066, 0.23194647381302336) , + rgb (0.1960826032659409, 0.07032122724242362, 0.22874673279569585) , + rgb (0.19410351363791453, 0.07160830485689157, 0.22558727307410353) , + rgb (0.19199449184606268, 0.0731828306492733, 0.22243385243433622) , + rgb (0.18975853639094634, 0.07501986186214377, 0.2193005075652994) , + rgb (0.18739228342697645, 0.07710209689958833, 0.21618875376309582) , + rgb (0.18488035509396164, 0.07942573027972388, 0.21307651648984993) , + rgb (0.18774482037046955, 0.07725158846803931, 0.21387448578597812) , + rgb (0.19049578401722037, 0.07531127841678764, 0.2146562337112265) , + rgb (0.1931548636579131, 0.07360681904011795, 0.21542362939081539) , + rgb (0.19571853588267552, 0.07215778103960274, 0.21617499187076789) , + rgb (0.19819343656336558, 0.07097462525273879, 0.21690975060032436) , + rgb (0.20058760685133747, 0.07006457614998421, 0.21762721310371608) , + rgb (0.20290365333558247, 0.06943524858045896, 0.21833167885096033) , + rgb (0.20531725273301316, 0.06891959226639757, 0.21911516689288835) , + rgb (0.20785704662965598, 0.06848439879702528, 0.22000133917653536) , + rgb (0.21052882914958676, 0.06812195249816172, 0.22098759107715404) , + rgb (0.2133313859647627, 0.06783014842602667, 0.2220704321302429) , + rgb (0.21625279838647882, 0.06761633027051639, 0.22324568672294431) , + rgb (0.21930503925136402, 0.06746578636294004, 0.22451023616807558) , + rgb (0.22247308588973624, 0.06738821405309284, 0.22585960379408354) , + rgb (0.2257539681670791, 0.06738213230014747, 0.22728984778098055) , + rgb (0.2291562027859284, 0.06743473087115257, 0.22879681433956656) , + rgb (0.23266299920501882, 0.06755710438847978, 0.23037617493752832) , + rgb (0.23627495835774248, 0.06774359820987802, 0.23202360805926608) , + rgb (0.23999586188690308, 0.06798502996477995, 0.23373434258507808) , + rgb (0.2438114972024792, 0.06828985152901187, 0.23550427698321885) , + rgb (0.247720929905011, 0.06865333790948652, 0.2373288009471749) , + rgb (0.25172899728289466, 0.0690646308260355, 0.23920260612763083) , + rgb (0.2558213554748177, 0.06953231029187984, 0.24112190491594204) , + rgb (0.25999463887892144, 0.07005385560386188, 0.24308218808684579) , + rgb (0.2642551220706094, 0.07061659562299544, 0.24507758869355967) , + rgb (0.2685909594817286, 0.07122671627792246, 0.24710443563450618) , + rgb (0.272997015188973, 0.07188355544616351, 0.2491584709323293) , + rgb (0.277471508091428, 0.07258296989925478, 0.2512349399594277) , + rgb (0.2820174629736694, 0.07331569321404097, 0.25332800295084507) , + rgb (0.28662309235899847, 0.07408846082680887, 0.2554347867371703) , + rgb (0.29128515387578635, 0.0748990498474667, 0.25755101595750435) , + rgb (0.2960004726065818, 0.07574533600095842, 0.25967245030364566) , + rgb (0.3007727681291869, 0.07661782433616476, 0.2617929409781967) , + rgb (0.30559226007249934, 0.07752196310753731, 0.2639100669211966) , + rgb (0.31045520848595526, 0.07845687167618218, 0.2660200572779356) , + rgb (0.3153587000920581, 0.07942099731524319, 0.2681190407694196) , + rgb (0.3202998655799406, 0.08041299473755484, 0.2702032289303951) , + rgb (0.3252788886040126, 0.08142839007654609, 0.27226772884656186) , + rgb (0.3302917447118144, 0.08246763389003825, 0.27430929404579435) , + rgb (0.3353335322445545, 0.08353243411900396, 0.2763253435679004) , + rgb (0.34040164359597463, 0.08462223619170267, 0.27831254595259397) , + rgb (0.345493557138718, 0.08573665496512634, 0.28026769921081435) , + rgb (0.3506067824603248, 0.08687555176033529, 0.28218770540182386) , + rgb (0.35573889947341125, 0.08803897435024335, 0.2840695897279818) , + rgb (0.36088752387578377, 0.0892271943627452, 0.28591050458531014) , + rgb (0.36605031412464006, 0.0904406854276979, 0.2877077458811747) , + rgb (0.3712250843130934, 0.09167999748026273, 0.2894586539763317) , + rgb (0.3764103053221462, 0.09294519809377791, 0.2911602415731392) , + rgb (0.38160247377467543, 0.09423873126371218, 0.2928110750626949) , + rgb (0.3867993907954417, 0.09556181960083443, 0.29440901248173756) , + rgb (0.39199887556812907, 0.09691583650296684, 0.2959521200550908) , + rgb (0.39719876876325577, 0.09830232096827862, 0.2974385647628578) , + rgb (0.40239692379737496, 0.09972293031495055, 0.2988667436973397) , + rgb (0.4075912039268871, 0.10117945586419633, 0.300235195077286) , + rgb (0.41277985630360303, 0.1026734006932461, 0.3015422643746897) , + rgb (0.41796105205173684, 0.10420644885760968, 0.3027865203963184) , + rgb (0.42313214269556043, 0.10578120994917611, 0.3039675809469457) , + rgb (0.4282910131578975, 0.1073997763055258, 0.30508479060294547) , + rgb (0.4334355841041439, 0.1090642347484701, 0.3061376792828915) , + rgb (0.4385637818793154, 0.11077667828375456, 0.30712600062348083) , + rgb (0.44367358645071275, 0.11253912421257944, 0.3080497309546545) , + rgb (0.4487629917317482, 0.1143535557462255, 0.30890905921943196) , + rgb (0.4538300508699989, 0.11622183788331528, 0.3097044124984492) , + rgb (0.45887288947308297, 0.11814571137706886, 0.3104363697903881) , + rgb (0.46389102840284874, 0.12012561256850712, 0.31110343446582983) , + rgb (0.46888111384598413, 0.12216445576414045, 0.31170911458932665) , + rgb (0.473841437035254, 0.12426354237989065, 0.31225470169927194) , + rgb (0.47877034239726296, 0.12642401401409453, 0.3127417273582196) , + rgb (0.48366628618847957, 0.1286467902201389, 0.31317188565991266) , + rgb (0.48852847371852987, 0.13093210934893723, 0.31354553695453014) , + rgb (0.49335504375145617, 0.13328091630401023, 0.31386561956734976) , + rgb (0.4981443546207415, 0.13569380302451714, 0.314135190862664) , + rgb (0.5028952497497061, 0.13817086581280427, 0.3143566215383367) , + rgb (0.5076068118105369, 0.14071192654913128, 0.3145320012008257) , + rgb (0.5122783510532176, 0.14331656120063752, 0.3146630922831542) , + rgb (0.5169084880054446, 0.14598463068714407, 0.3147540759228004) , + rgb (0.5214965286322996, 0.14871544765633712, 0.3148076795453443) , + rgb (0.5260418962547748, 0.15150818660835483, 0.31482653406646727) , + rgb (0.5305442048985645, 0.15436183633886777, 0.3148129978918713) , + rgb (0.5350027976174474, 0.15727540775107324, 0.3147708520739653) , + rgb (0.5394173664919906, 0.16024769309971934, 0.31470295028655965) , + rgb (0.5437877131360856, 0.16327738551419116, 0.31461204226295625) , + rgb (0.5481137003346762, 0.1663630904279047, 0.3145010299091471) , + rgb (0.5523952157271191, 0.16950338809328983, 0.3143729155461537) , + rgb (0.5566322903496934, 0.17269677158182117, 0.31423043195101424) , + rgb (0.5608249903911717, 0.17594170887918095, 0.31407639883970623) , + rgb (0.564973435290177, 0.17923664950367169, 0.3139136046337036) , + rgb (0.5690778478401143, 0.18258004462335425, 0.3137444095679653) , + rgb (0.5731384575410787, 0.18597036007065024, 0.3135712686852) , + rgb (0.5771555081299204, 0.18940601489760422, 0.3133970433357208) , + rgb (0.5811293276158656, 0.19288548904692518, 0.3132239939418394) , + rgb (0.5850602439646688, 0.19640737049066315, 0.3130540116373273) , + rgb (0.5889486193554471, 0.19997020971775276, 0.31288922211590126) , + rgb (0.5927948053652026, 0.20357251410079796, 0.3127323483930494) , + rgb (0.5965991810912237, 0.207212956082026, 0.3125852303112123) , + rgb (0.6003621301041158, 0.21089030138947745, 0.3124493441041469) , + rgb (0.6040840169673274, 0.21460331490206347, 0.31232652641170694) , + rgb (0.6077652399481865, 0.21835070166659282, 0.312219032918702) , + rgb (0.6114062072731884, 0.22213124697023234, 0.3121288139643524) , + rgb (0.6150072323639137, 0.22594402043981826, 0.3120568068576574) , + rgb (0.6185686525887719, 0.2297879924917992, 0.3120046383872893) , + rgb (0.6220907982108261, 0.2336621873300741, 0.3119738327362739) , + rgb (0.6255741650043496, 0.23756535071152696, 0.3119669831491227) , + rgb (0.6290189201698587, 0.24149689191922535, 0.3119844719564572) , + rgb (0.6324253485421027, 0.24545598775548677, 0.3120276597462445) , + rgb (0.6357937104834237, 0.24944185818822678, 0.3120979395330059) , + rgb (0.6391243387840212, 0.2534536546198314, 0.3121968961206398) , + rgb (0.642417577481186, 0.257490519876798, 0.31232631707560987) , + rgb (0.6456734938264543, 0.2615520316161528, 0.31248673753935263) , + rgb (0.6488923016945825, 0.2656375533620908, 0.3126794181957019) , + rgb (0.652074172902773, 0.269746505252367, 0.3129056060581917) , + rgb (0.6552193260932713, 0.2738782665241015, 0.3131666792687211) , + rgb (0.6583280801134499, 0.2780321095766563, 0.3134643447952643) , + rgb (0.6614003753260178, 0.28220778870555907, 0.3137991292649849) , + rgb (0.6644363246987884, 0.2864048361425618, 0.31417223403606975) , + rgb (0.6674360376636913, 0.29062280081258873, 0.31458483752056837) , + rgb (0.670399595476762, 0.29486126309253047, 0.3150381395687221) , + rgb (0.6733272556481733, 0.29911962764489264, 0.3155337232398221) , + rgb (0.6762189792440975, 0.30339762792450425, 0.3160724937230589) , + rgb (0.6790747402815734, 0.30769497879760166, 0.31665545668946665) , + rgb (0.6818945715094452, 0.31201133280550686, 0.3172838048924495) , + rgb (0.6846785094249453, 0.3163463482122221, 0.31795870784057567) , + rgb (0.6874265643516962, 0.32069970535138104, 0.3186813762227769) , + rgb (0.6901389321505248, 0.32507091815606004, 0.319453323328983) , + rgb (0.6928154484676493, 0.32945984647042675, 0.3202754315314667) , + rgb (0.6954560834689112, 0.33386622163232865, 0.3211488430698579) , + rgb (0.6980608153581771, 0.3382897632604862, 0.3220747885521809) , + rgb (0.700629624772421, 0.34273019305341756, 0.32305449047765694) , + rgb (0.7031624945881415, 0.34718723719598, 0.32408913679491225) , + rgb (0.7056595112261009, 0.3516605297812094, 0.32518014084085567) , + rgb (0.7081205956842048, 0.356149855233803, 0.32632861885644465) , + rgb (0.7105456546582587, 0.36065500290840113, 0.3275357416278876) , + rgb (0.7129346683977347, 0.36517570519856757, 0.3288027427038317) , + rgb (0.7152876061484729, 0.3697117022522345, 0.3301308728723546) , + rgb (0.7176044490813385, 0.3742627271068619, 0.3315213862095893) , + rgb (0.7198852149054985, 0.37882848839337313, 0.332975552002454) , + rgb (0.7221299918421461, 0.3834086450896306, 0.33449469983585844) , + rgb (0.7243386564778159, 0.38800301593162145, 0.3360799596569183) , + rgb (0.7265112290022755, 0.3926113126792577, 0.3377325942005665) , + rgb (0.7286477385671655, 0.39723324476747235, 0.33945384341064017) , + rgb (0.7307482075484517, 0.401868526884681, 0.3412449533046818) , + rgb (0.7328127050626875, 0.4065168468778026, 0.3431071517341082) , + rgb (0.7348413359856494, 0.4111778700451951, 0.3450416947080907) , + rgb (0.7368342217358587, 0.4158512585029011, 0.347049785207584) , + rgb (0.7387914002459927, 0.4205367299231533, 0.34913260148542435) , + rgb (0.7407130161950609, 0.4252339389526239, 0.35129130890802607) , + rgb (0.7425992159973317, 0.42994254036133867, 0.3535270924537459) , + rgb (0.7444501867657067, 0.4346621718461711, 0.35584108091122535) , + rgb (0.7462661578916344, 0.439392450449735, 0.3582343914230064) , + rgb (0.7480473927555956, 0.44413297780351974, 0.36070813602540136) , + rgb (0.7497942054717047, 0.4488833348154881, 0.3632633755836028) , + rgb (0.7515068504589166, 0.45364314496866825, 0.36590112443835765) , + rgb (0.7531856636904657, 0.45841199172949604, 0.3686223664223477) , + rgb (0.7548310506695954, 0.46318942799460555, 0.3714280448394211) , + rgb (0.7564434157714071, 0.4679750143794846, 0.37431909037543515) , + rgb (0.7580232553845584, 0.4727682731566229, 0.3772963553109668) , + rgb (0.7595711110534006, 0.4775687122205708, 0.380360657784311) , + rgb (0.7610876378057071, 0.48237579130289127, 0.3835127572385229) , + rgb (0.7625733355405261, 0.48718906673415824, 0.38675335037837993) , + rgb (0.7640288560928866, 0.49200802533379656, 0.39008308392311997) , + rgb (0.7654549259333051, 0.4968321290972723, 0.3935025400011538) , + rgb (0.7668522895064389, 0.5016608471009063, 0.39701221751773474) , + rgb (0.768221765997353, 0.5064936237128791, 0.40061257089416885) , + rgb (0.7695642334401418, 0.5113298901696085, 0.4043039806968248) , + rgb (0.7708809196230247, 0.516168926434691, 0.40808667584648967) , + rgb (0.7721725722960555, 0.5210102658711383, 0.4119608998712287) , + rgb (0.7734402182988989, 0.5258533209345156, 0.41592679539764366) , + rgb (0.774684947460632, 0.5306974938477673, 0.4199844035696376) , + rgb (0.775907907306857, 0.5355421788246119, 0.42413367909988375) , + rgb (0.7771103295521099, 0.5403867491056124, 0.4283745037125848) , + rgb (0.7782934580763312, 0.545230594884266, 0.432706647838971) , + rgb (0.7794586273150664, 0.5500730841397727, 0.4371297985644476) , + rgb (0.7806077474948377, 0.5549133574489061, 0.4416433242636464) , + rgb (0.7817418047898184, 0.5597509805259486, 0.44624687186865436) , + rgb (0.7828622526444091, 0.5645853311116688, 0.45093985823706345) , + rgb (0.7839706083641448, 0.5694157832671042, 0.4557215474289206) , + rgb (0.7850684501960684, 0.5742417003617839, 0.46059116206904965) , + rgb (0.7861573713233296, 0.5790624629815756, 0.465547782819184) , + rgb (0.7872390410818835, 0.5838774374455721, 0.47059039582133383) , + rgb (0.7883151404562396, 0.5886860017356244, 0.4757179187907608) , + rgb (0.7893873776625194, 0.5934875421745599, 0.48092913815357724) , + rgb (0.7904577684772788, 0.5982813427706246, 0.48622257801969754) , + rgb (0.7915283284347561, 0.603066705931472, 0.49159667021646397) , + rgb (0.7926003430423745, 0.6078432208703702, 0.4970502062153201) , + rgb (0.7936755969866496, 0.6126102933407219, 0.5025816129126943) , + rgb (0.7947558597265404, 0.617367344002207, 0.5081892121310299) , + rgb (0.7958429237958377, 0.6221137880845115, 0.5138712409190979) , + rgb (0.7969385471995161, 0.626849056792967, 0.5196258425240281) , + rgb (0.7980444781513664, 0.6315725822508955, 0.5254510814483478) , + rgb (0.7991624518501963, 0.6362837937202919, 0.5313449594256143) , + rgb (0.8002941538975398, 0.6409821330674986, 0.5373053518514104) , + rgb (0.8014412429256005, 0.6456670345921877, 0.5433300863249918) , + rgb (0.8026053114611295, 0.6503379374810385, 0.5494169158460365) , + rgb (0.8037879253107763, 0.6549942654947263, 0.5555635086708381) , + rgb (0.804990547908103, 0.6596354502756416, 0.5617674511054698) , + rgb (0.8062146052692706, 0.6642608958528229, 0.5680262917864979) , + rgb (0.8074614045096935, 0.6688700095398864, 0.5743374637345958) , + rgb (0.8087321917008969, 0.6734621670219452, 0.5806983480557674) , + rgb (0.8100280946652069, 0.6780367267397182, 0.5871062690808275) , + rgb (0.8113501401176333, 0.6825930154624339, 0.5935584890905076) , + rgb (0.8126992203988149, 0.6871303371461888, 0.600052148204351) , + rgb (0.8140761104699334, 0.6916479479148213, 0.6065843782630862) , + rgb (0.8154814662727948, 0.6961450550830809, 0.6131522120932265) , + rgb (0.8169157577505589, 0.7006208301478398, 0.6197526063725792) , + rgb (0.8183793116449822, 0.705074381896351, 0.626382454789333) , + rgb (0.8198723065045529, 0.7095047497878748, 0.6330385704006711) , + rgb (0.8213947205565636, 0.7139109141951604, 0.6397176669767276) , + rgb (0.8229463511042843, 0.7182917733129006, 0.6464164243818421) , + rgb (0.8245268129450285, 0.7226461431208888, 0.653131379154226) , + rgb (0.8261354971058026, 0.7269727551823826, 0.659859001562165) , + rgb (0.8277716072353446, 0.7312702332407809, 0.6665957020468297) , + rgb (0.8294340781648147, 0.7355371221572935, 0.6733377200930191) , + rgb (0.8311216352909631, 0.7397718464763862, 0.6800812520363146) , + rgb (0.8328327718577798, 0.7439727181745988, 0.6868223587464855) , + rgb (0.8345656905566583, 0.7481379479992134, 0.6935569764986385) , + rgb (0.8363189884473793, 0.7522654895287526, 0.7002799902886496) , + rgb (0.8380912347613196, 0.7563531486080863, 0.7069856139021298) , + rgb (0.8398783988412087, 0.7603990719977968, 0.7136714781112923) , + rgb (0.8416775076684515, 0.7644010120098295, 0.7203329938728462) , + rgb (0.843485292229337, 0.7683566039987018, 0.7269653699897204) , + rgb (0.8452981073195511, 0.7722633860104472, 0.7335636824054149) , + rgb (0.847111955079651, 0.7761188023604716, 0.7401227576280706) , + rgb (0.8489224556311764, 0.7799202140765015, 0.7466371929366437) , + rgb (0.8507269702317879, 0.7836645734238389, 0.7530974636118285) , + rgb (0.8525190720770844, 0.7873493613354844, 0.7594994148789691) , + rgb (0.8542921961147046, 0.7909719677709199, 0.765838014779141) , + rgb (0.856040223147254, 0.7945296360155061, 0.7721061003767414) , + rgb (0.857756629435049, 0.7980196314271393, 0.778295716672475) , + rgb (0.8594346370300241, 0.8014392309950078, 0.7843978875138392) , + rgb (0.8610711702756552, 0.8047851790981223, 0.7903952966373629) , + rgb (0.8626560105112757, 0.8080552380426153, 0.796282666437655) , + rgb (0.8641834372394103, 0.8112464422465354, 0.8020461269686395) , + rgb (0.8656493432560532, 0.8143554406751491, 0.8076697232416455) , + rgb (0.867053149070485, 0.8173780404191124, 0.813134196269114) , + rgb (0.8683995469581863, 0.8203087551218152, 0.8184163896312899) , + rgb (0.8696913150261381, 0.8231415885956916, 0.8235047668317317) , + rgb (0.8709384671729751, 0.8258685788943851, 0.8283849726114961) , + rgb (0.8721533197845432, 0.8284805282370967, 0.8330486712880828) , + rgb (0.8733517136091627, 0.8309671525127262, 0.8374885100119709) , + rgb (0.8745379332026019, 0.8333197294864546, 0.8417192535806901) , + rgb (0.875714587099614, 0.8355302318472394, 0.8457553751902708) , + rgb (0.8768784845161469, 0.8375923807118654, 0.8496137354915025) , + rgb (0.8780229843664901, 0.8395016561854007, 0.8533064535245892) , + rgb (0.8791324424079277, 0.8412555488447591, 0.8568557229103964) , + rgb (0.8801929331569581, 0.8428522482477862, 0.8602739992715663) , + rgb (0.8811916987134195, 0.8442906671771735, 0.8635659516866988) , + rgb (0.8821154248940161, 0.8455700725455935, 0.8667376504623333) , + rgb (0.8829516859544853, 0.8466897027569927, 0.8697961704819097) , + rgb (0.8836912714589804, 0.8476489176151927, 0.8727414710144156) , + rgb (0.8843271305411354, 0.8484474157205542, 0.8755678522824297) , + rgb (0.8848513815990857, 0.849084264228938, 0.8782823528537247) , + rgb (0.8852589797263047, 0.8495589281098921, 0.8808841479402484) , + rgb (0.8855471481195238, 0.8498717428363158, 0.8833620612117095) , + rgb (0.8857115512284565, 0.8500218611585632, 0.8857253899008712) + }); + + +list_data twilight_shifted = list_data(new pen[] { + rgb (0.18739228342697645, 0.07710209689958833, 0.21618875376309582) , + rgb (0.18975853639094634, 0.07501986186214377, 0.2193005075652994) , + rgb (0.19199449184606268, 0.0731828306492733, 0.22243385243433622) , + rgb (0.19410351363791453, 0.07160830485689157, 0.22558727307410353) , + rgb (0.1960826032659409, 0.07032122724242362, 0.22874673279569585) , + rgb (0.19794834061899208, 0.06931406607166066, 0.23194647381302336) , + rgb (0.19971571438603364, 0.06859271055370472, 0.23517094067076993) , + rgb (0.2015613376412984, 0.06798327102620025, 0.23852974228695395) , + rgb (0.20352007949514977, 0.06744417961242422, 0.24205576625191821) , + rgb (0.20558051999470117, 0.06699046239786874, 0.24572497420147632) , + rgb (0.2077442377448806, 0.06661453200418091, 0.24954644291943817) , + rgb (0.21001214221188125, 0.06630857391894718, 0.2535289048041211) , + rgb (0.21237411048541005, 0.06608502466175845, 0.2576516509356954) , + rgb (0.21482843531473683, 0.06594038561377849, 0.26191675992376573) , + rgb (0.21737845072382705, 0.06585966123356204, 0.2663420934966951) , + rgb (0.22001251100779617, 0.0658579189189076, 0.2709027999432586) , + rgb (0.22272706739121817, 0.06593379067565194, 0.275597146520537) , + rgb (0.22552164337737857, 0.0660781731193956, 0.28043398847505197) , + rgb (0.2283917709422868, 0.06628997924139618, 0.28541074411068496) , + rgb (0.23132955273021344, 0.06657618693909059, 0.29051361067988485) , + rgb (0.23433055727563878, 0.0669355996616394, 0.295740099298676) , + rgb (0.23739062429054825, 0.06736588805055552, 0.3010892218406587) , + rgb (0.2405081333229594, 0.0678571038148556, 0.3065705449367832) , + rgb (0.24367372557466271, 0.06841985515092269, 0.3121652405088837) , + rgb (0.24688226237959, 0.06905363944920445, 0.31786993834254956) , + rgb (0.2501284530619938, 0.06975820642910699, 0.32368100685760637) , + rgb (0.25340685873736807, 0.07053358292685183, 0.3295945757321303) , + rgb (0.256711916510839, 0.07138010624208224, 0.3356064898460009) , + rgb (0.26003895187439957, 0.0722947810433622, 0.3417175669546984) , + rgb (0.26338121807151404, 0.07328165793989708, 0.34791888996380105) , + rgb (0.26673233211995284, 0.0743440180285462, 0.3542027606624096) , + rgb (0.2700863774911405, 0.07548367558427554, 0.36056376228111864) , + rgb (0.2734373934245081, 0.07670280023749607, 0.36699616136347685) , + rgb (0.2767793961581559, 0.07800394103378822, 0.37349382846504675) , + rgb (0.2801063857697547, 0.07938999480226153, 0.38005028528138707) , + rgb (0.28341239797185225, 0.08086415336549937, 0.38665868550105914) , + rgb (0.28669151388283165, 0.08242987384848069, 0.39331182532243375) , + rgb (0.2899379244517661, 0.08409078829085731, 0.40000214725256295) , + rgb (0.2931459309698525, 0.08585065688962071, 0.40672178082365834) , + rgb (0.2963100038890529, 0.08771325096046395, 0.41346259134143476) , + rgb (0.2994248396021477, 0.08968225371675004, 0.42021619665853854) , + rgb (0.3024853636457425, 0.0917611873973036, 0.42697404043749887) , + rgb (0.30548675819945936, 0.09395276484082374, 0.4337284999936111) , + rgb (0.30842444457210105, 0.09625938534057774, 0.44047194882050544) , + rgb (0.31129434479712365, 0.0986847719340522, 0.4471931371516162) , + rgb (0.3140927841475553, 0.10123077676403242, 0.45388335612058467) , + rgb (0.316816480890235, 0.10389861387653518, 0.46053414662739794) , + rgb (0.31946262395497965, 0.1066887988239266, 0.46713728801395243) , + rgb (0.3220288227606932, 0.10960114111258401, 0.4736849472572688) , + rgb (0.32451307931207785, 0.11263459791730848, 0.48017007211645196) , + rgb (0.3269137124539796, 0.1157873496841953, 0.4865864649569746) , + rgb (0.3292300520323141, 0.11905764321981127, 0.49292595612342666) , + rgb (0.33146154891145124, 0.12244245263391232, 0.4991827458843107) , + rgb (0.33360804901486, 0.1259381830100592, 0.505351647966549) , + rgb (0.33566978565015315, 0.12954074251271822, 0.5114280721516895) , + rgb (0.3376473209067111, 0.13324562282438077, 0.5174080757397947) , + rgb (0.33954168752669694, 0.1370480189671817, 0.5232879753508524) , + rgb (0.34135411074506483, 0.1409427920655632, 0.5290650094033675) , + rgb (0.34308600291572294, 0.14492465359918028, 0.534737042820671) , + rgb (0.34473901574536375, 0.1489882058982641, 0.5403024592040654) , + rgb (0.3463150717579309, 0.15312802296627787, 0.5457599924248665) , + rgb (0.3478165323877778, 0.1573386351115298, 0.5511085345270326) , + rgb (0.34924513554955644, 0.16161477763045118, 0.5563483747416378) , + rgb (0.3506030444193101, 0.1659512998472086, 0.5614796470399323) , + rgb (0.3518924860887379, 0.17034320478524959, 0.5665028491121665) , + rgb (0.35311574421123737, 0.17478570377561287, 0.5714187152355529) , + rgb (0.3542753496066376, 0.17927413271618647, 0.5762280966066872) , + rgb (0.3553741530690672, 0.18380392577704466, 0.580931914318328) , + rgb (0.35641381143126327, 0.18837119869242164, 0.5855320765920552) , + rgb (0.35739663292915563, 0.1929720819784246, 0.5900301125106313) , + rgb (0.3583248996661781, 0.197602942459778, 0.5944276851750107) , + rgb (0.35920088560930186, 0.20226037920758122, 0.5987265295935138) , + rgb (0.36002681809096376, 0.20694122817889948, 0.6029284543191687) , + rgb (0.36080493826624654, 0.21164251793458128, 0.6070353217206471) , + rgb (0.36153829010998356, 0.21636111429594002, 0.6110488067964093) , + rgb (0.36222809558295926, 0.22109488427338303, 0.6149711234609613) , + rgb (0.3628764356004103, 0.2258414929328703, 0.6188041741082302) , + rgb (0.36348537610385145, 0.2305987621839642, 0.6225498862239288) , + rgb (0.3640569302208851, 0.23536470386204195, 0.6262101345195302) , + rgb (0.3645930889012501, 0.24013747024823828, 0.629786801550261) , + rgb (0.3650957984588681, 0.24491536803550484, 0.6332817352005559) , + rgb (0.3655669837353898, 0.24969683475296395, 0.6366967518748858) , + rgb (0.36600853966739794, 0.25448043878086224, 0.6400336180336859) , + rgb (0.3664224325155063, 0.25926481158628106, 0.6432940914076554) , + rgb (0.3668108554010799, 0.26404857724525643, 0.6464799165290824) , + rgb (0.36717513650699446, 0.26883085667326956, 0.6495927229789225) , + rgb (0.36751707094367697, 0.2736106331709098, 0.6526341171161864) , + rgb (0.3678384369653108, 0.2783869718129776, 0.655605668384537) , + rgb (0.3681410147989972, 0.2831590122118299, 0.6585088880697231) , + rgb (0.36842655638020444, 0.2879259643777846, 0.661345269109446) , + rgb (0.3686968223189527, 0.292687098561501, 0.6641162529823691) , + rgb (0.3689535530659678, 0.29744175492366276, 0.6668232208982426) , + rgb (0.36919847837592484, 0.3021893217650707, 0.6694675433161452) , + rgb (0.3694333459127623, 0.3069292355186234, 0.6720505284912062) , + rgb (0.36965987626565955, 0.3116609876295197, 0.6745734474341955) , + rgb (0.3698798032902536, 0.31638410101153364, 0.6770375543809057) , + rgb (0.37009487130772695, 0.3210981375964933, 0.6794440539905685) , + rgb (0.37030682071842685, 0.32580269697872455, 0.6817941168448668) , + rgb (0.37051738634484427, 0.3304974124430785, 0.6840888878885721) , + rgb (0.3707283327942267, 0.33518193808489577, 0.6863294816960677) , + rgb (0.3709415155133733, 0.33985591488818123, 0.6885170337950512) , + rgb (0.37115856636209105, 0.3445191141023017, 0.6906525358646499) , + rgb (0.37138124223736607, 0.34917126878479027, 0.6927370347192883) , + rgb (0.3716113323440048, 0.3538121372967869, 0.6947714991938089) , + rgb (0.3718506155898596, 0.3584414828587522, 0.6967569581025654) , + rgb (0.3721008970244382, 0.3630590973698238, 0.6986943461507372) , + rgb (0.37236397858465387, 0.36766477862108266, 0.7005846349652077) , + rgb (0.37264166757849604, 0.3722583500483685, 0.7024287314570723) , + rgb (0.3729357864666503, 0.3768396383521984, 0.7042275578058994) , + rgb (0.37324816143326384, 0.38140848555753937, 0.7059820097480604) , + rgb (0.3735806215098284, 0.3859647438605754, 0.7076929760731058) , + rgb (0.3739349933047578, 0.3905082752937583, 0.7093613429347845) , + rgb (0.3743131319931234, 0.3950389482828331, 0.7109879652237746) , + rgb (0.3747168601930223, 0.3995566498711684, 0.7125736851650046) , + rgb (0.37514802505380473, 0.4040612609993941, 0.7141193695725726) , + rgb (0.375608469194424, 0.40855267638072096, 0.7156258509158755) , + rgb (0.37610001286385814, 0.4130307995247706, 0.7170939691992023) , + rgb (0.37662448930806297, 0.41749553747893614, 0.7185245473617611) , + rgb (0.3771837184425123, 0.4219468022345483, 0.7199184152447577) , + rgb (0.3777794975351373, 0.4263845142616835, 0.7212763999353023) , + rgb (0.378413635091359, 0.43080859411413064, 0.7225993199306104) , + rgb (0.3790878928311076, 0.43521897612544935, 0.7238879869132313) , + rgb (0.3798040374484875, 0.4396155882122263, 0.7251432377876109) , + rgb (0.3805638069656562, 0.4439983720863372, 0.7263658704513531) , + rgb (0.3813688793045416, 0.4483672766927786, 0.7275567131714135) , + rgb (0.38222094988570376, 0.45272225034283325, 0.7287165614400378) , + rgb (0.3831216808440275, 0.457063235814072, 0.7298462679135326) , + rgb (0.38407269378943537, 0.46139018782416635, 0.7309466543290268) , + rgb (0.3850755679365139, 0.46570306719930193, 0.732018540336905) , + rgb (0.386131841788921, 0.4700018340988123, 0.7330627749243106) , + rgb (0.3872430145933025, 0.4742864593363539, 0.7340801678785439) , + rgb (0.3884105330084243, 0.47855691131792805, 0.7350715764115726) , + rgb (0.38963580160340855, 0.48281316715123496, 0.7360378254693274) , + rgb (0.390920175719949, 0.4870552025122304, 0.7369797713388125) , + rgb (0.39226494876209317, 0.4912830033289926, 0.7378982478447508) , + rgb (0.39367135736822567, 0.4954965577745118, 0.738794102296364) , + rgb (0.3951405880820763, 0.4996958532637776, 0.7396682021171571) , + rgb (0.3966737490566561, 0.5038808905384797, 0.7405213858051674) , + rgb (0.3982719152586337, 0.5080516653927614, 0.7413545091809972) , + rgb (0.39993606933454834, 0.5122081814321852, 0.7421684457131799) , + rgb (0.40166714010896104, 0.5163504496968876, 0.7429640345324835) , + rgb (0.40346600333905397, 0.5204784765384003, 0.7437421522356709) , + rgb (0.4053334378930318, 0.5245922817498312, 0.7445036583670813) , + rgb (0.4072701869421907, 0.5286918801105741, 0.7452494263758127) , + rgb (0.4092768899914751, 0.5327773017713032, 0.7459803063570782) , + rgb (0.4113541469730457, 0.5368485776500593, 0.7466971285506578) , + rgb (0.4135024574331473, 0.5409057477109848, 0.7474007329754309) , + rgb (0.4157223260454232, 0.544948827153504, 0.7480920445900052) , + rgb (0.4180141407923363, 0.5489778542188889, 0.7487719316700112) , + rgb (0.42037822361396326, 0.5529928715810817, 0.7494412559451894) , + rgb (0.4228148567577266, 0.5569939212699658, 0.7501008698822764) , + rgb (0.42532423665011354, 0.560981049599503, 0.7507516498900512) , + rgb (0.42790652284925834, 0.5649543060909209, 0.7513944352191484) , + rgb (0.4305617907305757, 0.5689137457245718, 0.752030080993127) , + rgb (0.43329008867358393, 0.5728594162560667, 0.7526594653256667) , + rgb (0.4360913895835637, 0.5767913799818608, 0.7532834105961016) , + rgb (0.43896563958048396, 0.5807096924109849, 0.7539027620828594) , + rgb (0.441912717666964, 0.5846144110017557, 0.7545183888441067) , + rgb (0.4449324685421538, 0.5885055998308617, 0.7551311041857901) , + rgb (0.4480247093358917, 0.5923833145214658, 0.7557417647410792) , + rgb (0.45118918687617743, 0.5962476205135354, 0.7563512064324664) , + rgb (0.45442563977552913, 0.6000985950385866, 0.7569601366060649) , + rgb (0.45773377230160567, 0.6039363004658646, 0.7575693690185916) , + rgb (0.4611132664702388, 0.607760777169989, 0.7581798643680714) , + rgb (0.4645637671630393, 0.6115720882286415, 0.7587924262302581) , + rgb (0.46808490970531597, 0.6153702869579029, 0.7594078989109274) , + rgb (0.4716762951887709, 0.6191554324288464, 0.7600270922788305) , + rgb (0.47533752394906287, 0.6229275728383581, 0.7606508557181775) , + rgb (0.47906816236197386, 0.6266867625186013, 0.7612800037566242) , + rgb (0.4828677867260272, 0.6304330455306234, 0.761915371498953) , + rgb (0.4867359599430568, 0.6341664625110051, 0.7625578008592404) , + rgb (0.4906722493856122, 0.6378870485884708, 0.7632081276316384) , + rgb (0.4946761847863938, 0.6415948411950443, 0.7638671900213091) , + rgb (0.4987473366135607, 0.6452898684900934, 0.7645357873418008) , + rgb (0.5028853540415561, 0.6489721673409526, 0.7652144671817491) , + rgb (0.5070896957645166, 0.6526417240314645, 0.7659044566083585) , + rgb (0.5113599254160193, 0.6562985398183186, 0.7666066378064533) , + rgb (0.5156955988596057, 0.65994260812898, 0.7673219148959617) , + rgb (0.5200962739223556, 0.6635739143403039, 0.768051194033441) , + rgb (0.5245615147059358, 0.6671924299614223, 0.7687954171423095) , + rgb (0.529090861832473, 0.6707981230280622, 0.7695555229231313) , + rgb (0.533683891477284, 0.6743909370521273, 0.7703325054879735) , + rgb (0.5383401557517628, 0.677970811290954, 0.7711273443905772) , + rgb (0.5430593242401823, 0.6815376725306588, 0.7719407969783508) , + rgb (0.5478409815301863, 0.6850914221850988, 0.7727738647344087) , + rgb (0.5526845058934713, 0.6886319451516638, 0.7736279426902245) , + rgb (0.5575894041960517, 0.6921591145825405, 0.7745041337932782) , + rgb (0.5625551535721934, 0.6956727838162965, 0.7754035914230984) , + rgb (0.5675811785386197, 0.6991727930264627, 0.776327485342753) , + rgb (0.5726668948158774, 0.7026589535425779, 0.7772770268091199) , + rgb (0.5778116438998202, 0.7061310615715398, 0.7782534512102552) , + rgb (0.5830148703693241, 0.7095888767699747, 0.7792578182047659) , + rgb (0.588275797805555, 0.7130321464645814, 0.7802914140563652) , + rgb (0.5935933569683722, 0.7164606049658685, 0.781355882376401) , + rgb (0.5989665814482068, 0.7198739489224673, 0.7824525989934664) , + rgb (0.6043943420027486, 0.7232718614323369, 0.7835829559353559) , + rgb (0.6098754317609306, 0.7266539875975829, 0.7847483573269471) , + rgb (0.6154084641177048, 0.7300199523273969, 0.7859502270675048) , + rgb (0.6209919306481755, 0.733369347989232, 0.7871899462469658) , + rgb (0.6266240202260459, 0.7367017540369944, 0.7884690131633456) , + rgb (0.6323027138710603, 0.740016721601314, 0.7897889276264043) , + rgb (0.6380258682854598, 0.7433137671403319, 0.7911510045957317) , + rgb (0.6437910831099849, 0.7465923800833657, 0.7925565320130605) , + rgb (0.6495957300425348, 0.7498520122194177, 0.7940067402149911) , + rgb (0.6554369232645472, 0.7530920875676843, 0.7955027112903105) , + rgb (0.6613112930078745, 0.7563120270871903, 0.7970456043491897) , + rgb (0.6672147980375281, 0.7595112803730375, 0.7986367465453776) , + rgb (0.6731442255942621, 0.7626890873389048, 0.8002762854580953) , + rgb (0.6790955449988215, 0.765844721313959, 0.8019646617300199) , + rgb (0.6850644615439593, 0.7689774029354699, 0.8037020626717691) , + rgb (0.691046410093091, 0.7720862946067809, 0.8054884169067907) , + rgb (0.6970365443886174, 0.7751705000806606, 0.8073233538006345) , + rgb (0.7030297722540817, 0.7782290497335813, 0.8092061884805697) , + rgb (0.709020781345393, 0.7812608871607091, 0.8111359185511793) , + rgb (0.7150040307625213, 0.7842648709158119, 0.8131111655994991) , + rgb (0.7209728066553678, 0.7872399592345264, 0.8151307392087926) , + rgb (0.726921775128297, 0.7901846863592763, 0.8171921746672638) , + rgb (0.7328454364552346, 0.7930974646884407, 0.8192926338423038) , + rgb (0.7387377170049494, 0.7959766573503101, 0.8214292278043301) , + rgb (0.7445924777189017, 0.7988204771958325, 0.8235986758615652) , + rgb (0.750403467654067, 0.8016269900896532, 0.8257973785108242) , + rgb (0.7561644358438198, 0.8043940873347794, 0.8280213899472) , + rgb (0.7618690793798029, 0.8071194938764749, 0.830266486168872) , + rgb (0.7675110850441786, 0.8098007598713145, 0.8325281663805967) , + rgb (0.7730841659017094, 0.8124352473546601, 0.8348017295057968) , + rgb (0.7785789200822759, 0.8150208937874255, 0.8370834463093898) , + rgb (0.7839910104276492, 0.8175542640053343, 0.8393674746403673) , + rgb (0.7893144556460892, 0.8200321318870201, 0.8416486380471222) , + rgb (0.7945430508923111, 0.8224511622630462, 0.8439218478682798) , + rgb (0.79967075421268, 0.8248078181208093, 0.8461821002957853) , + rgb (0.8046916442981458, 0.8270983878056066, 0.8484244929697402) , + rgb (0.8095999819094809, 0.8293189667350546, 0.8506444160105037) , + rgb (0.8143898212114309, 0.8314655869419785, 0.8528375906214702) , + rgb (0.8190465467793753, 0.8335364929949034, 0.855002062870101) , + rgb (0.8235742968025285, 0.8355248776479544, 0.8571319132851495) , + rgb (0.8279689431601354, 0.837426007513952, 0.8592239945130679) , + rgb (0.8322270571293441, 0.8392349062775448, 0.8612756350042788) , + rgb (0.8363403180919118, 0.8409479651895194, 0.8632852800107016) , + rgb (0.8403042080595778, 0.8425605950855084, 0.865250882410458) , + rgb (0.8441261828674842, 0.8440648271103739, 0.8671697332269007) , + rgb (0.8478071070257792, 0.8454546229209523, 0.8690403678369444) , + rgb (0.8513371457085719, 0.8467273579611647, 0.8708608165735044) , + rgb (0.8547259318925698, 0.8478748812467282, 0.8726282980930582) , + rgb (0.8579825924567037, 0.8488893481028184, 0.8743403855344628) , + rgb (0.8611024543689985, 0.8497675485700126, 0.8759924292343957) , + rgb (0.86408985081464, 0.8505039116750779, 0.8775792578489263) , + rgb (0.8669601550533358, 0.8510896085314068, 0.8790976697717334) , + rgb (0.8697047485350982, 0.8515240300479789, 0.8805388339000336) , + rgb (0.8723313408512408, 0.8518016547808089, 0.8818970435500162) , + rgb (0.8748534750857597, 0.8519152612302319, 0.8831692696761383) , + rgb (0.8772488085896548, 0.8518702833887027, 0.8843412038131143) , + rgb (0.8795410528270573, 0.8516567540749572, 0.8854143767924102) , + rgb (0.8817223105928579, 0.8512759407765347, 0.8863805692551482) , + rgb (0.8837852019553906, 0.8507294054031063, 0.8872322209694989) , + rgb (0.8857501584075443, 0.8500092494306783, 0.8879736506427196) , + rgb (0.8857115512284565, 0.8500218611585632, 0.8857253899008712) , + rgb (0.8855471481195238, 0.8498717428363158, 0.8833620612117095) , + rgb (0.8852589797263047, 0.8495589281098921, 0.8808841479402484) , + rgb (0.8848513815990857, 0.849084264228938, 0.8782823528537247) , + rgb (0.8843271305411354, 0.8484474157205542, 0.8755678522824297) , + rgb (0.8836912714589804, 0.8476489176151927, 0.8727414710144156) , + rgb (0.8829516859544853, 0.8466897027569927, 0.8697961704819097) , + rgb (0.8821154248940161, 0.8455700725455935, 0.8667376504623333) , + rgb (0.8811916987134195, 0.8442906671771735, 0.8635659516866988) , + rgb (0.8801929331569581, 0.8428522482477862, 0.8602739992715663) , + rgb (0.8791324424079277, 0.8412555488447591, 0.8568557229103964) , + rgb (0.8780229843664901, 0.8395016561854007, 0.8533064535245892) , + rgb (0.8768784845161469, 0.8375923807118654, 0.8496137354915025) , + rgb (0.875714587099614, 0.8355302318472394, 0.8457553751902708) , + rgb (0.8745379332026019, 0.8333197294864546, 0.8417192535806901) , + rgb (0.8733517136091627, 0.8309671525127262, 0.8374885100119709) , + rgb (0.8721533197845432, 0.8284805282370967, 0.8330486712880828) , + rgb (0.8709384671729751, 0.8258685788943851, 0.8283849726114961) , + rgb (0.8696913150261381, 0.8231415885956916, 0.8235047668317317) , + rgb (0.8683995469581863, 0.8203087551218152, 0.8184163896312899) , + rgb (0.867053149070485, 0.8173780404191124, 0.813134196269114) , + rgb (0.8656493432560532, 0.8143554406751491, 0.8076697232416455) , + rgb (0.8641834372394103, 0.8112464422465354, 0.8020461269686395) , + rgb (0.8626560105112757, 0.8080552380426153, 0.796282666437655) , + rgb (0.8610711702756552, 0.8047851790981223, 0.7903952966373629) , + rgb (0.8594346370300241, 0.8014392309950078, 0.7843978875138392) , + rgb (0.857756629435049, 0.7980196314271393, 0.778295716672475) , + rgb (0.856040223147254, 0.7945296360155061, 0.7721061003767414) , + rgb (0.8542921961147046, 0.7909719677709199, 0.765838014779141) , + rgb (0.8525190720770844, 0.7873493613354844, 0.7594994148789691) , + rgb (0.8507269702317879, 0.7836645734238389, 0.7530974636118285) , + rgb (0.8489224556311764, 0.7799202140765015, 0.7466371929366437) , + rgb (0.847111955079651, 0.7761188023604716, 0.7401227576280706) , + rgb (0.8452981073195511, 0.7722633860104472, 0.7335636824054149) , + rgb (0.843485292229337, 0.7683566039987018, 0.7269653699897204) , + rgb (0.8416775076684515, 0.7644010120098295, 0.7203329938728462) , + rgb (0.8398783988412087, 0.7603990719977968, 0.7136714781112923) , + rgb (0.8380912347613196, 0.7563531486080863, 0.7069856139021298) , + rgb (0.8363189884473793, 0.7522654895287526, 0.7002799902886496) , + rgb (0.8345656905566583, 0.7481379479992134, 0.6935569764986385) , + rgb (0.8328327718577798, 0.7439727181745988, 0.6868223587464855) , + rgb (0.8311216352909631, 0.7397718464763862, 0.6800812520363146) , + rgb (0.8294340781648147, 0.7355371221572935, 0.6733377200930191) , + rgb (0.8277716072353446, 0.7312702332407809, 0.6665957020468297) , + rgb (0.8261354971058026, 0.7269727551823826, 0.659859001562165) , + rgb (0.8245268129450285, 0.7226461431208888, 0.653131379154226) , + rgb (0.8229463511042843, 0.7182917733129006, 0.6464164243818421) , + rgb (0.8213947205565636, 0.7139109141951604, 0.6397176669767276) , + rgb (0.8198723065045529, 0.7095047497878748, 0.6330385704006711) , + rgb (0.8183793116449822, 0.705074381896351, 0.626382454789333) , + rgb (0.8169157577505589, 0.7006208301478398, 0.6197526063725792) , + rgb (0.8154814662727948, 0.6961450550830809, 0.6131522120932265) , + rgb (0.8140761104699334, 0.6916479479148213, 0.6065843782630862) , + rgb (0.8126992203988149, 0.6871303371461888, 0.600052148204351) , + rgb (0.8113501401176333, 0.6825930154624339, 0.5935584890905076) , + rgb (0.8100280946652069, 0.6780367267397182, 0.5871062690808275) , + rgb (0.8087321917008969, 0.6734621670219452, 0.5806983480557674) , + rgb (0.8074614045096935, 0.6688700095398864, 0.5743374637345958) , + rgb (0.8062146052692706, 0.6642608958528229, 0.5680262917864979) , + rgb (0.804990547908103, 0.6596354502756416, 0.5617674511054698) , + rgb (0.8037879253107763, 0.6549942654947263, 0.5555635086708381) , + rgb (0.8026053114611295, 0.6503379374810385, 0.5494169158460365) , + rgb (0.8014412429256005, 0.6456670345921877, 0.5433300863249918) , + rgb (0.8002941538975398, 0.6409821330674986, 0.5373053518514104) , + rgb (0.7991624518501963, 0.6362837937202919, 0.5313449594256143) , + rgb (0.7980444781513664, 0.6315725822508955, 0.5254510814483478) , + rgb (0.7969385471995161, 0.626849056792967, 0.5196258425240281) , + rgb (0.7958429237958377, 0.6221137880845115, 0.5138712409190979) , + rgb (0.7947558597265404, 0.617367344002207, 0.5081892121310299) , + rgb (0.7936755969866496, 0.6126102933407219, 0.5025816129126943) , + rgb (0.7926003430423745, 0.6078432208703702, 0.4970502062153201) , + rgb (0.7915283284347561, 0.603066705931472, 0.49159667021646397) , + rgb (0.7904577684772788, 0.5982813427706246, 0.48622257801969754) , + rgb (0.7893873776625194, 0.5934875421745599, 0.48092913815357724) , + rgb (0.7883151404562396, 0.5886860017356244, 0.4757179187907608) , + rgb (0.7872390410818835, 0.5838774374455721, 0.47059039582133383) , + rgb (0.7861573713233296, 0.5790624629815756, 0.465547782819184) , + rgb (0.7850684501960684, 0.5742417003617839, 0.46059116206904965) , + rgb (0.7839706083641448, 0.5694157832671042, 0.4557215474289206) , + rgb (0.7828622526444091, 0.5645853311116688, 0.45093985823706345) , + rgb (0.7817418047898184, 0.5597509805259486, 0.44624687186865436) , + rgb (0.7806077474948377, 0.5549133574489061, 0.4416433242636464) , + rgb (0.7794586273150664, 0.5500730841397727, 0.4371297985644476) , + rgb (0.7782934580763312, 0.545230594884266, 0.432706647838971) , + rgb (0.7771103295521099, 0.5403867491056124, 0.4283745037125848) , + rgb (0.775907907306857, 0.5355421788246119, 0.42413367909988375) , + rgb (0.774684947460632, 0.5306974938477673, 0.4199844035696376) , + rgb (0.7734402182988989, 0.5258533209345156, 0.41592679539764366) , + rgb (0.7721725722960555, 0.5210102658711383, 0.4119608998712287) , + rgb (0.7708809196230247, 0.516168926434691, 0.40808667584648967) , + rgb (0.7695642334401418, 0.5113298901696085, 0.4043039806968248) , + rgb (0.768221765997353, 0.5064936237128791, 0.40061257089416885) , + rgb (0.7668522895064389, 0.5016608471009063, 0.39701221751773474) , + rgb (0.7654549259333051, 0.4968321290972723, 0.3935025400011538) , + rgb (0.7640288560928866, 0.49200802533379656, 0.39008308392311997) , + rgb (0.7625733355405261, 0.48718906673415824, 0.38675335037837993) , + rgb (0.7610876378057071, 0.48237579130289127, 0.3835127572385229) , + rgb (0.7595711110534006, 0.4775687122205708, 0.380360657784311) , + rgb (0.7580232553845584, 0.4727682731566229, 0.3772963553109668) , + rgb (0.7564434157714071, 0.4679750143794846, 0.37431909037543515) , + rgb (0.7548310506695954, 0.46318942799460555, 0.3714280448394211) , + rgb (0.7531856636904657, 0.45841199172949604, 0.3686223664223477) , + rgb (0.7515068504589166, 0.45364314496866825, 0.36590112443835765) , + rgb (0.7497942054717047, 0.4488833348154881, 0.3632633755836028) , + rgb (0.7480473927555956, 0.44413297780351974, 0.36070813602540136) , + rgb (0.7462661578916344, 0.439392450449735, 0.3582343914230064) , + rgb (0.7444501867657067, 0.4346621718461711, 0.35584108091122535) , + rgb (0.7425992159973317, 0.42994254036133867, 0.3535270924537459) , + rgb (0.7407130161950609, 0.4252339389526239, 0.35129130890802607) , + rgb (0.7387914002459927, 0.4205367299231533, 0.34913260148542435) , + rgb (0.7368342217358587, 0.4158512585029011, 0.347049785207584) , + rgb (0.7348413359856494, 0.4111778700451951, 0.3450416947080907) , + rgb (0.7328127050626875, 0.4065168468778026, 0.3431071517341082) , + rgb (0.7307482075484517, 0.401868526884681, 0.3412449533046818) , + rgb (0.7286477385671655, 0.39723324476747235, 0.33945384341064017) , + rgb (0.7265112290022755, 0.3926113126792577, 0.3377325942005665) , + rgb (0.7243386564778159, 0.38800301593162145, 0.3360799596569183) , + rgb (0.7221299918421461, 0.3834086450896306, 0.33449469983585844) , + rgb (0.7198852149054985, 0.37882848839337313, 0.332975552002454) , + rgb (0.7176044490813385, 0.3742627271068619, 0.3315213862095893) , + rgb (0.7152876061484729, 0.3697117022522345, 0.3301308728723546) , + rgb (0.7129346683977347, 0.36517570519856757, 0.3288027427038317) , + rgb (0.7105456546582587, 0.36065500290840113, 0.3275357416278876) , + rgb (0.7081205956842048, 0.356149855233803, 0.32632861885644465) , + rgb (0.7056595112261009, 0.3516605297812094, 0.32518014084085567) , + rgb (0.7031624945881415, 0.34718723719598, 0.32408913679491225) , + rgb (0.700629624772421, 0.34273019305341756, 0.32305449047765694) , + rgb (0.6980608153581771, 0.3382897632604862, 0.3220747885521809) , + rgb (0.6954560834689112, 0.33386622163232865, 0.3211488430698579) , + rgb (0.6928154484676493, 0.32945984647042675, 0.3202754315314667) , + rgb (0.6901389321505248, 0.32507091815606004, 0.319453323328983) , + rgb (0.6874265643516962, 0.32069970535138104, 0.3186813762227769) , + rgb (0.6846785094249453, 0.3163463482122221, 0.31795870784057567) , + rgb (0.6818945715094452, 0.31201133280550686, 0.3172838048924495) , + rgb (0.6790747402815734, 0.30769497879760166, 0.31665545668946665) , + rgb (0.6762189792440975, 0.30339762792450425, 0.3160724937230589) , + rgb (0.6733272556481733, 0.29911962764489264, 0.3155337232398221) , + rgb (0.670399595476762, 0.29486126309253047, 0.3150381395687221) , + rgb (0.6674360376636913, 0.29062280081258873, 0.31458483752056837) , + rgb (0.6644363246987884, 0.2864048361425618, 0.31417223403606975) , + rgb (0.6614003753260178, 0.28220778870555907, 0.3137991292649849) , + rgb (0.6583280801134499, 0.2780321095766563, 0.3134643447952643) , + rgb (0.6552193260932713, 0.2738782665241015, 0.3131666792687211) , + rgb (0.652074172902773, 0.269746505252367, 0.3129056060581917) , + rgb (0.6488923016945825, 0.2656375533620908, 0.3126794181957019) , + rgb (0.6456734938264543, 0.2615520316161528, 0.31248673753935263) , + rgb (0.642417577481186, 0.257490519876798, 0.31232631707560987) , + rgb (0.6391243387840212, 0.2534536546198314, 0.3121968961206398) , + rgb (0.6357937104834237, 0.24944185818822678, 0.3120979395330059) , + rgb (0.6324253485421027, 0.24545598775548677, 0.3120276597462445) , + rgb (0.6290189201698587, 0.24149689191922535, 0.3119844719564572) , + rgb (0.6255741650043496, 0.23756535071152696, 0.3119669831491227) , + rgb (0.6220907982108261, 0.2336621873300741, 0.3119738327362739) , + rgb (0.6185686525887719, 0.2297879924917992, 0.3120046383872893) , + rgb (0.6150072323639137, 0.22594402043981826, 0.3120568068576574) , + rgb (0.6114062072731884, 0.22213124697023234, 0.3121288139643524) , + rgb (0.6077652399481865, 0.21835070166659282, 0.312219032918702) , + rgb (0.6040840169673274, 0.21460331490206347, 0.31232652641170694) , + rgb (0.6003621301041158, 0.21089030138947745, 0.3124493441041469) , + rgb (0.5965991810912237, 0.207212956082026, 0.3125852303112123) , + rgb (0.5927948053652026, 0.20357251410079796, 0.3127323483930494) , + rgb (0.5889486193554471, 0.19997020971775276, 0.31288922211590126) , + rgb (0.5850602439646688, 0.19640737049066315, 0.3130540116373273) , + rgb (0.5811293276158656, 0.19288548904692518, 0.3132239939418394) , + rgb (0.5771555081299204, 0.18940601489760422, 0.3133970433357208) , + rgb (0.5731384575410787, 0.18597036007065024, 0.3135712686852) , + rgb (0.5690778478401143, 0.18258004462335425, 0.3137444095679653) , + rgb (0.564973435290177, 0.17923664950367169, 0.3139136046337036) , + rgb (0.5608249903911717, 0.17594170887918095, 0.31407639883970623) , + rgb (0.5566322903496934, 0.17269677158182117, 0.31423043195101424) , + rgb (0.5523952157271191, 0.16950338809328983, 0.3143729155461537) , + rgb (0.5481137003346762, 0.1663630904279047, 0.3145010299091471) , + rgb (0.5437877131360856, 0.16327738551419116, 0.31461204226295625) , + rgb (0.5394173664919906, 0.16024769309971934, 0.31470295028655965) , + rgb (0.5350027976174474, 0.15727540775107324, 0.3147708520739653) , + rgb (0.5305442048985645, 0.15436183633886777, 0.3148129978918713) , + rgb (0.5260418962547748, 0.15150818660835483, 0.31482653406646727) , + rgb (0.5214965286322996, 0.14871544765633712, 0.3148076795453443) , + rgb (0.5169084880054446, 0.14598463068714407, 0.3147540759228004) , + rgb (0.5122783510532176, 0.14331656120063752, 0.3146630922831542) , + rgb (0.5076068118105369, 0.14071192654913128, 0.3145320012008257) , + rgb (0.5028952497497061, 0.13817086581280427, 0.3143566215383367) , + rgb (0.4981443546207415, 0.13569380302451714, 0.314135190862664) , + rgb (0.49335504375145617, 0.13328091630401023, 0.31386561956734976) , + rgb (0.48852847371852987, 0.13093210934893723, 0.31354553695453014) , + rgb (0.48366628618847957, 0.1286467902201389, 0.31317188565991266) , + rgb (0.47877034239726296, 0.12642401401409453, 0.3127417273582196) , + rgb (0.473841437035254, 0.12426354237989065, 0.31225470169927194) , + rgb (0.46888111384598413, 0.12216445576414045, 0.31170911458932665) , + rgb (0.46389102840284874, 0.12012561256850712, 0.31110343446582983) , + rgb (0.45887288947308297, 0.11814571137706886, 0.3104363697903881) , + rgb (0.4538300508699989, 0.11622183788331528, 0.3097044124984492) , + rgb (0.4487629917317482, 0.1143535557462255, 0.30890905921943196) , + rgb (0.44367358645071275, 0.11253912421257944, 0.3080497309546545) , + rgb (0.4385637818793154, 0.11077667828375456, 0.30712600062348083) , + rgb (0.4334355841041439, 0.1090642347484701, 0.3061376792828915) , + rgb (0.4282910131578975, 0.1073997763055258, 0.30508479060294547) , + rgb (0.42313214269556043, 0.10578120994917611, 0.3039675809469457) , + rgb (0.41796105205173684, 0.10420644885760968, 0.3027865203963184) , + rgb (0.41277985630360303, 0.1026734006932461, 0.3015422643746897) , + rgb (0.4075912039268871, 0.10117945586419633, 0.300235195077286) , + rgb (0.40239692379737496, 0.09972293031495055, 0.2988667436973397) , + rgb (0.39719876876325577, 0.09830232096827862, 0.2974385647628578) , + rgb (0.39199887556812907, 0.09691583650296684, 0.2959521200550908) , + rgb (0.3867993907954417, 0.09556181960083443, 0.29440901248173756) , + rgb (0.38160247377467543, 0.09423873126371218, 0.2928110750626949) , + rgb (0.3764103053221462, 0.09294519809377791, 0.2911602415731392) , + rgb (0.3712250843130934, 0.09167999748026273, 0.2894586539763317) , + rgb (0.36605031412464006, 0.0904406854276979, 0.2877077458811747) , + rgb (0.36088752387578377, 0.0892271943627452, 0.28591050458531014) , + rgb (0.35573889947341125, 0.08803897435024335, 0.2840695897279818) , + rgb (0.3506067824603248, 0.08687555176033529, 0.28218770540182386) , + rgb (0.345493557138718, 0.08573665496512634, 0.28026769921081435) , + rgb (0.34040164359597463, 0.08462223619170267, 0.27831254595259397) , + rgb (0.3353335322445545, 0.08353243411900396, 0.2763253435679004) , + rgb (0.3302917447118144, 0.08246763389003825, 0.27430929404579435) , + rgb (0.3252788886040126, 0.08142839007654609, 0.27226772884656186) , + rgb (0.3202998655799406, 0.08041299473755484, 0.2702032289303951) , + rgb (0.3153587000920581, 0.07942099731524319, 0.2681190407694196) , + rgb (0.31045520848595526, 0.07845687167618218, 0.2660200572779356) , + rgb (0.30559226007249934, 0.07752196310753731, 0.2639100669211966) , + rgb (0.3007727681291869, 0.07661782433616476, 0.2617929409781967) , + rgb (0.2960004726065818, 0.07574533600095842, 0.25967245030364566) , + rgb (0.29128515387578635, 0.0748990498474667, 0.25755101595750435) , + rgb (0.28662309235899847, 0.07408846082680887, 0.2554347867371703) , + rgb (0.2820174629736694, 0.07331569321404097, 0.25332800295084507) , + rgb (0.277471508091428, 0.07258296989925478, 0.2512349399594277) , + rgb (0.272997015188973, 0.07188355544616351, 0.2491584709323293) , + rgb (0.2685909594817286, 0.07122671627792246, 0.24710443563450618) , + rgb (0.2642551220706094, 0.07061659562299544, 0.24507758869355967) , + rgb (0.25999463887892144, 0.07005385560386188, 0.24308218808684579) , + rgb (0.2558213554748177, 0.06953231029187984, 0.24112190491594204) , + rgb (0.25172899728289466, 0.0690646308260355, 0.23920260612763083) , + rgb (0.247720929905011, 0.06865333790948652, 0.2373288009471749) , + rgb (0.2438114972024792, 0.06828985152901187, 0.23550427698321885) , + rgb (0.23999586188690308, 0.06798502996477995, 0.23373434258507808) , + rgb (0.23627495835774248, 0.06774359820987802, 0.23202360805926608) , + rgb (0.23266299920501882, 0.06755710438847978, 0.23037617493752832) , + rgb (0.2291562027859284, 0.06743473087115257, 0.22879681433956656) , + rgb (0.2257539681670791, 0.06738213230014747, 0.22728984778098055) , + rgb (0.22247308588973624, 0.06738821405309284, 0.22585960379408354) , + rgb (0.21930503925136402, 0.06746578636294004, 0.22451023616807558) , + rgb (0.21625279838647882, 0.06761633027051639, 0.22324568672294431) , + rgb (0.2133313859647627, 0.06783014842602667, 0.2220704321302429) , + rgb (0.21052882914958676, 0.06812195249816172, 0.22098759107715404) , + rgb (0.20785704662965598, 0.06848439879702528, 0.22000133917653536) , + rgb (0.20531725273301316, 0.06891959226639757, 0.21911516689288835) , + rgb (0.20290365333558247, 0.06943524858045896, 0.21833167885096033) , + rgb (0.20058760685133747, 0.07006457614998421, 0.21762721310371608) , + rgb (0.19819343656336558, 0.07097462525273879, 0.21690975060032436) , + rgb (0.19571853588267552, 0.07215778103960274, 0.21617499187076789) , + rgb (0.1931548636579131, 0.07360681904011795, 0.21542362939081539) , + rgb (0.19049578401722037, 0.07531127841678764, 0.2146562337112265) , + rgb (0.18774482037046955, 0.07725158846803931, 0.21387448578597812) , + rgb (0.18488035509396164, 0.07942573027972388, 0.21307651648984993) + }); + + +list_data viridis = list_data(new pen[] { + rgb (0.267004, 0.004874, 0.329415) , + rgb (0.26851, 0.009605, 0.335427) , + rgb (0.269944, 0.014625, 0.341379) , + rgb (0.271305, 0.019942, 0.347269) , + rgb (0.272594, 0.025563, 0.353093) , + rgb (0.273809, 0.031497, 0.358853) , + rgb (0.274952, 0.037752, 0.364543) , + rgb (0.276022, 0.044167, 0.370164) , + rgb (0.277018, 0.050344, 0.375715) , + rgb (0.277941, 0.056324, 0.381191) , + rgb (0.278791, 0.062145, 0.386592) , + rgb (0.279566, 0.067836, 0.391917) , + rgb (0.280267, 0.073417, 0.397163) , + rgb (0.280894, 0.078907, 0.402329) , + rgb (0.281446, 0.08432, 0.407414) , + rgb (0.281924, 0.089666, 0.412415) , + rgb (0.282327, 0.094955, 0.417331) , + rgb (0.282656, 0.100196, 0.42216) , + rgb (0.28291, 0.105393, 0.426902) , + rgb (0.283091, 0.110553, 0.431554) , + rgb (0.283197, 0.11568, 0.436115) , + rgb (0.283229, 0.120777, 0.440584) , + rgb (0.283187, 0.125848, 0.44496) , + rgb (0.283072, 0.130895, 0.449241) , + rgb (0.282884, 0.13592, 0.453427) , + rgb (0.282623, 0.140926, 0.457517) , + rgb (0.28229, 0.145912, 0.46151) , + rgb (0.281887, 0.150881, 0.465405) , + rgb (0.281412, 0.155834, 0.469201) , + rgb (0.280868, 0.160771, 0.472899) , + rgb (0.280255, 0.165693, 0.476498) , + rgb (0.279574, 0.170599, 0.479997) , + rgb (0.278826, 0.17549, 0.483397) , + rgb (0.278012, 0.180367, 0.486697) , + rgb (0.277134, 0.185228, 0.489898) , + rgb (0.276194, 0.190074, 0.493001) , + rgb (0.275191, 0.194905, 0.496005) , + rgb (0.274128, 0.199721, 0.498911) , + rgb (0.273006, 0.20452, 0.501721) , + rgb (0.271828, 0.209303, 0.504434) , + rgb (0.270595, 0.214069, 0.507052) , + rgb (0.269308, 0.218818, 0.509577) , + rgb (0.267968, 0.223549, 0.512008) , + rgb (0.26658, 0.228262, 0.514349) , + rgb (0.265145, 0.232956, 0.516599) , + rgb (0.263663, 0.237631, 0.518762) , + rgb (0.262138, 0.242286, 0.520837) , + rgb (0.260571, 0.246922, 0.522828) , + rgb (0.258965, 0.251537, 0.524736) , + rgb (0.257322, 0.25613, 0.526563) , + rgb (0.255645, 0.260703, 0.528312) , + rgb (0.253935, 0.265254, 0.529983) , + rgb (0.252194, 0.269783, 0.531579) , + rgb (0.250425, 0.27429, 0.533103) , + rgb (0.248629, 0.278775, 0.534556) , + rgb (0.246811, 0.283237, 0.535941) , + rgb (0.244972, 0.287675, 0.53726) , + rgb (0.243113, 0.292092, 0.538516) , + rgb (0.241237, 0.296485, 0.539709) , + rgb (0.239346, 0.300855, 0.540844) , + rgb (0.237441, 0.305202, 0.541921) , + rgb (0.235526, 0.309527, 0.542944) , + rgb (0.233603, 0.313828, 0.543914) , + rgb (0.231674, 0.318106, 0.544834) , + rgb (0.229739, 0.322361, 0.545706) , + rgb (0.227802, 0.326594, 0.546532) , + rgb (0.225863, 0.330805, 0.547314) , + rgb (0.223925, 0.334994, 0.548053) , + rgb (0.221989, 0.339161, 0.548752) , + rgb (0.220057, 0.343307, 0.549413) , + rgb (0.21813, 0.347432, 0.550038) , + rgb (0.21621, 0.351535, 0.550627) , + rgb (0.214298, 0.355619, 0.551184) , + rgb (0.212395, 0.359683, 0.55171) , + rgb (0.210503, 0.363727, 0.552206) , + rgb (0.208623, 0.367752, 0.552675) , + rgb (0.206756, 0.371758, 0.553117) , + rgb (0.204903, 0.375746, 0.553533) , + rgb (0.203063, 0.379716, 0.553925) , + rgb (0.201239, 0.38367, 0.554294) , + rgb (0.19943, 0.387607, 0.554642) , + rgb (0.197636, 0.391528, 0.554969) , + rgb (0.19586, 0.395433, 0.555276) , + rgb (0.1941, 0.399323, 0.555565) , + rgb (0.192357, 0.403199, 0.555836) , + rgb (0.190631, 0.407061, 0.556089) , + rgb (0.188923, 0.41091, 0.556326) , + rgb (0.187231, 0.414746, 0.556547) , + rgb (0.185556, 0.41857, 0.556753) , + rgb (0.183898, 0.422383, 0.556944) , + rgb (0.182256, 0.426184, 0.55712) , + rgb (0.180629, 0.429975, 0.557282) , + rgb (0.179019, 0.433756, 0.55743) , + rgb (0.177423, 0.437527, 0.557565) , + rgb (0.175841, 0.44129, 0.557685) , + rgb (0.174274, 0.445044, 0.557792) , + rgb (0.172719, 0.448791, 0.557885) , + rgb (0.171176, 0.45253, 0.557965) , + rgb (0.169646, 0.456262, 0.55803) , + rgb (0.168126, 0.459988, 0.558082) , + rgb (0.166617, 0.463708, 0.558119) , + rgb (0.165117, 0.467423, 0.558141) , + rgb (0.163625, 0.471133, 0.558148) , + rgb (0.162142, 0.474838, 0.55814) , + rgb (0.160665, 0.47854, 0.558115) , + rgb (0.159194, 0.482237, 0.558073) , + rgb (0.157729, 0.485932, 0.558013) , + rgb (0.15627, 0.489624, 0.557936) , + rgb (0.154815, 0.493313, 0.55784) , + rgb (0.153364, 0.497, 0.557724) , + rgb (0.151918, 0.500685, 0.557587) , + rgb (0.150476, 0.504369, 0.55743) , + rgb (0.149039, 0.508051, 0.55725) , + rgb (0.147607, 0.511733, 0.557049) , + rgb (0.14618, 0.515413, 0.556823) , + rgb (0.144759, 0.519093, 0.556572) , + rgb (0.143343, 0.522773, 0.556295) , + rgb (0.141935, 0.526453, 0.555991) , + rgb (0.140536, 0.530132, 0.555659) , + rgb (0.139147, 0.533812, 0.555298) , + rgb (0.13777, 0.537492, 0.554906) , + rgb (0.136408, 0.541173, 0.554483) , + rgb (0.135066, 0.544853, 0.554029) , + rgb (0.133743, 0.548535, 0.553541) , + rgb (0.132444, 0.552216, 0.553018) , + rgb (0.131172, 0.555899, 0.552459) , + rgb (0.129933, 0.559582, 0.551864) , + rgb (0.128729, 0.563265, 0.551229) , + rgb (0.127568, 0.566949, 0.550556) , + rgb (0.126453, 0.570633, 0.549841) , + rgb (0.125394, 0.574318, 0.549086) , + rgb (0.124395, 0.578002, 0.548287) , + rgb (0.123463, 0.581687, 0.547445) , + rgb (0.122606, 0.585371, 0.546557) , + rgb (0.121831, 0.589055, 0.545623) , + rgb (0.121148, 0.592739, 0.544641) , + rgb (0.120565, 0.596422, 0.543611) , + rgb (0.120092, 0.600104, 0.54253) , + rgb (0.119738, 0.603785, 0.5414) , + rgb (0.119512, 0.607464, 0.540218) , + rgb (0.119423, 0.611141, 0.538982) , + rgb (0.119483, 0.614817, 0.537692) , + rgb (0.119699, 0.61849, 0.536347) , + rgb (0.120081, 0.622161, 0.534946) , + rgb (0.120638, 0.625828, 0.533488) , + rgb (0.12138, 0.629492, 0.531973) , + rgb (0.122312, 0.633153, 0.530398) , + rgb (0.123444, 0.636809, 0.528763) , + rgb (0.12478, 0.640461, 0.527068) , + rgb (0.126326, 0.644107, 0.525311) , + rgb (0.128087, 0.647749, 0.523491) , + rgb (0.130067, 0.651384, 0.521608) , + rgb (0.132268, 0.655014, 0.519661) , + rgb (0.134692, 0.658636, 0.517649) , + rgb (0.137339, 0.662252, 0.515571) , + rgb (0.14021, 0.665859, 0.513427) , + rgb (0.143303, 0.669459, 0.511215) , + rgb (0.146616, 0.67305, 0.508936) , + rgb (0.150148, 0.676631, 0.506589) , + rgb (0.153894, 0.680203, 0.504172) , + rgb (0.157851, 0.683765, 0.501686) , + rgb (0.162016, 0.687316, 0.499129) , + rgb (0.166383, 0.690856, 0.496502) , + rgb (0.170948, 0.694384, 0.493803) , + rgb (0.175707, 0.6979, 0.491033) , + rgb (0.180653, 0.701402, 0.488189) , + rgb (0.185783, 0.704891, 0.485273) , + rgb (0.19109, 0.708366, 0.482284) , + rgb (0.196571, 0.711827, 0.479221) , + rgb (0.202219, 0.715272, 0.476084) , + rgb (0.20803, 0.718701, 0.472873) , + rgb (0.214, 0.722114, 0.469588) , + rgb (0.220124, 0.725509, 0.466226) , + rgb (0.226397, 0.728888, 0.462789) , + rgb (0.232815, 0.732247, 0.459277) , + rgb (0.239374, 0.735588, 0.455688) , + rgb (0.24607, 0.73891, 0.452024) , + rgb (0.252899, 0.742211, 0.448284) , + rgb (0.259857, 0.745492, 0.444467) , + rgb (0.266941, 0.748751, 0.440573) , + rgb (0.274149, 0.751988, 0.436601) , + rgb (0.281477, 0.755203, 0.432552) , + rgb (0.288921, 0.758394, 0.428426) , + rgb (0.296479, 0.761561, 0.424223) , + rgb (0.304148, 0.764704, 0.419943) , + rgb (0.311925, 0.767822, 0.415586) , + rgb (0.319809, 0.770914, 0.411152) , + rgb (0.327796, 0.77398, 0.40664) , + rgb (0.335885, 0.777018, 0.402049) , + rgb (0.344074, 0.780029, 0.397381) , + rgb (0.35236, 0.783011, 0.392636) , + rgb (0.360741, 0.785964, 0.387814) , + rgb (0.369214, 0.788888, 0.382914) , + rgb (0.377779, 0.791781, 0.377939) , + rgb (0.386433, 0.794644, 0.372886) , + rgb (0.395174, 0.797475, 0.367757) , + rgb (0.404001, 0.800275, 0.362552) , + rgb (0.412913, 0.803041, 0.357269) , + rgb (0.421908, 0.805774, 0.35191) , + rgb (0.430983, 0.808473, 0.346476) , + rgb (0.440137, 0.811138, 0.340967) , + rgb (0.449368, 0.813768, 0.335384) , + rgb (0.458674, 0.816363, 0.329727) , + rgb (0.468053, 0.818921, 0.323998) , + rgb (0.477504, 0.821444, 0.318195) , + rgb (0.487026, 0.823929, 0.312321) , + rgb (0.496615, 0.826376, 0.306377) , + rgb (0.506271, 0.828786, 0.300362) , + rgb (0.515992, 0.831158, 0.294279) , + rgb (0.525776, 0.833491, 0.288127) , + rgb (0.535621, 0.835785, 0.281908) , + rgb (0.545524, 0.838039, 0.275626) , + rgb (0.555484, 0.840254, 0.269281) , + rgb (0.565498, 0.84243, 0.262877) , + rgb (0.575563, 0.844566, 0.256415) , + rgb (0.585678, 0.846661, 0.249897) , + rgb (0.595839, 0.848717, 0.243329) , + rgb (0.606045, 0.850733, 0.236712) , + rgb (0.616293, 0.852709, 0.230052) , + rgb (0.626579, 0.854645, 0.223353) , + rgb (0.636902, 0.856542, 0.21662) , + rgb (0.647257, 0.8584, 0.209861) , + rgb (0.657642, 0.860219, 0.203082) , + rgb (0.668054, 0.861999, 0.196293) , + rgb (0.678489, 0.863742, 0.189503) , + rgb (0.688944, 0.865448, 0.182725) , + rgb (0.699415, 0.867117, 0.175971) , + rgb (0.709898, 0.868751, 0.169257) , + rgb (0.720391, 0.87035, 0.162603) , + rgb (0.730889, 0.871916, 0.156029) , + rgb (0.741388, 0.873449, 0.149561) , + rgb (0.751884, 0.874951, 0.143228) , + rgb (0.762373, 0.876424, 0.137064) , + rgb (0.772852, 0.877868, 0.131109) , + rgb (0.783315, 0.879285, 0.125405) , + rgb (0.79376, 0.880678, 0.120005) , + rgb (0.804182, 0.882046, 0.114965) , + rgb (0.814576, 0.883393, 0.110347) , + rgb (0.82494, 0.88472, 0.106217) , + rgb (0.83527, 0.886029, 0.102646) , + rgb (0.845561, 0.887322, 0.099702) , + rgb (0.85581, 0.888601, 0.097452) , + rgb (0.866013, 0.889868, 0.095953) , + rgb (0.876168, 0.891125, 0.09525) , + rgb (0.886271, 0.892374, 0.095374) , + rgb (0.89632, 0.893616, 0.096335) , + rgb (0.906311, 0.894855, 0.098125) , + rgb (0.916242, 0.896091, 0.100717) , + rgb (0.926106, 0.89733, 0.104071) , + rgb (0.935904, 0.89857, 0.108131) , + rgb (0.945636, 0.899815, 0.112838) , + rgb (0.9553, 0.901065, 0.118128) , + rgb (0.964894, 0.902323, 0.123941) , + rgb (0.974417, 0.90359, 0.130215) , + rgb (0.983868, 0.904867, 0.136897) , + rgb (0.993248, 0.906157, 0.143936) + }); + + diff --git a/Build/source/utils/asymptote/base/contour.asy b/Build/source/utils/asymptote/base/contour.asy new file mode 100644 index 00000000000..8c6dbba86b3 --- /dev/null +++ b/Build/source/utils/asymptote/base/contour.asy @@ -0,0 +1,683 @@ +// 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(picture pic, 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 (pic.scale.x.T(pts[i].x), pic.scale.y.T(pts[i].y)); + },pts.length-1); + g.push(cycle); + gd=join(...g); + } else + gd=join(...sequence(new guide(int i) { + return (pic.scale.x.T(pts[i].x), pic.scale.y.T(pts[i].y)); + },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(picture pic=currentpicture, 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(pic,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(picture pic=currentpicture, 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(pic,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(picture pic=currentpicture, 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(pic,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(picture pic=currentpicture, real f(pair), pair a, pair b, + real[] c, int nx=ngraph, int ny=nx, + interpolate join=operator --) +{ + return contour(pic,new real(real x, real y) {return f((x,y));},a,b,c,nx,ny,join); +} + +guide[][] contour(picture pic=currentpicture, 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(pic,points,c,join); +} diff --git a/Build/source/utils/asymptote/base/contour3.asy b/Build/source/utils/asymptote/base/contour3.asy new file mode 100644 index 00000000000..a15a6663b23 --- /dev/null +++ b/Build/source/utils/asymptote/base/contour3.asy @@ -0,0 +1,485 @@ +import graph_settings; +import three; + +real eps=10000*realEpsilon; + +private struct weighted +{ + triple normal; + real ratio; + int kpa0,kpa1,kpa2; + int kpb0,kpb1,kpb2; + triple v; +} + +private struct bucket +{ + triple v; + triple val; + int count; +} + +struct vertex +{ + triple v; + triple normal; +} + +// A group of 3 or 4 points. +private struct object +{ + bool active; + weighted[] pts; +} + +// Return contour vertices for a 3D data array. +// z: three-dimensional array of nonoverlapping mesh points +// f: three-dimensional arrays of real data values +// midpoint: optional array containing estimate of f at midpoint values +vertex[][] contour3(triple[][][] v, real[][][] f, + real[][][] midpoint=new real[][][], + projection P=currentprojection) +{ + int nx=v.length-1; + if(nx == 0) + abort("array v must have length >= 2"); + int ny=v[0].length-1; + if(ny == 0) + abort("array v[0] must have length >= 2"); + int nz=v[0][0].length-1; + if(nz == 0) + abort("array v[0][0] must have length >= 2"); + + bool midpoints=midpoint.length > 0; + + bucket[][][][] kps=new bucket[2nx+1][2ny+1][2nz+1][]; + for(int i=0; i < 2nx+1; ++i) + for(int j=0; j < 2ny+1; ++j) + for(int k=0; k < 2nz+1; ++k) + kps[i][j][k]=new bucket[]; + + object[] objects; + + // go over region a rectangle at a time + for(int i=0; i < nx; ++i) { + triple[][] vi=v[i]; + triple[][] vp=v[i+1]; + real[][] fi=f[i]; + real[][] fp=f[i+1]; + int i2=2i; + int i2p1=i2+1; + int i2p2=i2+2; + for(int j=0; j < ny; ++j) { + triple[] vij=vi[j]; + triple[] vpj=vp[j]; + triple[] vip=vi[j+1]; + triple[] vpp=vp[j+1]; + real[] fij=fi[j]; + real[] fpj=fp[j]; + real[] fip=fi[j+1]; + real[] fpp=fp[j+1]; + int j2=2j; + int j2p1=j2+1; + int j2p2=j2+2; + + for(int k=0; k < nz; ++k) { + // vertex values + real vdat0=fij[k]; + real vdat1=fij[k+1]; + real vdat2=fip[k]; + real vdat3=fip[k+1]; + real vdat4=fpj[k]; + real vdat5=fpj[k+1]; + real vdat6=fpp[k]; + real vdat7=fpp[k+1]; + + // define points + triple p000=vij[k]; + triple p001=vij[k+1]; + triple p010=vip[k]; + triple p011=vip[k+1]; + triple p100=vpj[k]; + triple p101=vpj[k+1]; + triple p110=vpp[k]; + triple p111=vpp[k+1]; + triple m0=0.25*(p000+p010+p110+p100); + triple m1=0.25*(p010+p110+p111+p011); + triple m2=0.25*(p110+p100+p101+p111); + triple m3=0.25*(p100+p000+p001+p101); + triple m4=0.25*(p000+p010+p011+p001); + triple m5=0.25*(p001+p011+p111+p101); + triple mc=0.5*(m0+m5); + + // optimization: we make sure we don't work with empty rectangles + int countm=0; + int countz=0; + int countp=0; + + void check(real vdat) { + if(vdat < -eps) ++countm; + else { + if(vdat <= eps) ++countz; + else ++countp; + } + } + + check(vdat0); + check(vdat1); + check(vdat2); + check(vdat3); + check(vdat4); + check(vdat5); + check(vdat6); + check(vdat7); + + if(countm == 8 || countp == 8 || + ((countm == 7 || countp == 7) && countz == 1)) continue; + + int k2=2k; + int k2p1=k2+1; + int k2p2=k2+2; + + // Evaluate midpoints of cube sides. + // Then evaluate midpoint of cube. + real vdat8=midpoints ? midpoint[i2p1][j2p1][k2] : + 0.25*(vdat0+vdat2+vdat6+vdat4); + real vdat9=midpoints ? midpoint[i2p1][j2p2][k2p1] : + 0.25*(vdat2+vdat6+vdat7+vdat3); + real vdat10=midpoints ? midpoint[i2p2][j2p1][k2p1] : + 0.25*(vdat7+vdat6+vdat4+vdat5); + real vdat11=midpoints ? midpoint[i2p1][j2][k2p1] : + 0.25*(vdat0+vdat4+vdat5+vdat1); + real vdat12=midpoints ? midpoint[i2][j2p1][k2p1] : + 0.25*(vdat0+vdat2+vdat3+vdat1); + real vdat13=midpoints ? midpoint[i2p1][j2p1][k2p2] : + 0.25*(vdat1+vdat3+vdat7+vdat5); + real vdat14=midpoints ? midpoint[i2p1][j2p1][k2p1] : + 0.125*(vdat0+vdat1+vdat2+vdat3+vdat4+vdat5+vdat6+vdat7); + + // Go through the 24 pyramids, 4 for each side. + + void addval(int kp0, int kp1, int kp2, triple add, triple v) { + bucket[] cur=kps[kp0][kp1][kp2]; + for(int q=0; q < cur.length; ++q) { + if(length(cur[q].v-v) < eps) { + cur[q].val += add; + ++cur[q].count; + return; + } + } + bucket newbuck; + newbuck.v=v; + newbuck.val=add; + newbuck.count=1; + cur.push(newbuck); + } + + void accrue(weighted w) { + triple val1=w.normal*w.ratio; + triple val2=w.normal*(1-w.ratio); + addval(w.kpa0,w.kpa1,w.kpa2,val1,w.v); + addval(w.kpb0,w.kpb1,w.kpb2,val2,w.v); + } + + triple dir=P.normal; + + void addnormals(weighted[] pts) { + triple vec2=pts[1].v-pts[0].v; + triple vec1=pts[0].v-pts[2].v; + triple vec0=-vec2-vec1; + vec2=unit(vec2); + vec1=unit(vec1); + vec0=unit(vec0); + triple normal=cross(vec2,vec1); + normal *= sgn(dot(normal,dir)); + + real angle(triple u, triple v) { + real Dot=-dot(u,v); + return Dot > 1 ? 0 : Dot < -1 ? pi : acos(Dot); + } + + real angle0=angle(vec1,vec2); + real angle1=angle(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(cur[0].v--cur[1].v--cur[2].v--cycle); + } + return s; +} diff --git a/Build/source/utils/asymptote/base/drawtree.asy b/Build/source/utils/asymptote/base/drawtree.asy new file mode 100644 index 00000000000..832dfc658a1 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/embed.asy b/Build/source/utils/asymptote/base/embed.asy new file mode 100644 index 00000000000..30848c10f4c --- /dev/null +++ b/Build/source/utils/asymptote/base/embed.asy @@ -0,0 +1,37 @@ +if(latex()) { + usepackage("hyperref"); + texpreamble("\hypersetup{"+settings.hyperrefOptions+"}"); + usepackage("media9","bigfiles"); +} + +// For documentation of the options see +// http://mirror.ctan.org/macros/latex/contrib/media9/doc/media9.pdf + +// Embed PRC or SWF content in pdf file +string embedplayer(string name, string text="", string options="", + real width=0, real height=0) +{ + if(width != 0) options += ",width="+(string) (width/pt)+"pt"; + if(height != 0) options += ",height="+(string) (height/pt)+"pt"; + return "% +\includemedia[noplaybutton,"+options+"]{"+text+"}{"+name+"}"; +} + +// Embed media in pdf file +string embed(string name, string text="", string options="", + real width=0, real height=0) +{ + return embedplayer("VPlayer.swf",text,"label="+name+ + ",activate=pageopen,addresource="+name+ + ",flashvars={source="+name+"&scaleMode=letterbox},"+ + options,width,height); +} + +string link(string label, string text="Play") +{ + return "\PushButton[ + onclick={ + annotRM['"+label+"'].activated=true; + annotRM['"+label+"'].callAS('playPause'); + }]{\fbox{"+text+"}}"; +} diff --git a/Build/source/utils/asymptote/base/external.asy b/Build/source/utils/asymptote/base/external.asy new file mode 100644 index 00000000000..9e12610c4b2 --- /dev/null +++ b/Build/source/utils/asymptote/base/external.asy @@ -0,0 +1,37 @@ +usepackage("hyperref"); +texpreamble("\hypersetup{"+settings.hyperrefOptions+"}"); + +// Embed object to be run in an external window. An image file name can be +// specified; if not given one will be automatically generated. +string embed(string name, string text="", string options="", + real width=0, real height=0, string image="") +{ + string options; // Ignore passed 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 label, string text="Play") +{ + return hyperlink("run:"+label,text); +} + diff --git a/Build/source/utils/asymptote/base/feynman.asy b/Build/source/utils/asymptote/base/feynman.asy new file mode 100644 index 00000000000..4182d989287 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/flowchart.asy b/Build/source/utils/asymptote/base/flowchart.asy new file mode 100644 index 00000000000..d1d87b7887e --- /dev/null +++ b/Build/source/utils/asymptote/base/flowchart.asy @@ -0,0 +1,526 @@ +// Flowchart routines written by Jacques Pienaar, Steve Melenchuk, John Bowman. + +private import math; + +struct flowdir {} + +restricted flowdir Horizontal; +restricted flowdir Vertical; + +real minblockwidth=0; +real minblockheight=0; +real mincirclediameter=0; +real defaultexcursion=0.1; + +struct block { + // The absolute center of the block in user coordinates. + pair center; + + // The size of the block + pair size; + + // The relative center of the block. + pair f_center; + + // These eight variables return the appropriate location on the block + // in relative coordinates, where the lower left corner of the block is (0,0). + pair f_top; + pair f_left; + pair f_right; + pair f_bottom; + pair f_topleft; + pair f_topright; + pair f_bottomleft; + pair f_bottomright; + + void operator init(pair z) { + center=z; + } + + void operator init(real x, real y) { + center=(x,y); + } + + pair shift(transform t=identity()) { + return t*center-f_center; + } + + // Returns the relative position along the boundary of the block. + pair f_position(real x); + + // Returns the absolute position along the boundary of the block. + pair position(real x, transform t=identity()) { + return shift(t)+f_position(x); + } + + // These eight functions return the appropriate location on the block + // in absolute coordinates. + pair top(transform t=identity()) { + return shift(t)+f_top; + } + pair bottom(transform t=identity()) { + return shift(t)+f_bottom; + } + pair left(transform t=identity()) { + return shift(t)+f_left; + } + pair right(transform t=identity()) { + return shift(t)+f_right; + } + pair topleft(transform t=identity()) { + return shift(t)+f_topleft; + } + pair topright(transform t=identity()) { + return shift(t)+f_topright; + } + pair bottomleft(transform t=identity()) { + return shift(t)+f_bottomleft; + } + pair bottomright(transform t=identity()) { + return shift(t)+f_bottomright; + } + + // Return a frame representing the block. + frame draw(pen p=currentpen); + + // Store optional label on outgoing edge. + Label label; + + // Store rectilinear path directions. + pair[] dirs; + + // Store optional arrow. + arrowbar arrow=None; +}; + +// Construct a rectangular block with header and body objects. +block rectangle(object header, object body, pair center=(0,0), + pen headerpen=mediumgray, pen bodypen=invisible, + pen drawpen=currentpen, + real dx=3, real minheaderwidth=minblockwidth, + real minheaderheight=minblockwidth, + real minbodywidth=minblockheight, + real minbodyheight=minblockheight) +{ + frame fbody=body.f; + frame fheader=header.f; + pair mheader=min(fheader); + pair Mheader=max(fheader); + pair mbody=min(fbody); + pair Mbody=max(fbody); + pair bound0=Mheader-mheader; + pair bound1=Mbody-mbody; + real width=max(bound0.x,bound1.x); + pair z0=maxbound((width+2dx,bound0.y+2dx),(minbodywidth,minbodyheight)); + pair z1=maxbound((width+2dx,bound1.y+2dx),(minheaderwidth,minheaderheight)); + path shape=(0,0)--(0,z1.y)--(0,z0.y+z1.y)--(z0.x,z0.y+z1.y)--z1--(z0.x,0)-- + cycle; + + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shift(0,z1.y)*box((0,0),z0),headerpen,drawpen); + add(block,shift(-0.5*(Mheader+mheader))*fheader,(0,z1.y)+0.5z0); + filldraw(block,box((0,0),z1),bodypen,drawpen); + add(block,shift(-0.5*(Mbody+mbody))*fbody,0.5z1); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=interp(point(shape,0),point(shape,3),0.5); + block.f_bottomleft=point(shape,0); + block.f_bottom=point(shape,5.5); + block.f_bottomright=point(shape,5); + block.f_right=point(shape,4.5); + block.f_topright=point(shape,3); + block.f_top=point(shape,2.5); + block.f_topleft=point(shape,2); + block.f_left=point(shape,0.5); + block.center=center; + block.size=point(shape,3); + return block; +} + +// As above, but without the header. +block rectangle(object body, pair center=(0,0), + pen fillpen=invisible, pen drawpen=currentpen, + real dx=3, real minwidth=minblockwidth, + real minheight=minblockheight) +{ + frame f=body.f; + pair m=min(f); + pair M=max(f); + pair z=maxbound(M-m+dx*(2,2),(minwidth,minheight)); + path shape=box((0,0),z); + + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shape,fillpen,drawpen); + add(block,shift(-0.5*(M+m))*f,0.5z); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=0.5*z; + block.center=center; + block.size=z; + block.f_bottomleft=point(shape,0); + block.f_bottom=point(shape,0.5); + block.f_bottomright=point(shape,1); + block.f_right=point(shape,1.5); + block.f_topright=point(shape,2); + block.f_top=point(shape,2.5); + block.f_topleft=point(shape,3); + block.f_left=point(shape,3.5); + return block; +} + +block parallelogram(object body, pair center=(0,0), + pen fillpen=invisible, pen drawpen=currentpen, + real dx=3, real slope=2, + real minwidth=minblockwidth, + real minheight=minblockheight) +{ + frame f=body.f; + pair m=min(f); + pair M=max(f); + pair bound=maxbound(M-m+dx*(0,2),(minwidth,minheight)); + + real skew=bound.y/slope; + real a=bound.x+skew; + real b=bound.y; + + path shape=(0,0)--(a,0)--(a+skew,b)--(skew,b)--cycle; + + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shape,fillpen,drawpen); + add(block,shift(-0.5*(M+m))*f,((a+skew)/2,b/2)); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=((a+skew)/2,b/2); + block.center=center; + block.size=(a+skew,b); + block.f_bottomleft=(0,0); + block.f_bottom=((a+skew)/2,0); + block.f_bottomright=(a,0); + block.f_right=(a+skew/2,b/2); + block.f_topright=(a+skew,b); + block.f_top=((a+skew)/2,b); + block.f_topleft=(skew,b); + block.f_left=(skew/2,b/2); + return block; +} + +block diamond(object body, pair center=(0,0), + pen fillpen=invisible, pen drawpen=currentpen, + real ds=5, real dw=1, + real height=20, real minwidth=minblockwidth, + real minheight=minblockheight) +{ + frame f=body.f; + pair m=min(f); + pair M=max(f); + pair bound=maxbound(M-m,(minwidth,minheight)); + + real e=ds; + real a=0.5bound.x-dw; + real b=0.5bound.y; + real c=b+height; + + real arg=a^2+b^2+c^2-2b*c-e^2; + real denom=e^2-a^2; + real slope=arg >= 0 && denom != 0 ? (a*(c-b)-e*sqrt(arg))/denom : 1.0; + real d=abs(c/slope); + + path shape=(2d,c)--(d,2c)--(0,c)--(d,0)--cycle; + + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shape,fillpen,drawpen); + add(block,shift(-0.5*(M+m))*f,(d,c)); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=(point(shape,1).x,point(shape,0).y); + block.center=center; + block.size=(point(shape,0).x,point(shape,1).y); + block.f_bottomleft=point(shape,2.5); + block.f_bottom=point(shape,3); + block.f_bottomright=point(shape,3.5); + block.f_right=point(shape,0); + block.f_topright=point(shape,0.5); + block.f_top=point(shape,1); + block.f_topleft=point(shape,1.5); + block.f_left=point(shape,2); + return block; +} + +block circle(object body, pair center=(0,0), pen fillpen=invisible, + pen drawpen=currentpen, real dr=3, + real mindiameter=mincirclediameter) +{ + frame f=body.f; + pair m=min(f); + pair M=max(f); + real r=max(0.5length(M-m)+dr,0.5mindiameter); + + path shape=(0,r)..(r,2r)..(2r,r)..(r,0)..cycle; + + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shape,fillpen,drawpen); + add(block,shift(-0.5*(M+m))*f,(r,r)); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=(r,r); + block.center=center; + block.size=(2r,2r); + block.f_left=point(shape,0); + block.f_topleft=point(shape,0.5); + block.f_top=point(shape,1); + block.f_topright=point(shape,1.5); + block.f_right=point(shape,2); + block.f_bottomright=point(shape,2.5); + block.f_bottom=point(shape,3); + block.f_bottomleft=point(shape,3.5); + return block; +} + +block roundrectangle(object body, pair center=(0,0), + pen fillpen=invisible, pen drawpen=currentpen, + real ds=5, real dw=0, real minwidth=minblockwidth, + real minheight=minblockheight) +{ + frame f=body.f; + pair m=min(f); + pair M=max(f); + pair bound=maxbound(M-m,(minwidth,minheight)); + + real a=bound.x; + real b=bound.y; + + path shape=(0,ds+dw)--(0,ds+b-dw){up}..{right} + (ds+dw,2ds+b)--(ds+a-dw,2ds+b){right}..{down} + (2ds+a,ds+b-dw)--(2ds+a,ds+dw){down}..{left} + (ds+a-dw,0)--(ds+dw,0){left}..{up}cycle; + + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shape,fillpen,drawpen); + add(block,shift(-0.5*(M+m))*f,(ds,ds)+0.5bound); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=(ds+0.5a,ds+0.5b); + block.center=center; + block.size=(2ds+a,2ds+b); + block.f_bottomleft=point(shape,7.5); + block.f_bottom=point(shape,6.5); + block.f_bottomright=point(shape,5.5); + block.f_right=point(shape,4.5); + block.f_topright=point(shape,3.5); + block.f_top=point(shape,2.5); + block.f_topleft=point(shape,1.5); + block.f_left=point(shape,0.5); + return block; +} + +block bevel(object body, pair center=(0,0), pen fillpen=invisible, + pen drawpen=currentpen, real dh=5, real dw=5, + real minwidth=minblockwidth, real minheight=minblockheight) +{ + frame f=body.f; + pair m=min(f); + pair M=max(f); + pair bound=maxbound(M-m,(minwidth,minheight)); + + real a=bound.x; + real b=0.5bound.y; + + path shape=(2dw+a,b+dh)--(dw+a,2b+2dh)--(dw,2b+2dh)--(0,b+dh)--(dw,0)-- + (dw+a,0)--cycle; + block block; + block.draw=new frame(pen p) { + frame block; + filldraw(block,shape,fillpen,drawpen); + add(block,shift(-0.5*(M+m))*f,(0.5bound+(dw,dh))); + return block; + }; + block.f_position=new pair(real x) { + return point(shape,x); + }; + block.f_center=(dw+0.5a,dh+b); + block.center=center; + block.size=(2dw+a,2dh+2b); + block.f_bottomleft=point(shape,4); + block.f_bottom=point(shape,4.5); + block.f_bottomright=point(shape,5); + block.f_right=point(shape,0); + block.f_topright=point(shape,1); + block.f_top=point(shape,1.5); + block.f_topleft=point(shape,2); + block.f_left=point(shape,3); + return block; +} + +path path(pair point[] ... flowdir dir[]) +{ + path line=point[0]; + pair current, prev=point[0]; + for(int i=1; i < point.length; ++i) { + if(i-1 >= dir.length || dir[i-1] == Horizontal) + current=(point[i].x,point[i-1].y); + else + current=(point[i-1].x,point[i].y); + + if(current != prev) { + line=line--current; + prev=current; + } + + current=point[i]; + if(current != prev) { + line=line--current; + prev=current; + } + } + return line; +} + +void draw(picture pic=currentpicture, block block, pen p=currentpen) +{ + pic.add(new void(frame f, transform t) { + add(f,shift(block.shift(t))*block.draw(p)); + },true); + pic.addBox(block.center,block.center, + -0.5*block.size+min(p),0.5*block.size+max(p)); +} + +typedef block blockconnector(block, block); + +blockconnector blockconnector(picture pic, transform t, pen p=currentpen, + margin margin=PenMargin) +{ + return new block(block b1, block b2) { + if(b1.dirs.length == 0) { + if(abs(b1.center.y-b2.center.y) < sqrtEpsilon) { + // horizontally aligned + b1.dirs[0]=b1.center.x < b2.center.x ? right : left; + blockconnector(pic,t,p,margin)(b1,b2); + } else if(abs(b1.center.x-b2.center.x) < sqrtEpsilon) { + // vertically aligned + b1.dirs[0]=b1.center.y < b2.center.y ? up : down; + blockconnector(pic,t,p,margin)(b1,b2); + } else { + if(abs(b1.center.y-b2.center.y) < abs(b1.center.x-b2.center.x)) { + b1.dirs[0]=b1.center.x < b2.center.x ? right : left; + b1.dirs[1]=b1.center.y < b2.center.y ? up : down; + blockconnector(pic,t,p,margin)(b1,b2); + } else { + b1.dirs[0]=b1.center.y < b2.center.y ? up : down; + b1.dirs[1]=b1.center.x < b2.center.x ? right : left; + blockconnector(pic,t,p,margin)(b1,b2); + } + } + return b2; + } + + // compute the link for given directions (and label if any) + pair[] dirs=copy(b1.dirs); // deep copy + pair current,prev; + pair dir=dirs[0]; + if(dir == up) prev=b1.top(t); + if(dir == down) prev=b1.bottom(t); + if(dir == left) prev=b1.left(t); + if(dir == right) prev=b1.right(t); + path line=prev; + arrowbar arrow=b1.arrow; + + int i; + for(i=1; i < dirs.length-1; ++i) { + if(abs(length(dirs[i-1])-1) < sqrtEpsilon) + current=prev+t*dirs[i-1]*defaultexcursion; + else + current=prev+t*dirs[i-1]; + + if(current != prev) { + line=line--current; + prev=current; + } + } + dir=dirs[dirs.length-1]; + current=0; + if(dir == up) current=b2.bottom(t); + if(dir == down) current=b2.top(t); + if(dir == left) current=b2.right(t); + if(dir == right) current=b2.left(t); + if(abs(dirs[i-1].y) < sqrtEpsilon && + abs(prev.x-current.x) > sqrtEpsilon) { + prev=(current.x,prev.y); + line=line--prev; // horizontal + } else if(abs(dirs[i-1].x) < sqrtEpsilon && + abs(prev.y-current.y) > sqrtEpsilon) { + prev=(prev.x,current.y); + line=line--prev; + } + if(current != prev) + line=line--current; + + draw(pic,b1.label,line,p,arrow,margin); + + b1.label=""; + b1.dirs.delete(); + b1.arrow=None; + return b2; + }; +} + +struct Dir +{ + pair z; + void operator init(pair z) {this.z=z;} +} + +Dir Right=Dir(right); +Dir Left=Dir(left); +Dir Up=Dir(up); +Dir Down=Dir(down); + +// Add a label to the current link +block operator --(block b1, Label label) +{ + b1.label=label; + return b1; +} + +// Add a direction to the current link +block operator --(block b1, Dir dir) +{ + b1.dirs.push(dir.z); + return b1; +} + +// Add an arrowbar to the current link +block operator --(block b, arrowbar arrowbar) +{ + b.arrow=arrowbar; + return b; +} diff --git a/Build/source/utils/asymptote/base/fontsize.asy b/Build/source/utils/asymptote/base/fontsize.asy new file mode 100644 index 00000000000..54b01829550 --- /dev/null +++ b/Build/source/utils/asymptote/base/fontsize.asy @@ -0,0 +1 @@ +if(latex()) usepackage("type1cm"); diff --git a/Build/source/utils/asymptote/base/geometry.asy b/Build/source/utils/asymptote/base/geometry.asy new file mode 100644 index 00000000000..420d5bdc456 --- /dev/null +++ b/Build/source/utils/asymptote/base/geometry.asy @@ -0,0 +1,7200 @@ +// geometry.asy + +// Copyright (C) 2007 +// Author: Philippe IVALDI 2007/09/01 +// http://www.piprime.fr/ + +// This program is free software ; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation ; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY ; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public License +// along with this program ; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +// COMMENTARY: +// An Asymptote geometry module. + +// THANKS: +// Special thanks to Olivier Guibe for his help in mathematical issues. + +// BUGS: + +// CODE: + +import math; +import markers; + +real Infinity=1.0/(1000*realEpsilon); + +// A rotation in the direction dir limited to [-90,90] +// This is useful for rotating text along a line in the direction dir. +private transform rotate(explicit pair dir) +{ + real angle=degrees(dir); + if(angle > 90 && angle < 270) angle -= 180; + return rotate(angle); +} + +// *=======================================================* +// *........................HEADER.........................* +/*<asyxml><variable type="real" signature="epsgeo"><code></asyxml>*/ +real epsgeo = 10 * sqrt(realEpsilon);/*<asyxml></code><documentation>Variable used in the approximate calculations.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="void" signature="addMargins(picture,real,real,real,real)"><code></asyxml>*/ +void addMargins(picture pic = currentpicture, + real lmargin = 0, real bmargin = 0, + real rmargin = lmargin, real tmargin = bmargin, + bool rigid = true, bool allObject = true) +{/*<asyxml></code><documentation>Add margins to 'pic' with respect to + the current bounding box of 'pic'. + If 'rigid' is false, margins are added iff an infinite curve will + be prolonged on the margin. + If 'allObject' is false, fixed - size objects (such as labels and + arrowheads) will be ignored.</documentation></function></asyxml>*/ + pair m = allObject ? truepoint(pic, SW) : point(pic, SW); + pair M = allObject ? truepoint(pic, NE) : point(pic, NE); + if(rigid) { + draw(m - inverse(pic.calculateTransform()) * (lmargin, bmargin), invisible); + draw(M + inverse(pic.calculateTransform()) * (rmargin, tmargin), invisible); + } else pic.addBox(m, M, -(lmargin, bmargin), (rmargin, tmargin)); +} + +real approximate(real t) +{ + real ot = t; + if(abs(t - ceil(t)) < epsgeo) ot = ceil(t); + else if(abs(t - floor(t)) < epsgeo) ot = floor(t); + return ot; +} + +real[] approximate(real[] T) +{ + return map(approximate, T); +} + +/*<asyxml><function type="real" signature="binomial(real,real)"><code></asyxml>*/ +real binomial(real n, real k) +{/*<asyxml></code><documentation>Return n!/((n - k)!*k!)</documentation></function></asyxml>*/ + return gamma(n + 1)/(gamma(n - k + 1) * gamma(k + 1)); +} + +/*<asyxml><function type="real" signature="rf(real,real,real)"><code></asyxml>*/ +real rf(real x, real y, real z) +{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the first kind. + x, y, and z must be non negative, and at most one can be zero.</documentation></function></asyxml>*/ + real ERRTOL = 0.0025, + TINY = 1.5e-38, + BIG = 3e37, + THIRD = 1/3, + C1 = 1/24, + C2 = 0.1, + C3 = 3/44, + C4 = 1/14; + real alamb, ave, delx, dely, delz, e2, e3, sqrtx, sqrty, sqrtz, xt, yt, zt; + if(min(x, y, z) < 0 || min(x + y, x + z, y + z) < TINY || + max(x, y, z) > BIG) abort("rf: invalid arguments."); + xt = x; + yt = y; + zt = z; + do { + sqrtx = sqrt(xt); + sqrty = sqrt(yt); + sqrtz = sqrt(zt); + alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz; + xt = 0.25 * (xt + alamb); + yt = 0.25 * (yt + alamb); + zt = 0.25 * (zt + alamb); + ave = THIRD * (xt + yt + zt); + delx = (ave - xt)/ave; + dely = (ave - yt)/ave; + delz = (ave - zt)/ave; + } while(max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL); + e2 = delx * dely - delz * delz; + e3 = delx * dely * delz; + return (1.0 + (C1 * e2 - C2 - C3 * e3) * e2 + C4 * e3)/sqrt(ave); +} + +/*<asyxml><function type="real" signature="rd(real,real,real)"><code></asyxml>*/ +real rd(real x, real y, real z) +{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the second kind. + x and y must be positive, and at most one can be zero. + z must be non negative.</documentation></function></asyxml>*/ + real ERRTOL = 0.0015, + TINY = 1e-25, + BIG = 4.5 * 10.0^21, + C1 = (3/14), + C2 = (1/6), + C3 = (9/22), + C4 = (3/26), + C5 = (0.25 * C3), + C6 = (1.5 * C4); + real alamb, ave, delx, dely, delz, ea, eb, ec, ed, ee, fac, sqrtx, sqrty, + sqrtz, sum, xt, yt, zt; + if (min(x, y) < 0 || min(x + y, z) < TINY || max(x, y, z) > BIG) + abort("rd: invalid arguments"); + xt = x; + yt = y; + zt = z; + sum = 0; + fac = 1; + do { + sqrtx = sqrt(xt); + sqrty = sqrt(yt); + sqrtz = sqrt(zt); + alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz; + sum += fac/(sqrtz * (zt + alamb)); + fac = 0.25 * fac; + xt = 0.25 * (xt + alamb); + yt = 0.25 * (yt + alamb); + zt = 0.25 * (zt + alamb); + ave = 0.2 * (xt + yt + 3.0 * zt); + delx = (ave - xt)/ave; + dely = (ave - yt)/ave; + delz = (ave - zt)/ave; + } while (max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL); + ea = delx * dely; + eb = delz * delz; + ec = ea - eb; + ed = ea - 6 * eb; + ee = ed + ec + ec; + return 3 * sum + fac * (1.0 + ed * (-C1 + C5 * ed - C6 * delz * ee) + +delz * (C2 * ee + delz * (-C3 * ec + delz * C4 * ea)))/(ave * sqrt(ave)); +} + +/*<asyxml><function type="real" signature="elle(real,real)"><code></asyxml>*/ +real elle(real phi, real k) +{/*<asyxml></code><documentation>Legendre elliptic integral of the 2nd kind, + evaluated using Carlson's functions RD and RF. + The argument ranges are -infinity < phi < +infinity, 0 <= k * sin(phi) <= 1.</documentation></function></asyxml>*/ + real result; + if (phi >= 0 && phi <= pi/2) { + real cc, q, s; + s = sin(phi); + cc = cos(phi)^2; + q = (1 - s * k) * (1 + s * k); + result = s * (rf(cc, q, 1) - (s * k)^2 * rd(cc, q, 1)/3); + } else + if (phi <= pi && phi >= 0) { + result = 2 * elle(pi/2, k) - elle(pi - phi, k); + } else + if (phi <= 3 * pi/2 && phi >= 0) { + result = 2 * elle(pi/2, k) + elle(phi - pi, k); + } else + if (phi <= 2 * pi && phi >= 0) { + result = 4 * elle(pi/2, k) - elle(2 * pi - phi, k); + } else + if (phi >= 0) { + int nb = floor(0.5 * phi/pi); + result = nb * elle(2 * pi, k) + elle(phi%(2 * pi), k); + } else result = -elle(-phi, k); + return result; +} + +/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real,real,real,real,real,real)"><code></asyxml>*/ +pair[] intersectionpoints(pair A, pair B, + real a, real b, real c, real d, real f, real g) +{/*<asyxml></code><documentation>Intersection points with the line (AB) and the quadric curve + a * x^2 + b * x * y + c * y^2 + d * x + f * y + g = 0 given in the default coordinate system</documentation></function></asyxml>*/ + pair[] op; + real ap = B.y - A.y, + bpp = A.x - B.x, + cp = A.y * B.x - A.x * B.y; + real sol[]; + if (abs(ap) > epsgeo) { + real aa = ap * c + a * bpp^2/ap - b * bpp, + bb = ap * f - bpp * d + 2 * a * bpp * cp/ap - b * cp, + cc = ap * g - cp * d + a * cp^2/ap; + sol = quadraticroots(aa, bb, cc); + for (int i = 0; i < sol.length; ++i) { + op.push((-bpp * sol[i]/ap - cp/ap, sol[i])); + } + } else { + real aa = a * bpp, + bb = d * bpp - b * cp, + cc = g * bpp - cp * f + c * cp^2/bpp; + sol = quadraticroots(aa, bb, cc); + for (int i = 0; i < sol.length; ++i) { + op.push((sol[i], -cp/bpp)); + } + } + return op; +} + +/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real[])"><code></asyxml>*/ +pair[] intersectionpoints(pair A, pair B, real[] equation) +{/*<asyxml></code><documentation>Return the intersection points of the line AB with + the conic whose an equation is + equation[0] * x^2 + equation[1] * x * y + equation[2] * y^2 + equation[3] * x + equation[4] * y + equation[5] = 0</documentation></function></asyxml>*/ + if(equation.length != 6) abort("intersectionpoints: bad length of array for a conic equation."); + return intersectionpoints(A, B, equation[0], equation[1], equation[2], + equation[3], equation[4], equation[5]); +} +// *........................HEADER.........................* +// *=======================================================* + +// *=======================================================* +// *......................COORDINATES......................* + +real EPS = sqrt(realEpsilon); + +/*<asyxml><typedef type = "convert" return = "pair" params = "pair"><code></asyxml>*/ +typedef pair convert(pair);/*<asyxml></code><documentation>Function type to convert pair in an other coordinate system.</documentation></typedef></asyxml>*/ +/*<asyxml><typedef type = "abs" return = "real" params = "pair"><code></asyxml>*/ +typedef real abs(pair);/*<asyxml></code><documentation>Function type to calculate modulus of pair.</documentation></typedef></asyxml>*/ +/*<asyxml><typedef type = "dot" return = "real" params = "pair, pair"><code></asyxml>*/ +typedef real dot(pair, pair);/*<asyxml></code><documentation>Function type to calculate dot product.</documentation></typedef></asyxml>*/ +/*<asyxml><typedef type = "polar" return = "pair" params = "real, real"><code></asyxml>*/ +typedef pair polar(real, real);/*<asyxml></code><documentation>Function type to calculate the coordinates from the polar coordinates.</documentation></typedef></asyxml>*/ + +/*<asyxml><struct signature="coordsys"><code></asyxml>*/ +struct coordsys +{/*<asyxml></code><documentation>This structure represents a coordinate system in the plane.</documentation></asyxml>*/ + /*<asyxml><method type = "pair" signature="relativetodefault(pair)"><code></asyxml>*/ + restricted convert relativetodefault = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to this coordinate system to + the pair relatively to the default coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "pair" signature="defaulttorelativet(pair)"><code></asyxml>*/ + restricted convert defaulttorelative = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to the default coordinate system to + the pair relatively to this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "real" signature="dot(pair,pair)"><code></asyxml>*/ + restricted dot dot = new real(pair m, pair n){return dot(m, n);};/*<asyxml></code><documentation>Return the dot product of this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "real" signature="abs(pair)"><code></asyxml>*/ + restricted abs abs = new real(pair m){return abs(m);};/*<asyxml></code><documentation>Return the modulus of a pair in this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "pair" signature="polar(real,real)"><code></asyxml>*/ + restricted polar polar = new pair(real r, real a){return (r * cos(a), r * sin(a));};/*<asyxml></code><documentation>Polar coordinates routine of this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><property type = "pair" signature="O,i,j"><code></asyxml>*/ + restricted pair O = (0, 0), i = (1, 0), j = (0, 1);/*<asyxml></code><documentation>Origin and units vector.</documentation></property></asyxml>*/ + /*<asyxml><method type = "void" signature="init(convert,convert,polar,dot)"><code></asyxml>*/ + void init(convert rtd, convert dtr, + polar polar, dot dot) + {/*<asyxml></code><documentation>The default constructor of the coordinate system.</documentation></method></asyxml>*/ + this.relativetodefault = rtd; + this.defaulttorelative = dtr; + this.polar = polar; + this.dot = dot; + this.abs = new real(pair m){return sqrt(dot(m, m));};; + this.O = rtd((0, 0)); + this.i = rtd((1, 0)) - O; + this.j = rtd((0, 1)) - O; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><operator type = "bool" signature="==(coordsys,coordsys)"><code></asyxml>*/ +bool operator ==(coordsys c1, coordsys c2) + {/*<asyxml></code><documentation>Return true iff the coordinate system have the same origin and units vector.</documentation></operator></asyxml>*/ + return c1.O == c2.O && c1.i == c2.i && c1.j == c2.j; + } + +/*<asyxml><function type="coordsys" signature="cartesiansystem(pair,pair,pair)"><code></asyxml>*/ +coordsys cartesiansystem(pair O = (0, 0), pair i, pair j) +{/*<asyxml></code><documentation>Return the Cartesian coordinate system (O, i, j).</documentation></function></asyxml>*/ + coordsys R; + real[][] P = {{0, 0}, {0, 0}}; + real[][] iP; + P[0][0] = i.x; + P[0][1] = j.x; + P[1][0] = i.y; + P[1][1] = j.y; + iP = inverse(P); + real ni = abs(i); + real nj = abs(j); + real ij = angle(j) - angle(i); + + pair rtd(pair m) + { + return O + (P[0][0] * m.x + P[0][1] * m.y, P[1][0] * m.x + P[1][1] * m.y); + } + + pair dtr(pair m) + { + m-=O; + return (iP[0][0] * m.x + iP[0][1] * m.y, iP[1][0] * m.x + iP[1][1] * m.y); + } + + pair polar(real r, real a) + { + real ca = sin(ij - a)/(ni * sin(ij)); + real sa = sin(a)/(nj * sin(ij)); + return r * (ca, sa); + } + + real tdot(pair m, pair n) + { + return m.x * n.x * ni^2 + m.y * n.y * nj^2 + (m.x * n.y + n.x * m.y) * dot(i, j); + } + + R.init(rtd, dtr, polar, tdot); + return R; +} + + +/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,coordsys,pen,pen,pen,pen,pen)"><code></asyxml>*/ +void show(picture pic = currentpicture, Label lo = "$O$", + Label li = "$\vec{\imath}$", + Label lj = "$\vec{\jmath}$", + coordsys R, + pen dotpen = currentpen, pen xpen = currentpen, pen ypen = xpen, + pen ipen = red, + pen jpen = ipen, + arrowbar arrow = Arrow) +{/*<asyxml></code><documentation>Draw the components (O, i, j, x - axis, y - axis) of 'R'.</documentation></function></asyxml>*/ + unravel R; + dot(pic, O, dotpen); + drawline(pic, O, O + i, xpen); + drawline(pic, O, O + j, ypen); + draw(pic, li, O--(O + i), ipen, arrow); + Label lj = lj.copy(); + lj.align(lj.align, unit(I * j)); + draw(pic, lj, O--(O + j), jpen, arrow); + draw(pic, lj, O--(O + j), jpen, arrow); + Label lo = lo.copy(); + lo.align(lo.align, -2 * dir(O--O + i, O--O + j)); + lo.p(dotpen); + label(pic, lo, O); +} + +/*<asyxml><operator type = "pair" signature="/(pair,coordsys)"><code></asyxml>*/ +pair operator /(pair p, coordsys R) +{/*<asyxml></code><documentation>Return the xy - coordinates of 'p' relatively to + the coordinate system 'R'. + For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), (0, 0)/R is (-1, -2).</documentation></operator></asyxml>*/ + return R.defaulttorelative(p); +} + +/*<asyxml><operator type = "pair" signature="*(coordsys,pair)"><code></asyxml>*/ +pair operator *(coordsys R, pair p) +{/*<asyxml></code><documentation>Return the coordinates of 'p' given in the + xy - coordinates 'R'. + For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), R * (0, 0) is (1, 2).</documentation></operator></asyxml>*/ + return R.relativetodefault(p); +} + +/*<asyxml><operator type = "path" signature="*(coordsys,path)"><code></asyxml>*/ +path operator *(coordsys R, path g) +{/*<asyxml></code><documentation>Return the reconstructed path applying R * pair to each node, pre and post control point of 'g'.</documentation></operator></asyxml>*/ + guide og = R * point(g, 0); + real l = length(g); + for(int i = 1; i <= l; ++i) + { + pair P = R * point(g, i); + pair post = R * postcontrol(g, i - 1); + pair pre = R * precontrol(g, i); + if(i == l && (cyclic(g))) + og = og..controls post and pre..cycle; + else + og = og..controls post and pre..P; + } + return og; +} + +/*<asyxml><operator type = "coordsys" signature="*(transform,coordsys)"><code></asyxml>*/ +coordsys operator *(transform t,coordsys R) +{/*<asyxml></code><documentation>Provide transform * coordsys. + Note that shiftless(t) is applied to R.i and R.j.</documentation></operator></asyxml>*/ + coordsys oc; + oc = cartesiansystem(t * R.O, shiftless(t) * R.i, shiftless(t) * R.j); + return oc; +} + +/*<asyxml><constant type = "coordsys" signature="defaultcoordsys"><code></asyxml>*/ +restricted coordsys defaultcoordsys = cartesiansystem(0, (1, 0), (0, 1));/*<asyxml></code><documentation>One can always refer to the default coordinate system using this constant.</documentation></constant></asyxml>*/ +/*<asyxml><variable type="coordsys" signature="currentcoordsys"><code></asyxml>*/ +coordsys currentcoordsys = defaultcoordsys;/*<asyxml></code><documentation>The coordinate system used by default.</documentation></variable></asyxml>*/ + +/*<asyxml><struct signature="point"><code></asyxml>*/ +struct point +{/*<asyxml></code><documentation>This structure replaces the pair to embed its coordinate system. + For example, if 'P = point(cartesiansystem((1, 2), i, j), (0, 0))', + P is equal to the pair (1, 2).</documentation></asyxml>*/ + /*<asyxml><property type = "coordsys" signature="coordsys"><code></asyxml>*/ + coordsys coordsys;/*<asyxml></code><documentation>The coordinate system of this point.</documentation></property><property type = "pair" signature="coordinates"><code></asyxml>*/ + restricted pair coordinates;/*<asyxml></code><documentation>The coordinates of this point relatively to the coordinate system 'coordsys'.</documentation></property><property type = "real" signature="x, y"><code></asyxml>*/ + restricted real x, y;/*<asyxml></code><documentation>The xpart and the ypart of 'coordinates'.</documentation></property></asyxml>*/ + /*<asyxml><method type = "" signature="init(coordsys,pair)"><code><property type = "real" signature="m"><code></asyxml>*/ + real m = 1;/*<asyxml></code><documentation>Used to cast mass<->point.</documentation></property></asyxml>*/ + void init(coordsys R, pair coordinates, real mass) + {/*<asyxml></code><documentation>The constructor.</documentation></method></asyxml>*/ + this.coordsys = R; + this.coordinates = coordinates; + this.x = coordinates.x; + this.y = coordinates.y; + this.m = mass; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="point" signature="point(coordsys,pair,real)"><code></asyxml>*/ +point point(coordsys R, pair p, real m = 1) +{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the + coordinate system 'R' and the mass 'm'.</documentation></function></asyxml>*/ + point op; + op.init(R, p, m); + return op; +} + +/*<asyxml><function type="point" signature="point(explicit pair,real)"><code></asyxml>*/ +point point(explicit pair p, real m) +{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the current + coordinate system and the mass 'm'.</documentation></function></asyxml>*/ + point op; + op.init(currentcoordsys, p, m); + return op; +} + +/*<asyxml><function type="point" signature="point(coordsys,explicit point,real)"><code></asyxml>*/ +point point(coordsys R, explicit point M, real m = M.m) +{/*<asyxml></code><documentation>Return the point of 'R' which has the coordinates of 'M' and the mass 'm'. + Do not confuse this routine with the further routine 'changecoordsys'.</documentation></function></asyxml>*/ + point op; + op.init(R, M.coordinates, M.m); + return op; +} + +/*<asyxml><function type="point" signature="changecoordsys(coordsys,point)"><code></asyxml>*/ +point changecoordsys(coordsys R, point M) +{/*<asyxml></code><documentation>Return the point 'M' in the coordinate system 'coordsys'. + In other words, the returned point marks the same plot as 'M' does.</documentation></function></asyxml>*/ + point op; + coordsys mco = M.coordsys; + op.init(R, R.defaulttorelative(mco.relativetodefault(M.coordinates)), M.m); + return op; +} + +/*<asyxml><function type="pair" signature="pair coordinates(point)"><code></asyxml>*/ +pair coordinates(point M) +{/*<asyxml></code><documentation>Return the coordinates of 'M' in its coordinate system.</documentation></function></asyxml>*/ + return M.coordinates; +} + +/*<asyxml><function type="bool" signature="bool samecoordsys(bool...point[])"><code></asyxml>*/ +bool samecoordsys(bool warn = true ... point[] M) +{/*<asyxml></code><documentation>Return true iff all the points have the same coordinate system. + If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/ + bool ret = true; + coordsys t = M[0].coordsys; + for (int i = 1; i < M.length; ++i) { + ret = (t == M[i].coordsys); + if(!ret) break; + t = M[i].coordsys; + } + if(warn && !ret) + warning("coodinatesystem", + "the coordinate system of two objects are not the same. +The operation will be done relative to the default coordinate system."); + return ret; +} + +/*<asyxml><function type="point[]" signature="standardizecoordsys(coordsys,bool...point[])"><code></asyxml>*/ +point[] standardizecoordsys(coordsys R = currentcoordsys, + bool warn = true ... point[] M) +{/*<asyxml></code><documentation>Return the points with the same coordinate system 'R'. + If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/ + point[] op = new point[]; + op = M; + if(!samecoordsys(warn ... M)) + for (int i = 1; i < M.length; ++i) + op[i] = changecoordsys(R, M[i]); + return op; +} + +/*<asyxml><operator type = "pair" signature="cast(point)"><code></asyxml>*/ +pair operator cast(point P) +{/*<asyxml></code><documentation>Cast point to pair.</documentation></operator></asyxml>*/ + return P.coordsys.relativetodefault(P.coordinates); +} + +/*<asyxml><operator type = "pair[]" signature="cast(point[])"><code></asyxml>*/ +pair[] operator cast(point[] P) +{/*<asyxml></code><documentation>Cast point[] to pair[].</documentation></operator></asyxml>*/ + pair[] op; + for (int i = 0; i < P.length; ++i) { + op.push((pair)P[i]); + } + return op; +} + +/*<asyxml><operator type = "point" signature="cast(pair)"><code></asyxml>*/ +point operator cast(pair p) +{/*<asyxml></code><documentation>Cast pair to point relatively to the current coordinate + system 'currentcoordsys'.</documentation></operator></asyxml>*/ + return point(currentcoordsys, p); +} + +/*<asyxml><operator type = "point[]" signature="cast(pair[])"><code></asyxml>*/ +point[] operator cast(pair[] p) +{/*<asyxml></code><documentation>Cast pair[] to point[] relatively to the current coordinate + system 'currentcoordsys'.</documentation></operator></asyxml>*/ + pair[] op; + for (int i = 0; i < p.length; ++i) { + op.push((point)p[i]); + } + return op; +} + +/*<asyxml><function type="pair" signature="locate(point)"><code></asyxml>*/ +pair locate(point P) +{/*<asyxml></code><documentation>Return the coordinates of 'P' in the default coordinate system.</documentation></function></asyxml>*/ + return P.coordsys * P.coordinates; +} + +/*<asyxml><function type="point" signature="locate(pair)"><code></asyxml>*/ +point locate(pair p) +{/*<asyxml></code><documentation>Return the point in the current coordinate system 'currentcoordsys'.</documentation></function></asyxml>*/ + return p; //automatic casting 'pair to point'. +} + +/*<asyxml><operator type = "point" signature="*(real,explicit point)"><code></asyxml>*/ +point operator *(real x, explicit point P) +{/*<asyxml></code><documentation>Multiply the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/ + return point(P.coordsys, x * P.coordinates, P.m); +} + +/*<asyxml><operator type = "point" signature="/(explicit point,real)"><code></asyxml>*/ +point operator /(explicit point P, real x) +{/*<asyxml></code><documentation>Divide the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/ + return point(P.coordsys, P.coordinates/x, P.m); +} + +/*<asyxml><operator type = "point" signature="/(real,explicit point)"><code></asyxml>*/ +point operator /(real x, explicit point P) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return point(P.coordsys, x/P.coordinates, P.m); +} + +/*<asyxml><operator type = "point" signature="-(explicit point)"><code></asyxml>*/ +point operator -(explicit point P) +{/*<asyxml></code><documentation>-P. The mass is inchanged.</documentation></operator></asyxml>*/ + return point(P.coordsys, -P.coordinates, P.m); +} + +/*<asyxml><operator type = "point" signature="+(explicit point,explicit point)"><code></asyxml>*/ +point operator +(explicit point P1, explicit point P2) +{/*<asyxml></code><documentation>Provide 'point + point'. + If the two points haven't the same coordinate system, a warning is sent and the + returned point has the default coordinate system 'defaultcoordsys'. + The masses are added.</documentation></operator></asyxml>*/ + point[] P = standardizecoordsys(P1, P2); + coordsys R = P[0].coordsys; + return point(R, P[0].coordinates + P[1].coordinates, P1.m + P2.m); +} + +/*<asyxml><operator type = "point" signature="+(explicit point,explicit pair)"><code></asyxml>*/ +point operator +(explicit point P1, explicit pair p2) +{/*<asyxml></code><documentation>Provide 'point + pair'. + The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'. + The mass is not changed.</documentation></operator></asyxml>*/ + coordsys R = currentcoordsys; + return point(R, P1.coordinates + point(R, p2).coordinates, P1.m); +} +point operator +(explicit pair p1, explicit point p2) +{ + return p2 + p1; +} + +/*<asyxml><operator type = "point" signature="-(explicit point,explicit point)"><code></asyxml>*/ +point operator -(explicit point P1, explicit point P2) +{/*<asyxml></code><documentation>Provide 'point - point'.</documentation></operator></asyxml>*/ + return P1 + (-P2); +} + +/*<asyxml><operator type = "point" signature="-(explicit point,explicit pair)"><code></asyxml>*/ +point operator -(explicit point P1, explicit pair p2) +{/*<asyxml></code><documentation>Provide 'point - pair'. + The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.</documentation></operator></asyxml>*/ + return P1 + (-p2); +} +point operator -(explicit pair p1, explicit point P2) +{ + return p1 + (-P2); +} + +/*<asyxml><operator type = "point" signature="*(transform,explicit point)"><code></asyxml>*/ +point operator *(transform t, explicit point P) +{/*<asyxml></code><documentation>Provide 'transform * point'. + Note that the transforms scale, xscale, yscale and rotate are carried out relatively + the default coordinate system 'defaultcoordsys' which is not desired for point + defined in an other coordinate system. + On can use scale(real, point), xscale(real, point), yscale(real, point), rotate(real, point), + scaleO(real), xscaleO(real), yscaleO(real) and rotateO(real) (described further) + to change the coordinate system of reference.</documentation></operator></asyxml>*/ + coordsys R = P.coordsys; + return point(R, (t * locate(P))/R, P.m); +} + +/*<asyxml><operator type = "point" signature="*(explicit point,explicit point)"><code></asyxml>*/ +point operator *(explicit point P1, explicit point P2) +{/*<asyxml></code><documentation>Provide 'point * point'. + The resulted mass is the mass of P2</documentation></operator></asyxml>*/ + point[] P = standardizecoordsys(P1, P2); + coordsys R = P[0].coordsys; + return point(R, P[0].coordinates * P[1].coordinates, P2.m); +} + +/*<asyxml><operator type = "point" signature="*(explicit point,explicit pair)"><code></asyxml>*/ +point operator *(explicit point P1, explicit pair p2) +{/*<asyxml></code><documentation>Provide 'point * pair'. + The pair 'p2' is supposed to be the coordinates of + the point in the coordinates system of 'P1'. + 'pair * point' is also defined.</documentation></operator></asyxml>*/ + point P = point(P1.coordsys, p2, P1.m); + return P1 * P; +} +point operator *(explicit pair p1, explicit point p2) +{ + return p2 * p1; +} + +/*<asyxml><operator type = "bool" signature="==(explicit point,explicit point)"><code></asyxml>*/ +bool operator ==(explicit point M, explicit point N) + {/*<asyxml></code><documentation>Provide the test 'M == N' wish returns true iff MN < EPS</documentation></operator></asyxml>*/ + return abs(locate(M) - locate(N)) < EPS; + } + +/*<asyxml><operator type = "bool" signature="!=(explicit point,explicit point)"><code></asyxml>*/ +bool operator !=(explicit point M, explicit point N) +{/*<asyxml></code><documentation>Provide the test 'M != N' wish return true iff MN >= EPS</documentation></operator></asyxml>*/ + return !(M == N); +} + +/*<asyxml><operator type = "guide" signature="cast(point)"><code></asyxml>*/ +guide operator cast(point p) +{/*<asyxml></code><documentation>Cast point to guide.</documentation></operator></asyxml>*/ + return locate(p); +} + +/*<asyxml><operator type = "path" signature="cast(point)"><code></asyxml>*/ +path operator cast(point p) +{/*<asyxml></code><documentation>Cast point to path.</documentation></operator></asyxml>*/ + return locate(p); +} + +/*<asyxml><function type="void" signature="dot(picture,Label,explicit point,align,string,pen)"><code></asyxml>*/ +void dot(picture pic = currentpicture, Label L, explicit point Z, + align align = NoAlign, + string format = defaultformat, pen p = currentpen) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + Label L = L.copy(); + L.position(locate(Z)); + if(L.s == "") { + if(format == "") format = defaultformat; + L.s = "("+format(format, Z.x)+", "+format(format, Z.y)+")"; + } + L.align(align, E); + L.p(p); + dot(pic, locate(Z), p); + add(pic, L); +} + +/*<asyxml><function type="real" signature="abs(coordsys,pair)"><code></asyxml>*/ +real abs(coordsys R, pair m) +{/*<asyxml></code><documentation>Return the modulus |m| in the coordinate system 'R'.</documentation></function></asyxml>*/ + return R.abs(m); +} + +/*<asyxml><function type="real" signature="abs(explicit point)"><code></asyxml>*/ +real abs(explicit point M) +{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system.</documentation></function></asyxml>*/ + return M.coordsys.abs(M.coordinates); +} + +/*<asyxml><function type="real" signature="length(explicit point)"><code></asyxml>*/ +real length(explicit point M) +{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system (same as 'abs').</documentation></function></asyxml>*/ + return M.coordsys.abs(M.coordinates); +} + +/*<asyxml><function type="point" signature="conj(explicit point)"><code></asyxml>*/ +point conj(explicit point M) +{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/ + return point(M.coordsys, conj(M.coordinates), M.m); +} + +/*<asyxml><function type="real" signature="degrees(explicit point,coordsys,bool)"><code></asyxml>*/ +real degrees(explicit point M, coordsys R = M.coordsys, bool warn = true) +{/*<asyxml></code><documentation>Return the angle of M (in degrees) relatively to 'R'.</documentation></function></asyxml>*/ + return (degrees(locate(M) - R.O, warn) - degrees(R.i))%360; +} + +/*<asyxml><function type="real" signature="angle(explicit point,coordsys,bool)"><code></asyxml>*/ +real angle(explicit point M, coordsys R = M.coordsys, bool warn = true) +{/*<asyxml></code><documentation>Return the angle of M (in radians) relatively to 'R'.</documentation></function></asyxml>*/ + return radians(degrees(M, R, warn)); +} + +bool Finite(explicit point z) +{ + return abs(z.x) < Infinity && abs(z.y) < Infinity; +} + +/*<asyxml><function type="bool" signature="finite(explicit point)"><code></asyxml>*/ +bool finite(explicit point p) +{/*<asyxml></code><documentation>Avoid to compute 'finite((pair)(infinite_point))'.</documentation></function></asyxml>*/ + return finite(p.coordinates); +} + +/*<asyxml><function type="real" signature="dot(point,point)"><code></asyxml>*/ +real dot(point A, point B) +{/*<asyxml></code><documentation>Return the dot product in the coordinate system of 'A'.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A.coordsys, A, B); + return P[0].coordsys.dot(P[0].coordinates, P[1].coordinates); +} + +/*<asyxml><function type="real" signature="dot(point,explicit pair)"><code></asyxml>*/ +real dot(point A, explicit pair B) +{/*<asyxml></code><documentation>Return the dot product in the default coordinate system. + dot(explicit pair, point) is also defined.</documentation></function></asyxml>*/ + return dot(locate(A), B); +} +real dot(explicit pair A, point B) +{ + return dot(A, locate(B)); +} + +/*<asyxml><function type="transforms" signature="rotateO(real)"><code></asyxml>*/ +transform rotateO(real a) +{/*<asyxml></code><documentation>Rotation around the origin of the current coordinate system.</documentation></function></asyxml>*/ + return rotate(a, currentcoordsys.O); +} + +/*<asyxml><function type="transform" signature="projection(point,point)"><code></asyxml>*/ +transform projection(point A, point B) +{/*<asyxml></code><documentation>Return the orthogonal projection on the line (AB).</documentation></function></asyxml>*/ + pair dir = unit(locate(A) - locate(B)); + pair a = locate(A); + real cof = dir.x * a.x + dir.y * a.y; + real tx = a.x - dir.x * cof; + real txx = dir.x^2; + real txy = dir.x * dir.y; + real ty = a.y - dir.y * cof; + real tyx = txy; + real tyy = dir.y^2; + transform t = (tx, ty, txx, txy, tyx, tyy); + return t; +} + +/*<asyxml><function type="transform" signature="projection(point,point,point,point,bool)"><code></asyxml>*/ +transform projection(point A, point B, point C, point D, bool safe = false) +{/*<asyxml></code><documentation>Return the (CD) parallel projection on (AB). + If 'safe = true' and (AB)//(CD) return the identity. + If 'safe = false' and (AB)//(CD) return an infinity scaling.</documentation></function></asyxml>*/ + pair a = locate(A); + pair u = unit(locate(B) - locate(A)); + pair v = unit(locate(D) - locate(C)); + real c = u.x * a.y - u.y * a.x; + real d = (conj(u) * v).y; + if (abs(d) < epsgeo) { + return safe ? identity() : scale(infinity); + } + real tx = c * v.x/d; + real ty = c * v.y/d; + real txx = u.x * v.y/d; + real txy = -u.x * v.x/d; + real tyx = u.y * v.y/d; + real tyy = -u.y * v.x/d; + transform t = (tx, ty, txx, txy, tyx, tyy); + return t; +} + +/*<asyxml><function type="transform" signature="scale(real,point)"><code></asyxml>*/ +transform scale(real k, point M) +{/*<asyxml></code><documentation>Homothety.</documentation></function></asyxml>*/ + pair P = locate(M); + return shift(P) * scale(k) * shift(-P); +} + +/*<asyxml><function type="transform" signature="xscale(real,point)"><code></asyxml>*/ +transform xscale(real k, point M) +{/*<asyxml></code><documentation>xscale from 'M' relatively to the x - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/ + pair P = locate(M); + real a = degrees(M.coordsys.i); + return (shift(P) * rotate(a)) * xscale(k) * (rotate(-a) * shift(-P)); +} + +/*<asyxml><function type="transform" signature="yscale(real,point)"><code></asyxml>*/ +transform yscale(real k, point M) +{/*<asyxml></code><documentation>yscale from 'M' relatively to the y - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/ + pair P = locate(M); + real a = degrees(M.coordsys.j) - 90; + return (shift(P) * rotate(a)) * yscale(k) * (rotate(-a) * shift(-P)); +} + +/*<asyxml><function type="transform" signature="scale(real,point,point,point,point,bool)"><code></asyxml>*/ +transform scale(real k, point A, point B, point C, point D, bool safe = false) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Affinit%C3%A9_%28math%C3%A9matiques%29"/> + (help me for English translation...) + If 'safe = true' and (AB)//(CD) return the identity. + If 'safe = false' and (AB)//(CD) return a infinity scaling.</documentation></function></asyxml>*/ + pair a = locate(A); + pair u = unit(locate(B) - locate(A)); + pair v = unit(locate(D) - locate(C)); + real c = u.x * a.y - u.y * a.x; + real d = (conj(u) * v).y; + real d = (conj(u) * v).y; + if (abs(d) < epsgeo) { + return safe ? identity() : scale(infinity); + } + real tx = (1 - k) * c * v.x/d; + real ty = (1 - k) * c * v.y/d; + real txx = (1 - k) * u.x * v.y/d + k; + real txy = (k - 1) * u.x * v.x/d; + real tyx = (1 - k) * u.y * v.y/d; + real tyy = (k - 1) * u.y * v.x/d + k; + transform t = (tx, ty, txx, txy, tyx, tyy); + return t; +} + +/*<asyxml><function type="transform" signature="scaleO(real)"><code></asyxml>*/ +transform scaleO(real x) +{/*<asyxml></code><documentation>Homothety from the origin of the current coordinate system.</documentation></function></asyxml>*/ + return scale(x, (0, 0)); +} + +/*<asyxml><function type="transform" signature="xscaleO(real)"><code></asyxml>*/ +transform xscaleO(real x) +{/*<asyxml></code><documentation>xscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/ + return scale(x, (0, 0), (0, 1), (0, 0), (1, 0)); +} + +/*<asyxml><function type="transform" signature="yscaleO(real)"><code></asyxml>*/ +transform yscaleO(real x) +{/*<asyxml></code><documentation>yscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/ + return scale(x, (0, 0), (1, 0), (0, 0), (0, 1)); +} + +/*<asyxml><struct signature="vector"><code></asyxml>*/ +struct vector +{/*<asyxml></code><documentation>Like a point but casting to pair, adding etc does not take account + of the origin of the coordinate system.</documentation><property type = "point" signature="v"><code></asyxml>*/ + point v;/*<asyxml></code><documentation>Coordinates as a point (embed coordinate system and pair).</documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><operator type = "point" signature="cast(vector)"><code></asyxml>*/ +point operator cast(vector v) +{/*<asyxml></code><documentation>Cast vector 'v' to point 'M' so that OM = v.</documentation></operator></asyxml>*/ + return v.v; +} + +/*<asyxml><operator type = "vector" signature="cast(pair)"><code></asyxml>*/ +vector operator cast(pair v) +{/*<asyxml></code><documentation>Cast pair to vector relatively to the current coordinate + system 'currentcoordsys'.</documentation></operator></asyxml>*/ + vector ov; + ov.v = point(currentcoordsys, v); + return ov; +} + +/*<asyxml><operator type = "vector" signature="cast(explicit point)"><code></asyxml>*/ +vector operator cast(explicit point v) +{/*<asyxml></code><documentation>A point can be interpreted like a vector using the code + '(vector)a_point'.</documentation></operator></asyxml>*/ + vector ov; + ov.v = v; + return ov; +} + +/*<asyxml><operator type = "pair" signature="cast(explicit vector)"><code></asyxml>*/ +pair operator cast(explicit vector v) +{/*<asyxml></code><documentation>Cast vector to pair (the coordinates of 'v' in the default coordinate system).</documentation></operator></asyxml>*/ + return locate(v.v) - v.v.coordsys.O; +} + +/*<asyxml><operator type = "align" signature="cast(vector)"><code></asyxml>*/ +align operator cast(vector v) +{/*<asyxml></code><documentation>Cast vector to align.</documentation></operator></asyxml>*/ + return (pair)v; +} + +/*<asyxml><function type="vector" signature="vector(coordsys, pair)"><code></asyxml>*/ +vector vector(coordsys R = currentcoordsys, pair v) +{/*<asyxml></code><documentation>Return the vector of 'R' which has the coordinates 'v'.</documentation></function></asyxml>*/ + vector ov; + ov.v = point(R, v); + return ov; +} + +/*<asyxml><function type="vector" signature="vector(point)"><code></asyxml>*/ +vector vector(point M) +{/*<asyxml></code><documentation>Return the vector OM, where O is the origin of the coordinate system of 'M'. + Useful to write 'vector(P - M);' instead of '(vector)(P - M)'.</documentation></function></asyxml>*/ + return M; +} + +/*<asyxml><function type="point" signature="point(explicit vector)"><code></asyxml>*/ +point point(explicit vector u) +{/*<asyxml></code><documentation>Return the point M so that OM = u, where O is the origin of the coordinate system of 'u'.</documentation></function></asyxml>*/ + return u.v; +} + +/*<asyxml><function type="pair" signature="locate(explicit vector)"><code></asyxml>*/ +pair locate(explicit vector v) +{/*<asyxml></code><documentation>Return the coordinates of 'v' in the default coordinate system (like casting vector to pair).</documentation></function></asyxml>*/ + return (pair)v; +} + +/*<asyxml><function type="void" signature="show(Label,pen,arrowbar)"><code></asyxml>*/ +void show(Label L, vector v, pen p = currentpen, arrowbar arrow = Arrow) +{/*<asyxml></code><documentation>Draw the vector v (from the origin of its coordinate system).</documentation></function></asyxml>*/ + coordsys R = v.v.coordsys; + draw(L, R.O--v.v, p, arrow); +} + +/*<asyxml><function type="vector" signature="changecoordsys(coordsys,vector)"><code></asyxml>*/ +vector changecoordsys(coordsys R, vector v) +{/*<asyxml></code><documentation>Return the vector 'v' relatively to coordinate system 'R'.</documentation></function></asyxml>*/ + vector ov; + ov.v = point(R, (locate(v) + R.O)/R); + return ov; +} + +/*<asyxml><operator type = "vector" signature="*(real,explicit vector)"><code></asyxml>*/ +vector operator *(real x, explicit vector v) +{/*<asyxml></code><documentation>Provide real * vector.</documentation></operator></asyxml>*/ + return x * v.v; +} + +/*<asyxml><operator type = "vector" signature="/(explicit vector,real)"><code></asyxml>*/ +vector operator /(explicit vector v, real x) +{/*<asyxml></code><documentation>Provide vector/real</documentation></operator></asyxml>*/ + return v.v/x; +} + +/*<asyxml><operator type = "vector" signature="*(transform t,explicit vector)"><code></asyxml>*/ +vector operator *(transform t, explicit vector v) +{/*<asyxml></code><documentation>Provide transform * vector.</documentation></operator></asyxml>*/ + return t * v.v; +} + +/*<asyxml><operator type = "vector" signature="*(explicit point,explicit vector)"><code></asyxml>*/ +vector operator *(explicit point M, explicit vector v) +{/*<asyxml></code><documentation>Provide point * vector</documentation></operator></asyxml>*/ + return M * v.v; +} + +/*<asyxml><operator type = "point" signature="+(explicit point,explicit vector)"><code></asyxml>*/ +point operator +(point M, explicit vector v) +{/*<asyxml></code><documentation>Return 'M' shifted by 'v'.</documentation></operator></asyxml>*/ + return shift(locate(v)) * M; +} + +/*<asyxml><operator type = "point" signature="-(explicit point,explicit vector)"><code></asyxml>*/ +point operator -(point M, explicit vector v) +{/*<asyxml></code><documentation>Return 'M' shifted by '-v'.</documentation></operator></asyxml>*/ + return shift(-locate(v)) * M; +} + +/*<asyxml><operator type = "vector" signature="-(explicit vector)"><code></asyxml>*/ +vector operator -(explicit vector v) +{/*<asyxml></code><documentation>Provide -v.</documentation></operator></asyxml>*/ + return -v.v; +} + +/*<asyxml><operator type = "point" signature="+(explicit pair,explicit vector)"><code></asyxml>*/ +point operator +(explicit pair m, explicit vector v) +{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of + a point in the current coordinates system 'currentcoordsys'. + Return this point shifted by the vector 'v'.</documentation></operator></asyxml>*/ + return locate(m) + v; +} + +/*<asyxml><operator type = "point" signature="-(explicit pair,explicit vector)"><code></asyxml>*/ +point operator -(explicit pair m, explicit vector v) +{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of + a point in the current coordinates system 'currentcoordsys'. + Return this point shifted by the vector '-v'.</documentation></operator></asyxml>*/ + return m + (-v); +} + +/*<asyxml><operator type = "vector" signature="+(explicit vector,explicit vector)"><code></asyxml>*/ +vector operator +(explicit vector v1, explicit vector v2) +{/*<asyxml></code><documentation>Provide vector + vector. + If the two vector haven't the same coordinate system, the returned + vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/ + coordsys R = v1.v.coordsys; + if(samecoordsys(false, v1, v2)){R = defaultcoordsys;} + return vector(R, (locate(v1) + locate(v2))/R); +} + +/*<asyxml><operator type = "vector" signature="-(explicit vector, explicit vector)"><code></asyxml>*/ +vector operator -(explicit vector v1, explicit vector v2) +{/*<asyxml></code><documentation>Provide vector - vector. + If the two vector haven't the same coordinate system, the returned + vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/ + return v1 + (-v2); +} + +/*<asyxml><operator type = "bool" signature="==(explicit vector,explicit vector)"><code></asyxml>*/ +bool operator ==(explicit vector u, explicit vector v) + {/*<asyxml></code><documentation>Return true iff |u - v|<EPS.</documentation></operator></asyxml>*/ + return abs(u - v) < EPS; + } + +/*<asyxml><function type="bool" signature="collinear(vector,vector)"><code></asyxml>*/ +bool collinear(vector u, vector v) +{/*<asyxml></code><documentation>Return 'true' iff the vectors 'u' and 'v' are collinear.</documentation></function></asyxml>*/ + return abs(ypart((conj((pair)u) * (pair)v))) < EPS; +} + +/*<asyxml><function type="vector" signature="unit(point)"><code></asyxml>*/ +vector unit(point M) +{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/ + return M/abs(M); +} + +/*<asyxml><function type="vector" signature="unit(vector)"><code></asyxml>*/ +vector unit(vector u) +{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/ + return u.v/abs(u.v); +} + +/*<asyxml><function type="real" signature="degrees(vector,coordsys,bool)"><code></asyxml>*/ +real degrees(vector v, + coordsys R = v.v.coordsys, + bool warn = true) +{/*<asyxml></code><documentation>Return the angle of 'v' (in degrees) relatively to 'R'.</documentation></function></asyxml>*/ + return (degrees(locate(v), warn) - degrees(R.i))%360; +} + +/*<asyxml><function type="real" signature="angle(vector,coordsys,bool)"><code></asyxml>*/ +real angle(explicit vector v, + coordsys R = v.v.coordsys, + bool warn = true) +{/*<asyxml></code><documentation>Return the angle of 'v' (in radians) relatively to 'R'.</documentation></function></asyxml>*/ + return radians(degrees(v, R, warn)); +} + +/*<asyxml><function type="vector" signature="conj(explicit vector)"><code></asyxml>*/ +vector conj(explicit vector u) +{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/ + return conj(u.v); +} + +/*<asyxml><function type="transform" signature="rotate(explicit vector)"><code></asyxml>*/ +transform rotate(explicit vector dir) +{/*<asyxml></code><documentation>A rotation in the direction 'dir' limited to [-90, 90] + This is useful for rotating text along a line in the direction dir. + rotate(explicit point dir) is also defined. + </documentation></function></asyxml>*/ + return rotate(locate(dir)); +} +transform rotate(explicit point dir){return rotate(locate(vector(dir)));} +// *......................COORDINATES......................* +// *=======================================================* + +// *=======================================================* +// *.........................BASES.........................* +/*<asyxml><variable type="point" signature="origin"><code></asyxml>*/ +point origin = point(defaultcoordsys, (0, 0));/*<asyxml></code><documentation>The origin of the current coordinate system.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="point" signature="origin(coordsys)"><code></asyxml>*/ +point origin(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return the origin of the coordinate system 'R'.</documentation></function></asyxml>*/ + return point(R, (0, 0)); //use automatic casting; +} + +/*<asyxml><variable type="real" signature="linemargin"><code></asyxml>*/ +real linemargin = 0;/*<asyxml></code><documentation>Margin used to draw lines.</documentation></variable></asyxml>*/ +/*<asyxml><function type="real" signature="linemargin()"><code></asyxml>*/ +real linemargin() +{/*<asyxml></code><documentation>Return the margin used to draw lines.</documentation></function></asyxml>*/ + return linemargin; +} + +/*<asyxml><variable type="pen" signature="addpenline"><code></asyxml>*/ +pen addpenline = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of "finish" lines.</documentation></variable></asyxml>*/ +pen addpenline(pen p) { + return addpenline + p; +} + +/*<asyxml><variable type="pen" signature="addpenarc"><code></asyxml>*/ +pen addpenarc = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of arcs.</documentation></variable></asyxml>*/ +pen addpenarc(pen p) {return addpenarc + p;} + +/*<asyxml><variable type="string" signature="defaultmassformat"><code></asyxml>*/ +string defaultmassformat = "$\left(%L;%.4g\right)$";/*<asyxml></code><documentation>Format used to construct the default label of masses.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="int" signature="sgnd(real)"><code></asyxml>*/ +int sgnd(real x) +{/*<asyxml></code><documentation>Return the -1 if x < 0, 1 if x >= 0.</documentation></function></asyxml>*/ + return (x == 0) ? 1 : sgn(x); +} +int sgnd(int x) +{ + return (x == 0) ? 1 : sgn(x); +} + +/*<asyxml><function type="bool" signature="defined(pair)"><code></asyxml>*/ +bool defined(point P) +{/*<asyxml></code><documentation>Return true iff the coordinates of 'P' are finite.</documentation></function></asyxml>*/ + return finite(P.coordinates); +} + +/*<asyxml><function type="bool" signature="onpath(picture,path,point,pen)"><code></asyxml>*/ +bool onpath(picture pic = currentpicture, path g, point M, pen p = currentpen) +{/*<asyxml></code><documentation>Return true iff 'M' is on the path drawn with the pen 'p' in 'pic'.</documentation></function></asyxml>*/ + transform t = inverse(pic.calculateTransform()); + return intersect(g, shift(locate(M)) * scale(linewidth(p)/2) * t * unitcircle).length > 0; +} + +/*<asyxml><function type="bool" signature="sameside(point,point,point)"><code></asyxml>*/ +bool sameside(point M, point N, point O) +{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the point 'O'.</documentation></function></asyxml>*/ + pair m = M, n = N, o = O; + return dot(m - o, n - o) >= -epsgeo; +} + +/*<asyxml><function type="bool" signature="between(point,point,point)"><code></asyxml>*/ +bool between(point M, point O, point N) +{/*<asyxml></code><documentation>Return 'true' iff 'O' is between 'M' and 'N'.</documentation></function></asyxml>*/ + return (!sameside(N, M, O) || M == O || N == O); +} + + +typedef path pathModifier(path); +pathModifier NoModifier = new path(path g){return g;}; + +private void Drawline(picture pic = currentpicture, Label L = "", pair P, bool dirP = true, pair Q, bool dirQ = true, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, + Label legend = "", marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/* Add the two parameters 'dirP' and 'dirQ' to the native routine + 'drawline' of the module 'math'. + Segment [PQ] will be prolonged in direction of P if 'dirP = true', in + direction of Q if 'dirQ = true'. + If 'dirP = dirQ = true', the behavior is that of the native 'drawline'. + Add all the other parameters of 'Draw'.*/ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + picture opic; + // Reduce the bounds by the size of the pen. + m -= min(p) - (linemargin(), linemargin()); M -= max(p) + (linemargin(), linemargin()); + + // Calculate the points and direction vector in the transformed space. + t = t * T; + pair z = t * P; + pair q = t * Q; + pair v = q - z; + // path g; + pair ptp, ptq; + real cp = dirP ? 1:0; + real cq = dirQ ? 1:0; + // Handle horizontal and vertical lines. + if(v.x == 0) { + if(m.x <= z.x && z.x <= M.x) + if (dot(v, m - z) < 0) { + ptp = (z.x, z.y + cp * (m.y - z.y)); + ptq = (z.x, q.y + cq * (M.y - q.y)); + } else { + ptq = (z.x, q.y + cq * (m.y - q.y)); + ptp = (z.x, z.y + cp * (M.y - z.y)); + } + } else if(v.y == 0) { + if (dot(v, m - z) < 0) { + ptp = (z.x + cp * (m.x - z.x), z.y); + ptq = (q.x + cq * (M.x - q.x), z.y); + } else { + ptq = (q.x + cq * (m.x - q.x), z.y); + ptp = (z.x + cp * (M.x - z.x), z.y); + } + } else { + // Calculate the maximum and minimum t values allowed for the + // parametric equation z + t * v + real mx = (m.x - z.x)/v.x, Mx = (M.x - z.x)/v.x; + real my = (m.y - z.y)/v.y, My = (M.y - z.y)/v.y; + real tmin = max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My); + real tmax = min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my); + pair pmin = z + tmin * v; + pair pmax = z + tmax * v; + if(tmin <= tmax) { + ptp = z + cp * tmin * v; + ptq = z + (cq == 0 ? v:tmax * v); + } + } + path g = ptp--ptq; + if (length(g)>0) + { + if(L.s != "") { + Label lL = L.copy(); + if(L.defaultposition) lL.position(Relative(.9)); + lL.p(p); + lL.out(opic, g); + } + g = pathModifier(g); + if(linetype(p).length == 0){ + pair m = midpoint(g); + pen tp; + tp = dirP ? p : addpenline(p); + draw(opic, pathModifier(m--ptp), tp); + tp = dirQ ? p : addpenline(p); + draw(opic, pathModifier(m--ptq), tp); + } else { + draw(opic, g, p); + } + marker.markroutine(opic, marker.f, g); + arrow(opic, g, p, NoMargin); + add(f, opic.fit()); + } + }); +} + +/*<asyxml><function type="void" signature="clipdraw(picture,Label,path,align,pen,arrowbar,arrowbar,real,real,Label,marker)"><code></asyxml>*/ +void clipdraw(picture pic = currentpicture, Label L = "", path g, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + real xmargin = 0, real ymargin = xmargin, + Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw the path 'g' on 'pic' clipped to the bounding box of 'pic'.</documentation></function></asyxml>*/ + if(L.s != "") { + picture tmp; + label(tmp, L, g, p); + add(pic, tmp); + } + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen and the margins. + m += min(p) + (xmargin, ymargin); M -= max(p) + (xmargin, ymargin); + path bound = box(m, M); + picture tmp; + draw(tmp, "", t * T * g, align, p, arrow, bar, NoMargin, legend, marker); + clip(tmp, bound); + add(f, tmp.fit()); + }); +} + +/*<asyxml><function type="void" signature="distance(picture pic,Label,point,point,bool,real,pen,pen,arrow)"><code></asyxml>*/ +void distance(picture pic = currentpicture, Label L = "", point A, point B, + bool rotated = true, real offset = 3mm, + pen p = currentpen, pen joinpen = invisible, + arrowbar arrow = Arrows(NoFill)) +{/*<asyxml></code><documentation>Draw arrow between A and B (from FAQ).</documentation></function></asyxml>*/ + pair A = A, B = B; + path g = A--B; + transform Tp = shift(-offset * unit(B - A) * I); + pic.add(new void(frame f, transform t) { + picture opic; + path G = Tp * t * g; + transform id = identity(); + transform T = rotated ? rotate(B - A) : id; + Label L = L.copy(); + L.align(L.align, Center); + if(abs(ypart((conj(A - B) * L.align.dir))) < epsgeo && L.filltype == NoFill) + L.filltype = UnFill(1); + draw(opic, T * L, G, p, arrow, Bars, PenMargins); + pair Ap = t * A, Bp = t * B; + draw(opic, (Ap--Tp * Ap)^^(Bp--Tp * Bp), joinpen); + add(f, opic.fit()); + }, true); + pic.addBox(min(g), max(g), Tp * min(p), Tp * max(p)); +} + +/*<asyxml><variable type="real" signature="perpfactor"><code></asyxml>*/ +real perpfactor = 1;/*<asyxml></code><documentation>Factor for drawing perpendicular symbol.</documentation></variable></asyxml>*/ +/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,explicit pair,real,pen,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, point z, + explicit pair align, + explicit pair dir = E, real size = 0, + pen p = currentpen, + margin margin = NoMargin, + filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align + relative to the path z--z + dir. + dir(45 + n * 90), where n in N*, are common values for 'align'.</documentation></function></asyxml>*/ + p = squarecap + miterjoin + p; + if(size == 0) size = perpfactor * 3mm + linewidth(p) / 2; + 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) * dir(Op--Ap, Op--Bp); + 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 equation ax + by + c = 0 + restricted real slope, origin; + segment copy() + { + segment s = new segment; + s.A = A; + s.B = B; + s.a = a; + s.b = b; + s.c = c; + s.slope = slope; + s.origin = origin; + s.u = u; + s.v = v; + return s; + } + + void init(point A, point B) + { + line l; + l.init(A, B); + this.A = l.A; this.B = l.B; + this.a = l.a; this.b = l.b; this.c = l.c; + this.slope = l.slope; this.origin = l.origin; + this.u = l.u; this.v = l.v; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="segment" signature="segment(point,point)"><code></asyxml>*/ +segment segment(point A, point B) +{/*<asyxml></code><documentation>Return the segment whose the extremities are A and B.</documentation></function></asyxml>*/ + segment s; + s.init(A, B); + return s; +} + +/*<asyxml><function type="real" signature="length(segment)"><code></asyxml>*/ +real length(segment s) +{/*<asyxml></code><documentation>Return the length of 's'.</documentation></function></asyxml>*/ + return abs(s.A - s.B); +} + +/*<asyxml><operator type = "line" signature="cast(segment)"><code></asyxml>*/ +line operator cast(segment s) +{/*<asyxml></code><documentation>A segment is casted to a "finite line".</documentation></operator></asyxml>*/ + return line(s.A, false, s.B, false); +} + +/*<asyxml><operator type = "segment" signature="cast(line)"><code></asyxml>*/ +segment operator cast(line l) +{/*<asyxml></code><documentation>Cast line 'l' to segment [l.A l.B].</documentation></operator></asyxml>*/ + return segment(l.A, l.B); +} + +/*<asyxml><operator type = "line" signature="*(transform,line)"><code></asyxml>*/ +line operator *(transform t, line l) +{/*<asyxml></code><documentation>Provide transform * line</documentation></operator></asyxml>*/ + return line(t * l.A, l.extendA, t * l.B, l.extendB); +} +/*<asyxml><operator type = "line" signature="/(line,real)"><code></asyxml>*/ +line operator /(line l, real x) +{/*<asyxml></code><documentation>Provide l/x. + Return the line passing through l.A/x and l.B/x.</documentation></operator></asyxml>*/ + return line(l.A/x, l.extendA, l.B/x, l.extendB); +} +line operator /(line l, int x){return line(l.A/x, l.B/x);} +/*<asyxml><operator type = "line" signature="*(real,line)"><code></asyxml>*/ +line operator *(real x, line l) +{/*<asyxml></code><documentation>Provide x * l. + Return the line passing through x * l.A and x * l.B.</documentation></operator></asyxml>*/ + return line(x * l.A, l.extendA, x * l.B, l.extendB); +} +line operator *(int x, line l){return line(x * l.A, l.extendA, x * l.B, l.extendB);} + +/*<asyxml><operator type = "line" signature="*(point,line)"><code></asyxml>*/ +line operator *(point M, line l) +{/*<asyxml></code><documentation>Provide point * line. + Return the line passing through unit(M) * l.A and unit(M) * l.B.</documentation></operator></asyxml>*/ + return line(unit(M) * l.A, l.extendA, unit(M) * l.B, l.extendB); +} +/*<asyxml><operator type = "line" signature="+(line,point)"><code></asyxml>*/ +line operator +(line l, vector u) +{/*<asyxml></code><documentation>Provide line + vector (and so line + point). + Return the line 'l' shifted by 'u'.</documentation></operator></asyxml>*/ + return line(l.A + u, l.extendA, l.B + u, l.extendB); +} +/*<asyxml><operator type = "line" signature="-(line,vector)"><code></asyxml>*/ +line operator -(line l, vector u) +{/*<asyxml></code><documentation>Provide line - vector (and so line - point). + Return the line 'l' shifted by '-u'.</documentation></operator></asyxml>*/ + return line(l.A - u, l.extendA, l.B - u, l.extendB); +} + +/*<asyxml><operator type = "line[]" signature="^^(line,line)"><code></asyxml>*/ +line[] operator ^^(line l1, line l2) +{/*<asyxml></code><documentation>Provide line^^line. + Return the line array {l1, l2}.</documentation></operator></asyxml>*/ + line[] ol; + ol.push(l1); ol.push(l2); + return ol; +} + +/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/ +line[] operator ^^(line l1, line[] l2) +{/*<asyxml></code><documentation>Provide line^^line[]. + Return the line array {l1, l2[0], l2[1]...}. + line[]^^line is also defined.</documentation></operator></asyxml>*/ + line[] ol; + ol.push(l1); + for (int i = 0; i < l2.length; ++i) { + ol.push(l2[i]); + } + return ol; +} +line[] operator ^^(line[] l2, line l1) +{ + line[] ol = l2; + ol.push(l1); + return ol; +} + +/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/ +line[] operator ^^(line l1[], line[] l2) +{/*<asyxml></code><documentation>Provide line[]^^line[]. + Return the line array {l1[0], l1[1], ..., l2[0], l2[1], ...}.</documentation></operator></asyxml>*/ + line[] ol = l1; + for (int i = 0; i < l2.length; ++i) { + ol.push(l2[i]); + } + return ol; +} + +/*<asyxml><function type="bool" signature="sameside(point,point,line)"><code></asyxml>*/ +bool sameside(point M, point P, line l) +{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the line (or on the line) 'l'.</documentation></function></asyxml>*/ + pair A = l.A, B = l.B, m = M, p = P; + pair mil = (A + B)/2; + pair mA = rotate(90, mil) * A; + pair mB = rotate(-90, mil) * A; + return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB)); + // transform proj = projection(l.A, l.B); + // point Mp = proj * M; + // point Pp = proj * P; + // dot(Mp);dot(Pp); + // return dot(locate(Mp - M), locate(Pp - P)) >= 0; +} + +/*<asyxml><function type="line" signature="line(segment)"><code></asyxml>*/ +line line(segment s) +{/*<asyxml></code><documentation>Return the line passing through 's.A' + and 's.B'.</documentation></function></asyxml>*/ + return line(s.A, s.B); +} +/*<asyxml><function type="segment" signature="segment(line)"><code></asyxml>*/ +segment segment(line l) +{/*<asyxml></code><documentation>Return the segment whose extremities + are 'l.A' and 'l.B'.</documentation></function></asyxml>*/ + return segment(l.A, l.B); +} + +/*<asyxml><function type="point" signature="midpoint(segment)"><code></asyxml>*/ +point midpoint(segment s) +{/*<asyxml></code><documentation>Return the midpoint of 's'.</documentation></function></asyxml>*/ + return 0.5 * (s.A + s.B); +} + +/*<asyxml><function type="void" signature="write(line)"><code></asyxml>*/ +void write(explicit line l) +{/*<asyxml></code><documentation>Write some informations about 'l'.</documentation></function></asyxml>*/ + write("A = "+(string)((pair)l.A)); + write("Extend A = "+(l.extendA ? "true" : "false")); + write("B = "+(string)((pair)l.B)); + write("Extend B = "+(l.extendB ? "true" : "false")); + write("u = "+(string)((pair)l.u)); + write("v = "+(string)((pair)l.v)); + write("a = "+(string) l.a); + write("b = "+(string) l.b); + write("c = "+(string) l.c); + write("slope = "+(string) l.slope); + write("origin = "+(string) l.origin); +} + +/*<asyxml><function type="void" signature="write(explicit segment)"><code></asyxml>*/ +void write(explicit segment s) +{/*<asyxml></code><documentation>Write some informations about 's'.</documentation></function></asyxml>*/ + write("A = "+(string)((pair)s.A)); + write("B = "+(string)((pair)s.B)); + write("u = "+(string)((pair)s.u)); + write("v = "+(string)((pair)s.v)); + write("a = "+(string) s.a); + write("b = "+(string) s.b); + write("c = "+(string) s.c); + write("slope = "+(string) s.slope); + write("origin = "+(string) s.origin); +} + +/*<asyxml><operator type = "bool" signature="==(line,line)"><code></asyxml>*/ +bool operator ==(line l1, line l2) + {/*<asyxml></code><documentation>Provide the test 'line == line'.</documentation></operator></asyxml>*/ + return (collinear(l1.u, l2.u) && + abs(ypart((locate(l1.A) - locate(l1.B))/(locate(l1.A) - locate(l2.B)))) < epsgeo && + l1.extendA == l2.extendA && l1.extendB == l2.extendB); + } + +/*<asyxml><operator type = "bool" signature="!=(line,line)"><code></asyxml>*/ +bool operator !=(line l1, line l2) +{/*<asyxml></code><documentation>Provide the test 'line != line'.</documentation></operator></asyxml>*/ + return !(l1 == l2); +} + +/*<asyxml><operator type = "bool" signature="@(point,line)"><code></asyxml>*/ +bool operator @(point m, line l) +{/*<asyxml></code><documentation>Provide the test 'point @ line'. + Return true iff 'm' is on the 'l'.</documentation></operator></asyxml>*/ + point M = changecoordsys(l.A.coordsys, m); + if (abs(l.a * M.x + l.b * M.y + l.c) >= epsgeo) return false; + if (l.extendA && l.extendB) return true; + if (!l.extendA && !l.extendB) return between(l.A, M, l.B); + if (l.extendA) return sameside(M, l.A, l.B); + return sameside(M, l.B, l.A); +} + +/*<asyxml><function type="coordsys" signature="coordsys(line)"><code></asyxml>*/ +coordsys coordsys(line l) +{/*<asyxml></code><documentation>Return the coordinate system in which 'l' is defined.</documentation></function></asyxml>*/ + return l.A.coordsys; +} + +/*<asyxml><function type="line" signature="reverse(line)"><code></asyxml>*/ +line reverse(line l) +{/*<asyxml></code><documentation>Permute the points 'A' and 'B' of 'l' and so its orientation.</documentation></function></asyxml>*/ + return line(l.B, l.extendB, l.A, l.extendA); +} + +/*<asyxml><function type="line" signature="extend(line)"><code></asyxml>*/ +line extend(line l) +{/*<asyxml></code><documentation>Return the infinite line passing through 'l.A' and 'l.B'.</documentation></function></asyxml>*/ + line ol = l.copy(); + ol.extendA = true; + ol.extendB = true; + return ol; +} + +/*<asyxml><function type="line" signature="complementary(explicit line)"><code></asyxml>*/ +line complementary(explicit line l) +{/*<asyxml></code><documentation>Return the complementary of a half-line with respect of + the full line 'l'.</documentation></function></asyxml>*/ + if (l.extendA && l.extendB) + abort("complementary: the parameter is not a half-line."); + point origin = l.extendA ? l.B : l.A; + point ptdir = l.extendA ? + rotate(180, l.B) * l.A : rotate(180, l.A) * l.B; + return line(origin, false, ptdir); +} + +/*<asyxml><function type="line[]" signature="complementary(explicit segment)"><code></asyxml>*/ +line[] complementary(explicit segment s) +{/*<asyxml></code><documentation>Return the two half-lines of origin 's.A' and 's.B' respectively.</documentation></function></asyxml>*/ + line[] ol = new line[2]; + ol[0] = complementary(line(s.A, false, s.B)); + ol[1] = complementary(line(s.A, s.B, false)); + return ol; +} + +/*<asyxml><function type="line" signature="Ox(coordsys)"><code></asyxml>*/ +line Ox(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return the x-axis of 'R'.</documentation></function></asyxml>*/ + return line(point(R, (0, 0)), point(R, E)); +} +/*<asyxml><constant type = "line" signature="Ox"><code></asyxml>*/ +restricted line Ox = Ox();/*<asyxml></code><documentation>the x-axis of + the default coordinate system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="Oy(coordsys)"><code></asyxml>*/ +line Oy(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return the y-axis of 'R'.</documentation></function></asyxml>*/ + return line(point(R, (0, 0)), point(R, N)); +} +/*<asyxml><constant type = "line" signature="Oy"><code></asyxml>*/ +restricted line Oy = Oy();/*<asyxml></code><documentation>the y-axis of + the default coordinate system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="line(real,point)"><code></asyxml>*/ +line line(real a, point A = point(currentcoordsys, (0, 0))) +{/*<asyxml></code><documentation>Return the line passing through 'A' with an + angle (in the coordinate system of A) 'a' in degrees. + line(point, real) is also defined.</documentation></function></asyxml>*/ + return line(A, A + point(A.coordsys, A.coordsys.polar(1, radians(a)))); +} +line line(point A = point(currentcoordsys, (0, 0)), real a) +{ + return line(a, A); +} +line line(int a, point A = point(currentcoordsys, (0, 0))) +{ + return line((real)a, A); +} + +/*<asyxml><function type="line" signature="line(coordsys,real,real)"><code></asyxml>*/ +line line(coordsys R = currentcoordsys, real slope, real origin) +{/*<asyxml></code><documentation>Return the line defined by slope and y-intercept relative to 'R'.</documentation></function></asyxml>*/ + if (slope == infinity || slope == -infinity) + abort("The slope is infinite. Please, use the routine 'vline'."); + return line(point(R, (0, origin)), point(R, (1, origin + slope))); +} + +/*<asyxml><function type="line" signature="line(coordsys,real,real,real)"><code></asyxml>*/ +line line(coordsys R = currentcoordsys, real a, real b, real c) +{/*<asyxml></code><documentation>Retrun the line defined by equation relative to 'R'.</documentation></function></asyxml>*/ + if (a == 0 && b == 0) abort("line: inconsistent equation..."); + pair M; + M = (a == 0) ? (0, -c/b) : (-c/a, 0); + return line(point(R, M), point(R, M + (-b, a))); +} + +/*<asyxml><function type="line" signature="vline(coordsys)"><code></asyxml>*/ +line vline(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return a vertical line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/ + point P = point(R, (0, 0)); + point PP = point(R, (R.O + N)/R); + return line(P, PP); +} +/*<asyxml><constant type = "line" signature="vline"><code></asyxml>*/ +restricted line vline = vline();/*<asyxml></code><documentation>The vertical line in the current coordinate system passing + through the origin of this system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="hline(coordsys)"><code></asyxml>*/ +line hline(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return a horizontal line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/ + point P = point(R, (0, 0)); + point PP = point(R, (R.O + E)/R); + return line(P, PP); +} +/*<asyxml><constant type = "line" signature="hline"><code></asyxml>*/ +line hline = hline();/*<asyxml></code><documentation>The horizontal line in the current coordinate system passing + through the origin of this system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="changecoordsys(coordsys,line)"><code></asyxml>*/ +line changecoordsys(coordsys R, line l) +{/*<asyxml></code><documentation>Return the line 'l' in the coordinate system 'R'.</documentation></function></asyxml>*/ + point A = changecoordsys(R, l.A); + point B = changecoordsys(R, l.B); + return line(A, B); +} + +/*<asyxml><function type="transform" signature="scale(real,line,line,bool)"><code></asyxml>*/ +transform scale(real k, line l1, line l2, bool safe = false) +{/*<asyxml></code><documentation>Return the dilatation with respect to + 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/ + return scale(k, l1.A, l1.B, l2.A, l2.B, safe); +} + +/*<asyxml><function type="transform" signature="reflect(line)"><code></asyxml>*/ +transform reflect(line l) +{/*<asyxml></code><documentation>Return the reflect about the line 'l'.</documentation></function></asyxml>*/ + return reflect((pair)l.A, (pair)l.B); +} + +/*<asyxml><function type="transform" signature="reflect(line,line)"><code></asyxml>*/ +transform reflect(line l1, line l2, bool safe = false) +{/*<asyxml></code><documentation>Return the reflect about the line + 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/ + return scale(-1.0, l1, l2, safe); +} + + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,path)"><code></asyxml>*/ +point[] intersectionpoints(line l, path g) +{/*<asyxml></code><documentation>Return all points of intersection of the line 'l' with the path 'g'.</documentation></function></asyxml>*/ + // TODO utiliser la version 1.44 de intersections(path g, pair p, pair q) + // real [] t = intersections(g, l.A, l.B); + // coordsys R = coordsys(l); + // return sequence(new point(int n){return point(R, point(g, t[n])/R);}, t.length); + real [] t; + pair[] op; + pair A = l.A; + pair B = l.B; + real dy = B.y - A.y, + dx = A.x - B.x, + lg = length(g); + + for (int i = 0; i < lg; ++i) + { + pair z0 = point(g, i), + z1 = point(g, i + 1), + c0 = postcontrol(g, i), + c1 = precontrol(g, i + 1), + t3 = z1 - z0 - 3 * c1 + 3 * c0, + t2 = 3 * z0 + 3 * c1 - 6 * c0, + t1 = 3 * c0 - 3z0; + real a = dy * t3.x + dx * t3.y, + b = dy * t2.x + dx * t2.y, + c = dy * t1.x + dx * t1.y, + d = dy * z0.x + dx * z0.y + A.y * B.x - A.x * B.y; + + t = cubicroots(a, b, c, d); + for (int j = 0; j < t.length; ++j) + if ( + t[j]>=0 + && ( + t[j]<1 + || ( + t[j] == 1 + && (i == lg - 1) + && !cyclic(g) + ) + ) + ) { + op.push(point(g, i + t[j])); + } + } + + point[] opp; + for (int i = 0; i < op.length; ++i) + opp.push(point(coordsys(l), op[i]/coordsys(l))); + return opp; +} + +/*<asyxml><function type="point" signature="intersectionpoint(line,line)"><code></asyxml>*/ +point intersectionpoint(line l1, line l2) +{/*<asyxml></code><documentation>Return the point of intersection of line 'l1' with 'l2'. + If 'l1' and 'l2' have an infinity or none point of intersection, + this routine return (infinity, infinity).</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(l1.A, l1.B, l2.A, l2.B); + coordsys R = P[0].coordsys; + pair p = extension(P[0], P[1], P[2], P[3]); + if(finite(p)){ + point p = point(R, p/R); + if (p @ l1 && p @ l2) return p; + } + return point(R, (infinity, infinity)); +} + +/*<asyxml><function type="line" signature="parallel(point,line)"><code></asyxml>*/ +line parallel(point M, line l) +{/*<asyxml></code><documentation>Return the line parallel to 'l' passing through 'M'.</documentation></function></asyxml>*/ + point A, B; + if (M.coordsys != coordsys(l)) + { + A = changecoordsys(M.coordsys, l.A); + B = changecoordsys(M.coordsys, l.B); + } else {A = l.A;B = l.B;} + return line(M, M - A + B); +} + +/*<asyxml><function type="line" signature="parallel(point,explicit vector)"><code></asyxml>*/ +line parallel(point M, explicit vector dir) +{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/ + return line(M, M + locate(dir)); +} + +/*<asyxml><function type="line" signature="parallel(point,explicit pair)"><code></asyxml>*/ +line parallel(point M, explicit pair dir) +{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/ + return line(M, M + vector(currentcoordsys, dir)); +} + +/*<asyxml><function type="bool" signature="parallel(line,line)"><code></asyxml>*/ +bool parallel(line l1, line l2, bool strictly = false) +{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are (strictly ?) parallel.</documentation></function></asyxml>*/ + bool coll = collinear(l1.u, l2.u); + return strictly ? coll && (l1 != l2) : coll; +} + +/*<asyxml><function type="bool" signature="concurrent(...line[])"><code></asyxml>*/ +bool concurrent(... line[] l) +{/*<asyxml></code><documentation>Returns true if all the lines 'l' are concurrent.</documentation></function></asyxml>*/ + if (l.length < 3) abort("'concurrent' needs at least for three lines ..."); + pair point = intersectionpoint(l[0], l[1]); + bool conc; + for (int i = 2; i < l.length; ++i) { + pair pt = intersectionpoint(l[i - 1], l[i]); + conc = simeq(pt, point); + if (!conc) break; + } + return conc; +} + +/*<asyxml><function type="transform" signature="projection(line)"><code></asyxml>*/ +transform projection(line l) +{/*<asyxml></code><documentation>Return the orthogonal projection on 'l'.</documentation></function></asyxml>*/ + return projection(l.A, l.B); +} + +/*<asyxml><function type="transform" signature="projection(line,line,bool)"><code></asyxml>*/ +transform projection(line l1, line l2, bool safe = false) +{/*<asyxml></code><documentation>Return the projection on (AB) in parallel of (CD). + If 'safe = true' and (l1)//(l2) return the identity. + If 'safe = false' and (l1)//(l2) return a infinity scaling.</documentation></function></asyxml>*/ + return projection(l1.A, l1.B, l2.A, l2.B, safe); +} + +/*<asyxml><function type="transform" signature="vprojection(line,bool)"><code></asyxml>*/ +transform vprojection(line l, bool safe = false) +{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of N--S. + If 'safe' is 'true' the projected point keeps the same place if 'l' + is vertical.</documentation></function></asyxml>*/ + coordsys R = defaultcoordsys; + return projection(l, line(point(R, N), point(R, S)), safe); +} + +/*<asyxml><function type="transform" signature="hprojection(line,bool)"><code></asyxml>*/ +transform hprojection(line l, bool safe = false) +{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of E--W. + If 'safe' is 'true' the projected point keeps the same place if 'l' + is horizontal.</documentation></function></asyxml>*/ + coordsys R = defaultcoordsys; + return projection(l, line(point(R, E), point(R, W)), safe); +} + +/*<asyxml><function type="line" signature="perpendicular(point,line)"><code></asyxml>*/ +line perpendicular(point M, line l) +{/*<asyxml></code><documentation>Return the perpendicular line of 'l' passing through 'M'.</documentation></function></asyxml>*/ + point Mp = projection(l) * M; + point A = Mp == l.A ? l.B : l.A; + return line(Mp, rotate(90, Mp) * A); +} + +/*<asyxml><function type="line" signature="perpendicular(point,explicit vector)"><code></asyxml>*/ +line perpendicular(point M, explicit vector normal) +{/*<asyxml></code><documentation>Return the line passing through 'M' + whose normal is \param{normal}.</documentation></function></asyxml>*/ + return perpendicular(M, line(M, M + locate(normal))); +} + +/*<asyxml><function type="line" signature="perpendicular(point,explicit pair)"><code></asyxml>*/ +line perpendicular(point M, explicit pair normal) +{/*<asyxml></code><documentation>Return the line passing through 'M' + whose normal is \param{normal} (given in the currentcoordsys).</documentation></function></asyxml>*/ + return perpendicular(M, line(M, M + vector(currentcoordsys, normal))); +} + +/*<asyxml><function type="bool" signature="perpendicular(line,line)"><code></asyxml>*/ +bool perpendicular(line l1, line l2) +{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are perpendicular.</documentation></function></asyxml>*/ + return abs(dot(locate(l1.u), locate(l2.u))) < epsgeo ; +} + +/*<asyxml><function type="real" signature="angle(line,coordsys)"><code></asyxml>*/ +real angle(line l, coordsys R = coordsys(l)) +{/*<asyxml></code><documentation>Return the angle of the oriented line 'l', + in radian, in the interval ]-pi, pi] and relatively to 'R'.</documentation></function></asyxml>*/ + return angle(l.u, R, false); +} + +/*<asyxml><function type="real" signature="degrees(line,coordsys,bool)"><code></asyxml>*/ +real degrees(line l, coordsys R = coordsys(l)) +{/*<asyxml></code><documentation>Returns the angle of the oriented line 'l' in degrees, + in the interval [0, 360[ and relatively to 'R'.</documentation></function></asyxml>*/ + return degrees(angle(l, R)); +} + +/*<asyxml><function type="real" signature="sharpangle(line,line)"><code></asyxml>*/ +real sharpangle(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in radians of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/ + vector u1 = l1.u; + vector u2 = (dot(l1.u, l2.u) < 0) ? -l2.u : l2.u; + real a12 = angle(locate(u2)) - angle(locate(u1)); + a12 = a12%(sgnd(a12) * pi); + if (a12 <= -pi/2) { + a12 += pi; + } else if (a12 > pi/2) { + a12 -= pi; + } + return a12; +} + +/*<asyxml><function type="real" signature="angle(line,line)"><code></asyxml>*/ +real angle(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in radians of oriented angle (l1.u, l2.u).</documentation></function></asyxml>*/ + return angle(locate(l2.u)) - angle(locate(l1.u)); +} + +/*<asyxml><function type="real" signature="degrees(line,line)"><code></asyxml>*/ +real degrees(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in degrees of the + angle formed by the oriented lines 'l1' and 'l2'.</documentation></function></asyxml>*/ + return degrees(angle(l1, l2)); +} + +/*<asyxml><function type="real" signature="sharpdegrees(line,line)"><code></asyxml>*/ +real sharpdegrees(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in degrees of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/ + return degrees(sharpangle(l1, l2)); +} + +/*<asyxml><function type="line" signature="bisector(line,line,real,bool)"><code></asyxml>*/ +line bisector(line l1, line l2, real angle = 0, bool sharp = true) +{/*<asyxml></code><documentation>Return the bisector of the angle formed by 'l1' and 'l2' + rotated by the angle 'angle' (in degrees) around intersection point of 'l1' with 'l2'. + If 'sharp' is true (the default), this routine returns the bisector of the sharp angle. + Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/ + line ol; + if (l1 == l2) return l1; + point A = intersectionpoint(l1, l2); + if (finite(A)) { + if(sharp) ol = rotate(sharpdegrees(l1, l2)/2 + angle, A) * l1; + else { + coordsys R = coordsys(l1); + pair a = A, b = A + l1.u, c = A + l2.u; + pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c)); + return rotate(angle, A) * line(A, point(R, pp/R)); + } + } else { + ol = l1; + } + return ol; +} + +/*<asyxml><function type="line" signature="sector(int,int,line,line,real,bool)"><code></asyxml>*/ +line sector(int n = 2, int p = 1, line l1, line l2, real angle = 0, bool sharp = true) +{/*<asyxml></code><documentation>Return the p-th nth-sector of the angle + formed by the oriented line 'l1' and 'l2' + rotated by the angle 'angle' (in degrees) around the intersection point of 'l1' with 'l2'. + If 'sharp' is true (the default), this routine returns the bisector of the sharp angle. + Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/ + line ol; + if (l1 == l2) return l1; + point A = intersectionpoint(l1, l2); + if (finite(A)) { + if(sharp) ol = rotate(p * sharpdegrees(l1, l2)/n + angle, A) * l1; + else { + ol = rotate(p * degrees(l1, l2)/n + angle, A) * l1; + } + } else { + ol = l1; + } + return ol; +} + +/*<asyxml><function type="line" signature="bisector(point,point,point,point,real)"><code></asyxml>*/ +line bisector(point A, point B, point C, point D, real angle = 0, bool sharp = true) +{/*<asyxml></code><documentation>Return the bisector of the angle formed by the lines (AB) and (CD). + <look href = "#bisector(line, line, real, bool)"/>.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C, D); + return bisector(line(P[0], P[1]), line(P[2], P[3]), angle, sharp); +} + +/*<asyxml><function type="line" signature="bisector(segment,real)"><code></asyxml>*/ +line bisector(segment s, real angle = 0) +{/*<asyxml></code><documentation>Return the bisector of the segment line 's' rotated by 'angle' (in degrees) around the + midpoint of 's'.</documentation></function></asyxml>*/ + coordsys R = coordsys(s); + point m = midpoint(s); + vector dir = rotateO(90) * unit(s.A - m); + return rotate(angle, m) * line(m + dir, m - dir); +} + +/*<asyxml><function type="line" signature="bisector(point,point,real)"><code></asyxml>*/ +line bisector(point A, point B, real angle = 0) +{/*<asyxml></code><documentation>Return the bisector of the segment line [AB] rotated by 'angle' (in degrees) around the + midpoint of [AB].</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B); + return bisector(segment(P[0], P[1]), angle); +} + +/*<asyxml><function type="real" signature="distance(point,line)"><code></asyxml>*/ +real distance(point M, line l) +{/*<asyxml></code><documentation>Return the distance from 'M' to 'l'. + distance(line, point) is also defined.</documentation></function></asyxml>*/ + point A = changecoordsys(defaultcoordsys, l.A); + point B = changecoordsys(defaultcoordsys, l.B); + line ll = line(A, B); + pair m = locate(M); + return abs(ll.a * m.x + ll.b * m.y + ll.c)/sqrt(ll.a^2 + ll.b^2); +} + +real distance(line l, point M) +{ + return distance(M, l); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,line,bool,bool,align,pen,arrowbar,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", + line l, bool dirA = l.extendA, bool dirB = l.extendB, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, + Label legend = "", marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/*<asyxml></code><documentation>Draw the line 'l' without altering the size of picture pic. + The boolean parameters control the infinite section. + The global variable 'linemargin' (default value is 0) allows to modify + the bounding box in which the line must be drawn.</documentation></function></asyxml>*/ + if(!(dirA || dirB)) draw(l.A--l.B, invisible);// l is a segment. + Drawline(pic, L, l.A, dirP = dirA, l.B, dirQ = dirB, + align, p, arrow, + legend, marker, pathModifier); +} + +/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen[], arrowbar,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l, + align align = NoAlign, pen[] p = new pen[], + arrowbar arrow = None, + Label[] legend = new Label[], marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/*<asyxml></code><documentation>Draw each lines with the corresponding pen.</documentation></function></asyxml>*/ + for (int i = 0; i < l.length; ++i) { + draw(pic, L.length>0 ? L[i] : "", l[i], + align, p = p.length>0 ? p[i] : currentpen, + arrow, legend.length>0 ? legend[i] : "", marker, + pathModifier); + } +} + +/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen,arrowbar,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l, + align align = NoAlign, pen p, + arrowbar arrow = None, + Label[] legend = new Label[], marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/*<asyxml></code><documentation>Draw each lines with the same pen 'p'.</documentation></function></asyxml>*/ + pen[] tp = sequence(new pen(int i){return p;}, l.length); + draw(pic, L, l, align, tp, arrow, legend, marker, pathModifier); +} + +/*<asyxml><function type="void" signature="show(picture,line,pen)"><code></asyxml>*/ +void show(picture pic = currentpicture, line l, pen p = red) +{/*<asyxml></code><documentation>Draw some informations of 'l'.</documentation></function></asyxml>*/ + dot("$A$", (pair)l.A, align = -locate(l.v), p); + dot("$B$", (pair)l.B, align = -locate(l.v), p); + draw(l, dotted); + draw("$\vec{u}$", locate(l.A)--locate(l.A + l.u), p, Arrow); + draw("$\vec{v}$", locate(l.A)--locate(l.A + l.v), p, Arrow); +} + +/*<asyxml><function type="point[]" signature="sameside(point,line,line)"><code></asyxml>*/ +point[] sameside(point M, line l1, line l2) +{/*<asyxml></code><documentation>Return two points on 'l1' and 'l2' respectively. + The first point is from the same side of M relatively to 'l2', + the second point is from the same side of M relatively to 'l1'.</documentation></function></asyxml>*/ + point[] op; + coordsys R1 = coordsys(l1); + coordsys R2 = coordsys(l2); + if (parallel(l1, l2)) { + op.push(projection(l1) * M); + op.push(projection(l2) * M); + } else { + point O = intersectionpoint(l1, l2); + if (M @ l2) op.push((sameside(M, O + l1.u, l2)) ? O + l1.u : rotate(180, O) * (O + l1.u)); + else op.push(projection(l1, l2) * M); + if (M @ l1) op.push((sameside(M, O + l2.u, l1)) ? O + l2.u : rotate(180, O) * (O + l2.u)); + else {op.push(projection(l2, l1) * M);} + } + return op; +} + +/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit pair,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/ +void markangle(picture pic = currentpicture, + Label L = "", int n = 1, real radius = 0, real space = 0, + explicit line l1, explicit line l2, explicit pair align = dir(1), + arrowbar arrow = None, pen p = currentpen, + filltype filltype = NoFill, + margin margin = NoMargin, marker marker = nomarker) +{/*<asyxml></code><documentation>Mark the angle (l1, l2) aligned in the direction 'align' relative to 'l1'. + Commune values for 'align' are dir(real).</documentation></function></asyxml>*/ + if (parallel(l1, l2, true)) return; + real al = degrees(l1, defaultcoordsys); + pair O, A, B; + if (radius == 0) radius = markangleradius(p); + real d = degrees(locate(l1.u)); + align = rotate(d) * align; + if (l1 == l2) { + O = midpoint(segment(l1.A, l1.B)); + A = l1.A;B = l1.B; + if (sameside(rotate(sgn(angle(B-A)) * 45, O) * A, O + align, l1)) {radius = -radius;} + } else { + O = intersectionpoint(extend(l1), extend(l2)); + pair R = O + align; + point [] ss = sameside(point(coordsys(l1), R/coordsys(l1)), l1, l2); + A = ss[0]; + B = ss[1]; + } + markangle(pic = pic, L = L, n = n, radius = radius, space = space, + O = O, A = A, B = B, + arrow = arrow, p = p, filltype = filltype, + margin = margin, marker = marker); +} + +/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit vector,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/ +void markangle(picture pic = currentpicture, + Label L = "", int n = 1, real radius = 0, real space = 0, + explicit line l1, explicit line l2, explicit vector align, + arrowbar arrow = None, pen p = currentpen, + filltype filltype = NoFill, + margin margin = NoMargin, marker marker = nomarker) +{/*<asyxml></code><documentation>Mark the angle (l1, l2) in the direction 'dir' given relatively to 'l1'.</documentation></function></asyxml>*/ + markangle(pic, L, n, radius, space, l1, l2, (pair)align, arrow, + p, filltype, margin, marker); +} + +/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/ +// void markangle(picture pic = currentpicture, +// Label L = "", int n = 1, real radius = 0, real space = 0, +// explicit line l1, explicit line l2, +// arrowbar arrow = None, pen p = currentpen, +// filltype filltype = NoFill, +// margin margin = NoMargin, marker marker = nomarker) +// {/*<asyxml></code><documentation>Mark the oriented angle (l1, l2).</documentation></function></asyxml>*/ +// if (parallel(l1, l2, true)) return; +// real al = degrees(l1, defaultcoordsys); +// pair O, A, B; +// if (radius == 0) radius = markangleradius(p); +// real d = degrees(locate(l1.u)); +// if (l1 == l2) { +// O = midpoint(segment(l1.A, l1.B)); +// } else { +// O = intersectionpoint(extend(l1), extend(l2)); +// } +// A = O + locate(l1.u); +// B = O + locate(l2.u); +// markangle(pic = pic, L = L, n = n, radius = radius, space = space, +// O = O, A = A, B = B, +// arrow = arrow, p = p, filltype = filltype, +// margin = margin, marker = marker); +// } + +/*<asyxml><function type="void" signature="perpendicularmark(picture,line,line,real,pen,int,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, line l1, line l2, + real size = 0, pen p = currentpen, int quarter = 1, + margin margin = NoMargin, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a right angle at the intersection point of lines and + aligned in the 'quarter' nth quarter of circle formed by 'l1.u' and + 'l2.u'.</documentation></function></asyxml>*/ + point P = intersectionpoint(l1, l2); + pair align = rotate(90 * (quarter - 1)) * dir(45); + perpendicularmark(P, align, locate(l1.u), size, p, margin, filltype); +} +// *.........................LINES.........................* +// *=======================================================* + +// *=======================================================* +// *........................CONICS.........................* +/*<asyxml><struct signature="bqe"><code></asyxml>*/ +struct bqe +{/*<asyxml></code><documentation>Bivariate Quadratic Equation.</documentation></asyxml>*/ + /*<asyxml><property type = "real[]" signature="a"><code></asyxml>*/ + real[] a;/*<asyxml></code><documentation>a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0</documentation></property><property type = "coordsys" signature="coordsys"><code></asyxml>*/ + coordsys coordsys;/*<asyxml></code></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="bqe" signature="bqe(coordsys,real,real,real,real,real,real)"><code></asyxml>*/ +bqe bqe(coordsys R = currentcoordsys, + real a, real b, real c, real d, real e, real f) +{/*<asyxml></code><documentation>Return the bivariate quadratic equation + a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0 + relatively to the coordinate system R.</documentation></function></asyxml>*/ + bqe obqe; + obqe.coordsys = R; + obqe.a = new real[] {a, b, c, d, e, f}; + return obqe; +} + +/*<asyxml><function type="bqe" signature="changecoordsys(coordsys,bqe)"><code></asyxml>*/ +bqe changecoordsys(coordsys R, bqe bqe) +{/*<asyxml></code><documentation>Returns the bivariate quadratic equation relatively to 'R'.</documentation></function></asyxml>*/ + pair i = coordinates(changecoordsys(R, vector(defaultcoordsys, + bqe.coordsys.i))); + pair j = coordinates(changecoordsys(R, vector(defaultcoordsys, + bqe.coordsys.j))); + pair O = coordinates(changecoordsys(R, point(defaultcoordsys, + bqe.coordsys.O))); + real a = bqe.a[0], b = bqe.a[1], c = bqe.a[2], d = bqe.a[3], f = bqe.a[4], g = bqe.a[5]; + real ux = i.x, uy = i.y; + real vx = j.x, vy = j.y; + real ox = O.x, oy = O.y; + real D = ux * vy - uy * vx; + real ap = (a * vy^2 - b * uy * vy + c * uy^2)/D^2; + real bpp = (-2 * a * vx * vy + b * ux * vy + b * uy * vx - 2 * c * ux * uy)/D^2; + real cp = (a * vx^2 - b * ux * vx + c * ux^2)/D^2; + real dp = (-2a * ox * vy^2 + 2a * oy * vx * vy + 2b * ox * uy * vy- + b * oy * ux * vy - b * oy * uy * vx - 2c * ox * uy^2 + 2c * oy * uy * ux)/D^2+ + (d * vy - f * uy)/D; + real fp = (2a * ox * vx * vy - b * ox * ux * vy - 2a * oy * vx^2- + b * ox * uy * vx + 2 * b * oy * ux * vx + 2c * ox * ux * uy - 2c * oy * ux^2)/D^2+ + (f * ux - d * vx)/D; + g = (a * ox^2 * vy^2 - 2a * ox * oy * vx * vy - b * ox^2 * uy * vy + b * ox * oy * ux * vy+ + a * oy^2 * vx^2 + b * ox * oy * uy * vx - b * oy^2 * ux * vx + c * ox^2 * uy^2- + 2 * c * ox * oy * ux * uy + c * oy^2 * ux^2)/D^2+ + (d * oy * vx + f * ox * uy - d * ox * vy - f * oy * ux)/D + g; + bqe obqe; + obqe.a = approximate(new real[] {ap, bpp, cp, dp, fp, g}); + obqe.coordsys = R; + return obqe; +} + +/*<asyxml><function type="bqe" signature="bqe(point,point,point,point,point)"><code></asyxml>*/ +bqe bqe(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the bqe of conic passing through the five points (if possible).</documentation></function></asyxml>*/ + coordsys R; + pair[] pts; + if (samecoordsys(M1, M2, M3, M4, M5)) { + R = M1.coordsys; + pts= new pair[] {M1.coordinates, M2.coordinates, M3.coordinates, M4.coordinates, M5.coordinates}; + } else { + R = defaultcoordsys; + pts= new pair[] {M1, M2, M3, M4, M5}; + } + real[][] M; + real[] x; + bqe bqe; + bqe.coordsys = R; + for (int i = 0; i < 5; ++i) {// Try a = -1 + M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1}; + x[i] = pts[i].x^2; + } + if(abs(determinant(M)) < 1e-5) {// Try c = -1 + for (int i = 0; i < 5; ++i) { + M[i] = new real[] {pts[i].x^2, pts[i].x * pts[i].y, pts[i].x, pts[i].y, 1}; + x[i] = pts[i].y^2; + } + real[] coef = solve(M, x); + bqe.a = new real[] {coef[0], coef[1], -1, coef[2], coef[3], coef[4]}; + } else { + real[] coef = solve(M, x); + bqe.a = new real[] {-1, coef[0], coef[1], coef[2], coef[3], coef[4]}; + } + bqe.a = approximate(bqe.a); + return bqe; +} + +/*<asyxml><function type="bool" signature="samecoordsys(bool...bqe[])"><code></asyxml>*/ +bool samecoordsys(bool warn = true ... bqe[] bqes) +{/*<asyxml></code><documentation>Return true if all the bivariate quadratic equations have the same coordinate system.</documentation></function></asyxml>*/ + bool ret = true; + coordsys t = bqes[0].coordsys; + for (int i = 1; i < bqes.length; ++i) { + ret = (t == bqes[i].coordsys); + if(!ret) break; + t = bqes[i].coordsys; + } + if(warn && !ret) + warning("coodinatesystem", + "the coordinate system of two bivariate quadratic equations are not +the same. The operation will be done relatively to the default coordinate +system."); + return ret; +} + +/*<asyxml><function type="real[]" signature="realquarticroots(real,real,real,real,real)"><code></asyxml>*/ +real[] realquarticroots(real a, real b, real c, real d, real e) +{/*<asyxml></code><documentation>Return the real roots of the quartic equation ax^4 + b^x3 + cx^2 + dx = 0.</documentation></function></asyxml>*/ + static real Fuzz = sqrt(realEpsilon); + pair[] zroots = quarticroots(a, b, c, d, e); + real[] roots; + real p(real x){return a * x^4 + b * x^3 + c * x^2 + d * x + e;} + real prime(real x){return 4 * a * x^3 + 3 * b * x^2 + 2 * c * x + d;} + real x; + bool search = true; + int n; + void addroot(real x) + { + bool exist = false; + for (int i = 0; i < roots.length; ++i) { + if(abs(roots[i]-x) < 1e-5) {exist = true; break;} + } + if(!exist) roots.push(x); + } + for(int i = 0; i < zroots.length; ++i) { + if(zroots[i].y == 0 || abs(p(zroots[i].x)) < Fuzz) addroot(zroots[i].x); + else { + if(abs(zroots[i].y) < 1e-3) { + x = zroots[i].x; + search = true; + n = 200; + while(search) { + real tx = abs(p(x)) < Fuzz ? x : newton(iterations = n, p, prime, x); + if(tx < realMax) { + if(abs(p(tx)) < Fuzz) { + addroot(tx); + search = false; + } else if(n < 200) n *=2; + else { + search = false; + } + } else search = false; //It's not a real root. + } + } + } + } + return roots; +} + +/*<asyxml><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(F2 - F1).</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.C = (P[0] + P[1])/2; + this.angle = degrees(F2 - F1, warn=false); + 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>Value is degrees(F - V).</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); + this.F = P[0]; + line l = line(P[1], P[2]); + 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, warn=false); + } +}/*<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>Value is degrees(F2 - F1).</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.C = (P[0] + P[1])/2; + this.angle = degrees(F2 - F1, warn=false); + this.a = a; + 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 + rotate(90,this.C.coordsys.O) * unit(A - this.C)); + this.D2 = reverse(rotate(180, C) * D1); + this.V1 = C + a * unit(F1 - C); + this.V2 = C + a * unit(F2 - C); + this.A1 = line(C, V1 + b * unit(rotateO(-90) * (C - V1))); + this.A2 = line(C, V1 + b * unit(rotateO(90) * (C - V1))); + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><variable type="int" signature="conicnodesfactor"><code></asyxml>*/ +int conicnodesfactor = 1;/*<asyxml></code><documentation>Factor for the node number of all conics.</documentation></variable></asyxml>*/ + +/*<asyxml><variable type="int" signature="circlenodesnumberfactor"><code></asyxml>*/ +int circlenodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the node number of circles.</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="circlenodesnumber(real)"><code></asyxml>*/ +int circlenodesnumber(real r) +{/*<asyxml></code><documentation>Return the number of nodes for drawing a circle of radius 'r'.</documentation></function></asyxml>*/ + if (circlenodesnumberfactor < 100) + warning("circlenodesnumberfactor", + "variable 'circlenodesnumberfactor' may be too small."); + int oi = ceil(circlenodesnumberfactor * abs(r)^0.1); + oi = 45 * floor(oi/45); + return oi == 0 ? 4 : conicnodesfactor * oi; +} + +/*<asyxml><function type="int" signature="circlenodesnumber(real,real,real)"><code></asyxml>*/ +int circlenodesnumber(real r, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the number of nodes to draw a circle arc.</documentation></function></asyxml>*/ + return (r > 0) ? + ceil(circlenodesnumber(r) * abs(angle1 - angle2)/360) : + ceil(circlenodesnumber(r) * abs((1 - abs(angle1 - angle2)/360))); +} + +/*<asyxml><variable type="int" signature="ellispenodesnumberfactor"><code></asyxml>*/ +int ellipsenodesnumberfactor = 250;/*<asyxml></code><documentation>Factor for the node number of ellispe (non-circle).</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real)"><code></asyxml>*/ +int ellipsenodesnumber(real a, real b) +{/*<asyxml></code><documentation>Return the number of nodes to draw a ellipse of axis 'a' and 'b'.</documentation></function></asyxml>*/ + if (ellipsenodesnumberfactor < 250) + write("ellipsenodesnumberfactor", + "variable 'ellipsenodesnumberfactor' maybe too small."); + int tmp = circlenodesnumberfactor; + circlenodesnumberfactor = ellipsenodesnumberfactor; + int oi = circlenodesnumber(max(abs(a), abs(b))/min(abs(a), abs(b))); + circlenodesnumberfactor = tmp; + return conicnodesfactor * oi; +} + +/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real,real)"><code></asyxml>*/ +int ellipsenodesnumber(real a, real b, real angle1, real angle2, bool dir) +{/*<asyxml></code><documentation>Return the number of nodes to draw an ellipse arc.</documentation></function></asyxml>*/ + real d; + real da = angle2 - angle1; + if(dir) { + d = angle1 < angle2 ? da : 360 + da; + } else { + d = angle1 < angle2 ? -360 + da : da; + } + int n = floor(ellipsenodesnumber(a, b) * abs(d)/360); + return n < 5 ? 5 : n; +} + +/*<asyxml><variable type="int" signature="parabolanodesnumberfactor"><code></asyxml>*/ +int parabolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of parabolas.</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="parabolanodesnumber(parabola,real,real)"><code></asyxml>*/ +int parabolanodesnumber(parabola p, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the number of nodes for drawing a parabola.</documentation></function></asyxml>*/ + return conicnodesfactor * floor(0.01 * parabolanodesnumberfactor * abs(angle1 - angle2)); +} + +/*<asyxml><variable type="int" signature="hyperbolanodesnumberfactor"><code></asyxml>*/ +int hyperbolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of hyperbolas.</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="hyperbolanodesnumber(hyperbola,real,real)"><code></asyxml>*/ +int hyperbolanodesnumber(hyperbola h, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the number of nodes for drawing an hyperbola.</documentation></function></asyxml>*/ + return conicnodesfactor * floor(0.01 * hyperbolanodesnumberfactor * abs(angle1 - angle2)/h.e); +} + +/*<asyxml><operator type = "conic" signature="+(conic,explicit point)"><code></asyxml>*/ +conic operator +(conic c, explicit point M) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F + M, c.D + M, c.e); +} +/*<asyxml><operator type = "conic" signature="-(conic,explicit point)"><code></asyxml>*/ +conic operator -(conic c, explicit point M) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F - M, c.D - M, c.e); +} +/*<asyxml><operator type = "conic" signature="+(conic,explicit pair)"><code></asyxml>*/ +conic operator +(conic c, explicit pair m) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + point M = point(c.F.coordsys, m); + return conic(c.F + M, c.D + M, c.e); +} +/*<asyxml><operator type = "conic" signature="-(conic,explicit pair)"><code></asyxml>*/ +conic operator -(conic c, explicit pair m) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + point M = point(c.F.coordsys, m); + return conic(c.F - M, c.D - M, c.e); +} +/*<asyxml><operator type = "conic" signature="+(conic,vector)"><code></asyxml>*/ +conic operator +(conic c, vector v) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F + v, c.D + v, c.e); +} +/*<asyxml><operator type = "conic" signature="-(conic,vector)"><code></asyxml>*/ +conic operator -(conic c, vector v) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F - v, c.D - v, c.e); +} + +/*<asyxml><function type="coordsys" signature="coordsys(conic)"><code></asyxml>*/ +coordsys coordsys(conic co) +{/*<asyxml></code><documentation>Return the coordinate system of 'co'.</documentation></function></asyxml>*/ + return co.F.coordsys; +} + +/*<asyxml><function type="conic" signature="changecoordsys(coordsys,conic)"><code></asyxml>*/ +conic changecoordsys(coordsys R, conic co) +{/*<asyxml></code><documentation>Change the coordinate system of 'co' to 'R'</documentation></function></asyxml>*/ + line l = changecoordsys(R, co.D); + point F = changecoordsys(R, co.F); + return conic(F, l, co.e); +} + +/*<asyxml><typedef type = "polarconicroutine" return = "path" params = "conic, real, real, int, bool"><code></asyxml>*/ +typedef path polarconicroutine(conic co, real angle1, real angle2, int n, bool direction);/*<asyxml></code><documentation>Routine type used to draw conics from 'angle1' to 'angle2'</documentation></typedef></asyxml>*/ + +/*<asyxml><function type="path" signature="arcfromfocus(conic,real,real,int,bool)"><code></asyxml>*/ +path arcfromfocus(conic co, real angle1, real angle2, int n = 400, bool direction = CCW) +{/*<asyxml></code><documentation>Return the path of the conic section 'co' from angle1 to angle2 in degrees, + drawing in the given direction, with n nodes.</documentation></function></asyxml>*/ + guide op; + if (n < 1) return op; + if (angle1 > angle2) { + path g = arcfromfocus(co, angle2, angle1, n, !direction); + return g == nullpath ? g : reverse(g); + } + point O = projection(co.D) * co.F; + pair i = unit(locate(co.F) - locate(O)); + pair j = rotate(90) * i; + coordsys Rp = cartesiansystem(co.F, i, j); + real a1 = direction ? radians(angle1) : radians(angle2); + real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi; + real step = n == 1 ? 0 : (a2 - a1)/(n - 1); + real a, r; + for (int i = 0; i < n; ++i) { + a = a1 + i * step; + if(co.e >= 1) { + r = 1 - co.e * cos(a); + if(r > epsgeo) { + r = co.p/r; + op = op--Rp * Rp.polar(r, a); + } + } else { + r = co.p/(1 - co.e * cos(a)); + op = op..Rp * Rp.polar(r, a); + } + } + if(co.e < 1 && abs(abs(a2 - a1) - 2 * pi) < epsgeo) op = (path)op..cycle; + + return (direction ? op : op == nullpath ? op :reverse(op)); +} + +/*<asyxml><variable type="polarconicroutine" signature="currentpolarconicroutine"><code></asyxml>*/ +polarconicroutine currentpolarconicroutine = arcfromfocus;/*<asyxml></code><documentation>Default routine used to cast conic section to path.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="point" signature="angpoint(conic,real)"><code></asyxml>*/ +point angpoint(conic co, real angle) +{/*<asyxml></code><documentation>Return the point of 'co' whose the angular (in degrees) + coordinate is 'angle' (mesured from the focus of 'co', relatively + to its 'natural coordinate system').</documentation></function></asyxml>*/ + coordsys R = coordsys(co); + return point(R, point(arcfromfocus(co, angle, angle, 1, CCW), 0)/R); +} + +/*<asyxml><operator type = "bool" signature="@(point,conic)"><code></asyxml>*/ +bool operator @(point M, conic co) +{/*<asyxml></code><documentation>Return true iff 'M' on 'co'.</documentation></operator></asyxml>*/ + if(co.e == 0) return abs(abs(co.F - M) - co.p) < 10 * epsgeo; + return abs(co.e * distance(M, co.D) - abs(co.F - M)) < 10 * epsgeo; +} + +/*<asyxml><function type="coordsys" signature="coordsys(ellipse)"><code></asyxml>*/ +coordsys coordsys(ellipse el) +{/*<asyxml></code><documentation>Return the coordinate system of 'el'.</documentation></function></asyxml>*/ + return el.F1.coordsys; +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(ellipse)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(ellipse el) +{/*<asyxml></code><documentation>Return the canonical cartesian system of the ellipse 'el'.</documentation></function></asyxml>*/ + if(degenerate(el)) return cartesiansystem(el.l.A, el.l.u, el.l.v); + pair O = locate(el.C); + pair i = el.e == 0 ? el.C.coordsys.i : unit(locate(el.F1) - O); + pair j = rotate(90) * i; + return cartesiansystem(O, i, j); +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(parabola)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(parabola p) +{/*<asyxml></code><documentation>Return the canonical cartesian system of a parabola, + so that Origin = vertex of 'p' and directrix: x = -a.</documentation></function></asyxml>*/ + point A = projection(p.D) * p.F; + pair O = locate((A + p.F)/2); + pair i = unit(locate(p.F) - O); + pair j = rotate(90) * i; + return cartesiansystem(O, i, j); +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(hyperbola h) +{/*<asyxml></code><documentation>Return the canonical cartesian system of an hyperbola.</documentation></function></asyxml>*/ + pair O = locate(h.C); + pair i = unit(locate(h.F2) - O); + pair j = rotate(90) * i; + return cartesiansystem(O, i, j); +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,point,real)"><code></asyxml>*/ +ellipse ellipse(point F1, point F2, real a) +{/*<asyxml></code><documentation>Return the ellipse whose the foci are 'F1' and 'F2' + and the semimajor axis is 'a'.</documentation></function></asyxml>*/ + ellipse oe; + oe.init(F1, F2, a); + return oe; +} + +/*<asyxml><constant type = "bool" signature="byfoci,byvertices"><code></asyxml>*/ +restricted bool byfoci = true, byvertices = false;/*<asyxml></code><documentation>Constants useful for the routine 'hyperbola(point P1, point P2, real ae, bool byfoci = byfoci)'</documentation></constant></asyxml>*/ + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,real,bool)"><code></asyxml>*/ +hyperbola hyperbola(point P1, point P2, real ae, bool byfoci = byfoci) +{/*<asyxml></code><documentation>if 'byfoci = true': + return the hyperbola whose the foci are 'P1' and 'P2' + and the semimajor axis is 'ae'. + else return the hyperbola whose vertexes are 'P1' and 'P2' with eccentricity 'ae'.</documentation></function></asyxml>*/ + hyperbola oh; + point[] P = standardizecoordsys(P1, P2); + if(byfoci) { + oh.init(P[0], P[1], ae); + } else { + real a = abs(P[0]-P[1])/2; + vector V = unit(P[0]-P[1]); + point F1 = P[0] + a * (ae - 1) * V; + point F2 = P[1]-a * (ae - 1) * V; + oh.init(F1, F2, a); + } + return oh; +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,point,point)"><code></asyxml>*/ +ellipse ellipse(point F1, point F2, point M) +{/*<asyxml></code><documentation>Return the ellipse passing through 'M' whose the foci are 'F1' and 'F2'.</documentation></function></asyxml>*/ + real a = abs(F1 - M) + abs(F2 - M); + return ellipse(F1, F2, finite(a) ? a/2 : a); +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,real,real,real)"><code></asyxml>*/ +ellipse ellipse(point C, real a, real b, real angle = 0) +{/*<asyxml></code><documentation>Return the ellipse centered at 'C' with semimajor axis 'a' along C--C + dir(angle), + semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/ + ellipse oe; + coordsys R = C.coordsys; + angle += degrees(R.i); + if(a < b) {angle += 90; real tmp = a; a = b; b = tmp;} + if(finite(a) && finite(b)) { + real c = sqrt(abs(a^2 - b^2)); + point f1, f2; + if(abs(a - b) < epsgeo) { + f1 = C; f2 = C; + } else { + f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R); + f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R); + } + oe.init(f1, f2, a); + } else { + if(finite(b) || !finite(a)) oe.init(C, C + R.polar(1, angle), infinity); + else oe.init(C, C + R.polar(1, 90 + angle), infinity); + } + return oe; +} + +/*<asyxml><function type="ellipse" signature="ellipse(bqe)"><code></asyxml>*/ +ellipse ellipse(bqe bqe) +{/*<asyxml></code><documentation>Return the ellipse a[0] * x^2 + a[1] * xy + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0 + given in the coordinate system of 'bqe' with a[i] = bque.a[i]. + <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/> + <url href = "http://mathworld.wolfram.com/Ellipse.html"/>.</documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + coordsys R = bqe.coordsys; + string message = "ellipse: the given equation is not an equation of an ellipse."; + real u = b^2 * g + d^2 * c + f^2 * a; + real delta = a * c * g + b * f * d + d * b * f - u; + if(abs(delta) < epsgeo) abort(message); + real j = b^2 - a * c; + real i = a + c; + real dd = j * (sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a); + real ddd = j * (-sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a); + + if(abs(ddd) < epsgeo || abs(dd) < epsgeo || + j >= -epsgeo || delta/sgnd(i) > 0) abort(message); + + real x = (c * d - b * f)/j, y = (a * f - b * d)/j; + // real dir = abs(b) < epsgeo ? 0 : pi/2-0.5 * acot(0.5 * (c-a)/b); + real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b); + if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2; + real cd = cos(dir), sd = sin(dir); + real t = a * cd^2 - 2 * b * cd * sd + c * sd^2; + real tt = a * sd^2 + 2 * b * cd * sd + c * cd^2; + real gg = -g + ((d * cd - f * sd)^2)/t + ((d * sd + f * cd)^2)/tt; + t = t/gg; tt = tt/gg; + // The equation of the ellipse is t * (x - center.x)^2 + tt * (y - center.y)^2 = 1; + real aa, bb; + aa = sqrt(2 * (u - 2 * b * d * f - a * c * g)/dd); + bb = sqrt(2 * (u - 2 * b * d * f - a * c * g)/ddd); + a = t > tt ? max(aa, bb) : min(aa, bb); + b = t > tt ? min(aa, bb) : max(aa, bb); + return ellipse(point(R, (x, y)/R), + a, b, degrees(pi/2 - dir - angle(R.i))); +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,point,point,point,point)"><code></asyxml>*/ +ellipse ellipse(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the ellipse passing through the five points (if possible)</documentation></function></asyxml>*/ + return ellipse(bqe(M1, M2, M3, M4, M5)); +} + +/*<asyxml><function type="bool" signature="inside(ellipse,point)"><code></asyxml>*/ +bool inside(ellipse el, point M) +{/*<asyxml></code><documentation>Return 'true' iff 'M' is inside 'el'.</documentation></function></asyxml>*/ + return abs(el.F1 - M) + abs(el.F2 - M) - 2 * el.a < -epsgeo; +} + +/*<asyxml><function type="bool" signature="inside(parabola,point)"><code></asyxml>*/ +bool inside(parabola p, point M) +{/*<asyxml></code><documentation>Return 'true' if 'M' is inside 'p'.</documentation></function></asyxml>*/ + return distance(p.D, M) - abs(p.F - M) > epsgeo; +} + +/*<asyxml><function type="parabola" signature="parabola(point,line)"><code></asyxml>*/ +parabola parabola(point F, line l) +{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and directrix is 'l'.</documentation></function></asyxml>*/ + parabola op; + op.init(F, l); + return op; +} + +/*<asyxml><function type="parabola" signature="parabola(point,point)"><code></asyxml>*/ +parabola parabola(point F, point vertex) +{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and vertex is 'vertex'.</documentation></function></asyxml>*/ + parabola op; + point[] P = standardizecoordsys(F, vertex); + point A = rotate(180, P[1]) * P[0]; + point B = A + rotateO(90) * unit(P[1]-A); + op.init(P[0], line(A, B)); + return op; +} + +/*<asyxml><function type="parabola" signature="parabola(point,real,real)"><code></asyxml>*/ +parabola parabola(point F, real a, real angle) +{/*<asyxml></code><documentation>Return the parabola whose focus is F, latus rectum is 4a and + the angle of the axis of symmetry (in the coordinate system of F) is 'angle'.</documentation></function></asyxml>*/ + parabola op; + coordsys R = F.coordsys; + point A = F - point(R, R.polar(2a, radians(angle))); + point B = A + point(R, R.polar(1, radians(90 + angle))); + op.init(F, line(A, B)); + return op; +} + +/*<asyxml><function type="bool" signature="isparabola(bqe)"><code></asyxml>*/ +bool isparabola(bqe bqe) +{/*<asyxml></code><documentation>Return true iff 'bqe' is the equation of a parabola.</documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + return (abs(delta) > epsgeo && abs(b^2 - a * c) < epsgeo); +} + +/*<asyxml><function type="parabola" signature="parabola(bqe)"><code></asyxml>*/ +parabola parabola(bqe bqe) +{/*<asyxml></code><documentation>Return the parabola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]). + <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/> + <url href = "http://mathworld.wolfram.com/Parabola.html"/></documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + string message = "parabola: the given equation is not an equation of a parabola."; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) > 10 * epsgeo) abort(message); + real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b); + if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2; + real cd = cos(dir), sd = sin(dir); + real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2; + real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2; + real dp = d * cd - f * sd; + real fp = d * sd + f * cd; + real gp = g; + parabola op; + coordsys R = bqe.coordsys; + // The equation of the parabola is ap * x'^2 + cp * y'^2 + 2dp * x'+2fp * y'+gp = 0 + if (abs(ap) < epsgeo) {/* directrix parallel to the rotated(dir) y-axis + equation: (y-vertex.y)^2 = 4 * a * (x-vertex) + */ + pair pvertex = rotate(degrees(-dir)) * (0.5(-gp + fp^2/cp)/dp, -fp/cp); + real a = -0.5 * dp/cp; + point vertex = point(R, pvertex/R); + point focus = point(R, (pvertex + a * expi(-dir))/R); + op = parabola(focus, vertex); + + } else {/* directrix parallel to the rotated(dir) x-axis + equation: (x-vertex)^2 = 4 * a * (y-vertex.y) + */ + pair pvertex = rotate(degrees(-dir)) * (-dp/ap, 0.5 * (-gp + dp^2/ap)/fp); + real a = -0.5 * fp/ap; + point vertex = point(R, pvertex/R); + point focus = point(R, (pvertex + a * expi(pi/2 - dir))/R); + op = parabola(focus, vertex); + } + return op; +} + +/*<asyxml><function type="parabola" signature="parabola(point,point,point,line)"><code></asyxml>*/ +parabola parabola(point M1, point M2, point M3, line l) +{/*<asyxml></code><documentation>Return the parabola passing through the three points with its directix + parallel to the line 'l'.</documentation></function></asyxml>*/ + coordsys R; + pair[] pts; + if (samecoordsys(M1, M2, M3)) { + R = M1.coordsys; + } else { + R = defaultcoordsys; + } + real gle = degrees(l); + coordsys Rp = cartesiansystem(R.O, rotate(gle) * R.i, rotate(gle) * R.j); + pts = new pair[] {coordinates(changecoordsys(Rp, M1)), + coordinates(changecoordsys(Rp, M2)), + coordinates(changecoordsys(Rp, M3))}; + real[][] M; + real[] x; + for (int i = 0; i < 3; ++i) { + M[i] = new real[] {pts[i].x, pts[i].y, 1}; + x[i] = -pts[i].x^2; + } + real[] coef = solve(M, x); + return parabola(changecoordsys(R, bqe(Rp, 1, 0, 0, coef[0], coef[1], coef[2]))); +} + +/*<asyxml><function type="parabola" signature="parabola(point,point,point,point,point)"><code></asyxml>*/ +parabola parabola(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the parabola passing through the five points.</documentation></function></asyxml>*/ + return parabola(bqe(M1, M2, M3, M4, M5)); +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,point)"><code></asyxml>*/ +hyperbola hyperbola(point F1, point F2, point M) +{/*<asyxml></code><documentation>Return the hyperbola passing through 'M' whose the foci are 'F1' and 'F2'.</documentation></function></asyxml>*/ + real a = abs(abs(F1 - M) - abs(F2 - M)); + return hyperbola(F1, F2, finite(a) ? a/2 : a); +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,real,real,real)"><code></asyxml>*/ +hyperbola hyperbola(point C, real a, real b, real angle = 0) +{/*<asyxml></code><documentation>Return the hyperbola centered at 'C' with semimajor axis 'a' along C--C + dir(angle), + semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/ + hyperbola oh; + coordsys R = C.coordsys; + angle += degrees(R.i); + real c = sqrt(a^2 + b^2); + point f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R); + point f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R); + oh.init(f1, f2, a); + return oh; +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(bqe)"><code></asyxml>*/ +hyperbola hyperbola(bqe bqe) +{/*<asyxml></code><documentation>Return the hyperbola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]). + <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/> + <url href = "http://mathworld.wolfram.com/Hyperbola.html"/></documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + string message = "hyperbola: the given equation is not an equation of a hyperbola."; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) < 0) abort(message); + real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b); + real cd = cos(dir), sd = sin(dir); + real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2; + real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2; + real dp = d * cd - f * sd; + real fp = d * sd + f * cd; + real gp = -g + dp^2/ap + fp^2/cp; + hyperbola op; + coordsys R = bqe.coordsys; + real j = b^2 - a * c; + point C = point(R, ((c * d - b * f)/j, (a * f - b * d)/j)/R); + real aa = gp/ap, bb = gp/cp; + real a = sqrt(abs(aa)), b = sqrt(abs(bb)); + if(aa < 0) {dir -= pi/2; aa = a; a = b; b = aa;} + return hyperbola(C, a, b, degrees(-dir - angle(R.i))); +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,point,point,point)"><code></asyxml>*/ +hyperbola hyperbola(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the hyperbola passing through the five points (if possible).</documentation></function></asyxml>*/ + return hyperbola(bqe(M1, M2, M3, M4, M5)); +} + +/*<asyxml><function type="hyperbola" signature="conj(hyperbola)"><code></asyxml>*/ +hyperbola conj(hyperbola h) +{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/ + return hyperbola(h.C, h.b, h.a, 90 + h.angle); +} + +/*<asyxml><function type="circle" signature="circle(explicit point,real)"><code></asyxml>*/ +circle circle(explicit point C, real r) +{/*<asyxml></code><documentation>Circle given by center and radius.</documentation></function></asyxml>*/ + circle oc = new circle; + oc.C = C; + oc.r = r; + if(!finite(r)) oc.l = line(C, C + vector(C.coordsys, (1, 0))); + return oc; +} + +/*<asyxml><function type="circle" signature="circle(point,point)"><code></asyxml>*/ +circle circle(point A, point B) +{/*<asyxml></code><documentation>Return the circle of diameter AB.</documentation></function></asyxml>*/ + real r; + circle oc; + real a = abs(A), b = abs(B); + if(finite(a) && finite(b)) { + oc = circle((A + B)/2, abs(A - B)/2); + } else { + oc.r = infinity; + if(finite(abs(A))) oc.l = line(A, A + unit(B)); + else { + if(finite(abs(B))) oc.l = line(B, B + unit(A)); + else if(finite(abs(A - B)/2)) oc = circle((A + B)/2, abs(A - B)/2); else + oc.l = line(A, B); + } + } + return oc; +} + +/*<asyxml><function type="circle" signature="circle(segment)"><code></asyxml>*/ +circle circle(segment s) +{/*<asyxml></code><documentation>Return the circle of diameter 's'.</documentation></function></asyxml>*/ + return circle(s.A, s.B); +} + +/*<asyxml><function type="point" signature="circumcenter(point,point,point)"><code></asyxml>*/ +point circumcenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the circumcenter of triangle ABC.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair a = A, b = B, c = C; + pair mAB = (a + b)/2; + pair mAC = (a + c)/2; + pair pp = extension(mAB, rotate(90, mAB) * a, mAC, rotate(90, mAC) * c); + return point(R, pp/R); +} + +/*<asyxml><function type="circle" signature="circle(point,point,point)"><code></asyxml>*/ +circle circle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/ + if(collinear(A - B, A - C)) { + circle oc; + oc.r = infinity; + oc.C = (A + B + C)/3; + oc.l = line(oc.C, oc.C == A ? B : A); + return oc; + } + point c = circumcenter(A, B, C); + return circle(c, abs(c - A)); +} + +/*<asyxml><function type="circle" signature="circumcircle(point,point,point)"><code></asyxml>*/ +circle circumcircle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/ + return circle(A, B, C); +} + +/*<asyxml><operator type = "circle" signature="*(real,explicit circle)"><code></asyxml>*/ +circle operator *(real x, explicit circle c) +{/*<asyxml></code><documentation>Multiply the radius of 'c'.</documentation></operator></asyxml>*/ + return finite(c.r) ? circle(c.C, x * c.r) : c; +} +circle operator *(int x, explicit circle c) +{ + return finite(c.r) ? circle(c.C, x * c.r) : c; +} +/*<asyxml><operator type = "circle" signature="/(explicit circle,real)"><code></asyxml>*/ +circle operator /(explicit circle c, real x) +{/*<asyxml></code><documentation>Divide the radius of 'c'</documentation></operator></asyxml>*/ + return finite(c.r) ? circle(c.C, c.r/x) : c; +} +circle operator /(explicit circle c, int x) +{ + return finite(c.r) ? circle(c.C, c.r/x) : c; +} +/*<asyxml><operator type = "circle" signature="+(explicit circle,explicit point)"><code></asyxml>*/ +circle operator +(explicit circle c, explicit point M) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C + M, c.r); +} +/*<asyxml><operator type = "circle" signature="-(explicit circle,explicit point)"><code></asyxml>*/ +circle operator -(explicit circle c, explicit point M) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C - M, c.r); +} +/*<asyxml><operator type = "circle" signature="+(explicit circle,pair)"><code></asyxml>*/ +circle operator +(explicit circle c, pair m) +{/*<asyxml></code><documentation>Translation of 'c'. + 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/ + return circle(c.C + m, c.r); +} +/*<asyxml><operator type = "circle" signature="-(explicit circle,pair)"><code></asyxml>*/ +circle operator -(explicit circle c, pair m) +{/*<asyxml></code><documentation>Translation of 'c'. + 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/ + return circle(c.C - m, c.r); +} +/*<asyxml><operator type = "circle" signature="+(explicit circle,vector)"><code></asyxml>*/ +circle operator +(explicit circle c, vector m) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C + m, c.r); +} +/*<asyxml><operator type = "circle" signature="-(explicit circle,vector)"><code></asyxml>*/ +circle operator -(explicit circle c, vector m) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C - m, c.r); +} +/*<asyxml><operator type = "real" signature="^(point,explicit circle)"><code></asyxml>*/ +real operator ^(point M, explicit circle c) +{/*<asyxml></code><documentation>The power of 'M' with respect to the circle 'c'</documentation></operator></asyxml>*/ + return xpart((abs(locate(M) - locate(c.C)), c.r)^2); +} +/*<asyxml><operator type = "bool" signature="@(point,explicit circle)"><code></asyxml>*/ +bool operator @(point M, explicit circle c) +{/*<asyxml></code><documentation>Return true iff 'M' is on the circle 'c'.</documentation></operator></asyxml>*/ + return finite(c.r) ? + abs(abs(locate(M) - locate(c.C)) - abs(c.r)) <= 10 * epsgeo : + M @ c.l; +} + +/*<asyxml><operator type = "ellipse" signature="cast(circle)"><code></asyxml>*/ +ellipse operator cast(circle c) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return finite(c.r) ? ellipse(c.C, c.r, c.r, 0) : ellipse(c.l.A, c.l.B, infinity); +} + +/*<asyxml><operator type = "circle" signature="cast(ellipse)"><code></asyxml>*/ +circle operator ecast(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 ecast(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 ecast(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 ecast(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 ecast(conic c) +{/*<asyxml></code><documentation>Conic section to circle.</documentation></operator></asyxml>*/ + ellipse el = (ellipse)c; + circle oc; + if(abs(el.a - el.b) < epsgeo) { + oc = circle(el.C, el.a); + if(degenerate(c)) oc.l = c.l[0]; + } + else abort("Can not cast this conic to a circle"); + return oc; +} + +/*<asyxml><operator type = "ellipse" signature="*(transform,ellipse)"><code></asyxml>*/ +ellipse operator *(transform t, ellipse el) +{/*<asyxml></code><documentation>Provide transform * ellipse.</documentation></operator></asyxml>*/ + if(!degenerate(el)) { + point[] ep; + for (int i = 0; i < 360; i += 72) { + ep.push(t * angpoint(el, i)); + } + ellipse oe = ellipse(ep[0], ep[1], ep[2], ep[3], ep[4]); + if(angpoint(oe, 0) != ep[0]) return ellipse(oe.F2, oe.F1, oe.a); + return oe; + } + return ellipse(t * el.l.A, t * el.l.B, infinity); +} + +/*<asyxml><operator type = "parabola" signature="*(transform,parabola)"><code></asyxml>*/ +parabola operator *(transform t, parabola p) +{/*<asyxml></code><documentation>Provide transform * parabola.</documentation></operator></asyxml>*/ + point[] P; + P.push(t * angpoint(p, 45)); + P.push(t * angpoint(p, -45)); + P.push(t * angpoint(p, 180)); + parabola op = parabola(P[0], P[1], P[2], t * p.D); + op.bmin = p.bmin; + op.bmax = p.bmax; + + return op; +} + +/*<asyxml><operator type = "ellipse" signature="*(transform,circle)"><code></asyxml>*/ +ellipse operator *(transform t, circle c) +{/*<asyxml></code><documentation>Provide transform * circle. + For example, 'circle C = scale(2) * circle' and 'ellipse E = xscale(2) * circle' are valid + but 'circle C = xscale(2) * circle' is invalid.</documentation></operator></asyxml>*/ + return t * ((ellipse)c); +} + +/*<asyxml><operator type = "hyperbola" signature="*(transform,hyperbola)"><code></asyxml>*/ +hyperbola operator *(transform t, hyperbola h) +{/*<asyxml></code><documentation>Provide transform * hyperbola.</documentation></operator></asyxml>*/ + if (t == identity()) { + return h; + } + + point[] ep; + for (int i = 90; i <= 270; i += 45) { + ep.push(t * angpoint(h, i)); + } + + hyperbola oe = hyperbola(ep[0], ep[1], ep[2], ep[3], ep[4]); + if(angpoint(oe, 90) != ep[0]) { + oe = hyperbola(oe.F2, oe.F1, oe.a); + } + + oe.bmin = h.bmin; + oe.bmax = h.bmax; + + return oe; +} + +/*<asyxml><operator type = "conic" signature="*(transform,conic)"><code></asyxml>*/ +conic operator *(transform t, conic co) +{/*<asyxml></code><documentation>Provide transform * conic.</documentation></operator></asyxml>*/ + if(co.e < 1) return (t * ((ellipse)co)); + if(co.e == 1) return (t * ((parabola)co)); + return (t * ((hyperbola)co)); +} + +/*<asyxml><operator type = "ellipse" signature="*(real,ellipse)"><code></asyxml>*/ +ellipse operator *(real x, ellipse el) +{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(x, el.C) * el'.</documentation></operator></asyxml>*/ + return degenerate(el) ? el : ellipse(el.C, x * el.a, x * el.b, el.angle); +} + +/*<asyxml><operator type = "ellipse" signature="/(ellipse,real)"><code></asyxml>*/ +ellipse operator /(ellipse el, real x) +{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(1/x, el.C) * el'.</documentation></operator></asyxml>*/ + return degenerate(el) ? el : ellipse(el.C, el.a/x, el.b/x, el.angle); +} + +/*<asyxml><function type="path" signature="arcfromcenter(ellipse,real,real,int,bool)"><code></asyxml>*/ +path arcfromcenter(ellipse el, real angle1, real angle2, + bool direction=CCW, + int n=ellipsenodesnumber(el.a,el.b,angle1,angle2,direction)) +{/*<asyxml></code><documentation>Return the path of the ellipse 'el' from angle1 to angle2 in degrees, + drawing in the given direction, with n nodes. + The angles are mesured relatively to the axis (C,x-axis) where C is + the center of the ellipse.</documentation></function></asyxml>*/ + if(degenerate(el)) abort("arcfromcenter: can not convert degenerated ellipse to path."); + if (angle1 > angle2) + return reverse(arcfromcenter(el, angle2, angle1, !direction, n)); + + guide op; + coordsys Rp=coordsys(el); + if (n < 1) return op; + + interpolate join = operator ..; + real stretch = max(el.a/el.b, el.b/el.a); + + if (stretch > 10) { + n *= floor(stretch/5); + join = operator --; + } + + real a1 = direction ? radians(angle1) : radians(angle2); + real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi; + real step=(a2 - a1)/(n != 1 ? n-1 : 1); + real a, r; + real da = radians(el.angle); + + for (int i=0; i < n; ++i) { + a = a1 + i * step; + r = el.b/sqrt(1 - (el.e * cos(a))^2); + op = join(op, Rp*Rp.polar(r, da + a)); + } + + return shift(el.C.x*Rp.i + el.C.y*Rp.j) * (direction ? op : reverse(op)); +} + +/*<asyxml><function type="path" signature="arcfromcenter(hyperbola,real,real,int,bool)"><code></asyxml>*/ +path arcfromcenter(hyperbola h, real angle1, real angle2, + int n = hyperbolanodesnumber(h, angle1, angle2), + bool direction = CCW) +{/*<asyxml></code><documentation>Return the path of the hyperbola 'h' from angle1 to angle2 in degrees, + drawing in the given direction, with n nodes. + The angles are mesured relatively to the axis (C, x-axis) where C is + the center of the hyperbola.</documentation></function></asyxml>*/ + guide op; + coordsys Rp = coordsys(h); + if (n < 1) return op; + if (angle1 > angle2) { + path g = reverse(arcfromcenter(h, angle2, angle1, n, !direction)); + return g == nullpath ? g : reverse(g); + } + real a1 = direction ? radians(angle1) : radians(angle2); + real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi; + real step = (a2 - a1)/(n != 1 ? n - 1 : 1); + real a, r; + typedef guide interpolate(... guide[]); + interpolate join = operator ..; + real da = radians(h.angle); + for (int i = 0; i < n; ++i) { + a = a1 + i * step; + r = (h.b * cos(a))^2 - (h.a * sin(a))^2; + if(r > epsgeo) { + r = sqrt(h.a^2 * h.b^2/r); + op = join(op, Rp * Rp.polar(r, a + da)); + join = operator ..; + } else join = operator --; + } + return shift(h.C.x * Rp.i + h.C.y * Rp.j)* + (direction ? op : op == nullpath ? op : reverse(op)); +} + +/*<asyxml><function type="path" signature="arcfromcenter(explicit conic,real,real,int,bool)"><code></asyxml>*/ +path arcfromcenter(explicit conic co, real angle1, real angle2, + int n, bool direction = CCW) +{/*<asyxml></code><documentation>Use arcfromcenter(ellipse, ...) or arcfromcenter(hyperbola, ...) depending of + the eccentricity of 'co'.</documentation></function></asyxml>*/ + path g; + if(co.e < 1) + g = arcfromcenter((ellipse)co, angle1, + angle2, direction, n); + else if(co.e > 1) + g = arcfromcenter((hyperbola)co, angle1, + angle2, n, direction); + else abort("arcfromcenter: does not exist for a parabola."); + return g; +} + +/*<asyxml><constant type = "polarconicroutine" signature="fromCenter"><code></asyxml>*/ +restricted polarconicroutine fromCenter = arcfromcenter;/*<asyxml></code><documentation></documentation></constant></asyxml>*/ +/*<asyxml><constant type = "polarconicroutine" signature="fromFocus"><code></asyxml>*/ +restricted polarconicroutine fromFocus = arcfromfocus;/*<asyxml></code><documentation></documentation></constant></asyxml>*/ + +/*<asyxml><function type="bqe" signature="equation(ellipse)"><code></asyxml>*/ +bqe equation(ellipse el) +{/*<asyxml></code><documentation>Return the coefficients of the equation of the ellipse in its coordinate system: + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0. + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + pair[] pts; + for (int i = 0; i < 360; i += 72) + pts.push(locate(angpoint(el, i))); + + real[][] M; + real[] x; + for (int i = 0; i < 5; ++i) { + M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1}; + x[i] = -pts[i].x^2; + } + real[] coef = solve(M, x); + bqe bqe = changecoordsys(coordsys(el), + bqe(defaultcoordsys, + 1, coef[0], coef[1], coef[2], coef[3], coef[4])); + bqe.a = approximate(bqe.a); + return bqe; +} + +/*<asyxml><function type="bqe" signature="equation(parabola)"><code></asyxml>*/ +bqe equation(parabola p) +{/*<asyxml></code><documentation>Return the coefficients of the equation of the parabola in its coordinate system. + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0 + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + coordsys R = canonicalcartesiansystem(p); + parabola tp = (parabola) changecoordsys(R, p); + point A = projection(tp.D) * point(R, (0, 0)); + real a = abs(A); + return changecoordsys(coordsys(p), + bqe(R, 0, 0, 1, -4 * a, 0, 0)); +} + +/*<asyxml><function type="bqe" signature="equation(hyperbola)"><code></asyxml>*/ +bqe equation(hyperbola h) +{/*<asyxml></code><documentation>Return the coefficients of the equation of the hyperbola in its coordinate system. + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0 + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + coordsys R = canonicalcartesiansystem(h); + return changecoordsys(coordsys(h), + bqe(R, 1/h.a^2, 0, -1/h.b^2, 0, 0, -1)); +} + +/*<asyxml><operator type = "path" signature="cast(ellipse)"><code></asyxml>*/ +path operator cast(ellipse el) +{/*<asyxml></code><documentation>Cast ellipse to path.</documentation></operator></asyxml>*/ + if(degenerate(el)) + abort("Casting degenerated ellipse to path is not possible."); + int n = el.e == 0 ? circlenodesnumber(el.a) : ellipsenodesnumber(el.a, el.b); + return arcfromcenter(el, 0.0, 360, CCW, n)&cycle; +} + +/*<asyxml><operator type = "path" signature="cast(circle)"><code></asyxml>*/ +path operator cast(circle c) +{/*<asyxml></code><documentation>Cast circle to path.</documentation></operator></asyxml>*/ + return (path)((ellipse)c); +} + +/*<asyxml><function type="real[]" signature="bangles(picture,parabola)"><code></asyxml>*/ +real[] bangles(picture pic = currentpicture, parabola p) +{/*<asyxml></code><documentation>Return the array {ma, Ma} where 'ma' and 'Ma' are respectively + the smaller and the larger angles for which the parabola 'p' is included + in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/ + pair bmin, bmax; + pair[] b; + if (p.bmin == p.bmax) { + bmin = pic.userMin(); + bmax = pic.userMax(); + } else { + bmin = p.bmin;bmax = p.bmax; + } + if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax))) + return new real[] {0, 0}; + b[0] = bmin; + b[1] = (bmax.x, bmin.y); + b[2] = bmax; + b[3] = (bmin.x, bmax.y); + real[] eq = changecoordsys(defaultcoordsys, equation(p)).a; + pair[] inter; + for (int i = 0; i < 4; ++i) { + pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq); + for (int j = 0; j < tmp.length; ++j) { + if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo) + inter.push(tmp[j]); + } + } + pair F = p.F, V = p.V; + real d = degrees(F - V); + real[] a = sequence(new real(int n){ + return (360 - d + degrees(inter[n]-F))%360; + }, inter.length); + real ma = a.length != 0 ? min(a) : 0, Ma= a.length != 0 ? max(a) : 0; + return new real[] {ma, Ma}; +} + +/*<asyxml><function type="real[][]" signature="bangles(picture,hyperbola)"><code></asyxml>*/ +real[][] bangles(picture pic = currentpicture, hyperbola h) +{/*<asyxml></code><documentation>Return the array {{ma1, Ma1}, {ma2, Ma2}} where 'maX' and 'MaX' are respectively + the smaller and the bigger angles (from h.FX) for which the hyperbola 'h' is included + in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/ + pair bmin, bmax; + pair[] b; + if (h.bmin == h.bmax) { + bmin = pic.userMin(); + bmax = pic.userMax(); + } else { + bmin = h.bmin;bmax = h.bmax; + } + if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax))) + return new real[][] {{0, 0}, {0, 0}}; + b[0] = bmin; + b[1] = (bmax.x, bmin.y); + b[2] = bmax; + b[3] = (bmin.x, bmax.y); + real[] eq = changecoordsys(defaultcoordsys, equation(h)).a; + pair[] inter0, inter1; + pair C = locate(h.C); + pair F1 = h.F1; + for (int i = 0; i < 4; ++i) { + pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq); + for (int j = 0; j < tmp.length; ++j) { + if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo) { + if(dot(F1 - C, tmp[j]-C) > 0) inter0.push(tmp[j]); + else inter1.push(tmp[j]); + } + } + } + real d = degrees(F1 - C); + real[] ma, Ma; + pair[][] inter = new pair[][] {inter0, inter1}; + for (int i = 0; i < 2; ++i) { + real[] a = sequence(new real(int n){ + return (360 - d + degrees(inter[i][n]-F1))%360; + }, inter[i].length); + ma[i] = a.length != 0 ? min(a) : 0; + Ma[i] = a.length != 0 ? max(a) : 0; + } + return new real[][] {{ma[0], Ma[0]}, {ma[1], Ma[1]}}; +} + +/*<asyxml><operator type = "path" signature="cast(parabola)"><code></asyxml>*/ +path operator cast(parabola p) +{/*<asyxml></code><documentation>Cast parabola to path. + If possible, the returned path is restricted to the actual bounding box + of the current picture if the variables 'p.bmin' and 'p.bmax' are not set else + the bounding box of box(p.bmin, p.bmax) is used instead.</documentation></operator></asyxml>*/ + real[] bangles = bangles(p); + int n = parabolanodesnumber(p, bangles[0], bangles[1]); + return arcfromfocus(p, bangles[0], bangles[1], n, CCW); +} + + +/*<asyxml><function type="void" signature="draw(picture,Label,circle,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", circle c, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + if(degenerate(c)) draw(pic, L, c.l, align, p, arrow, legend, marker); + else draw(pic, L, (path)c, align, p, arrow, bar, margin, legend, marker); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,ellipse,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", ellipse el, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation></documentation>Draw the ellipse 'el' if it is not degenerated else draw 'el.l'.</function></asyxml>*/ + if(degenerate(el)) draw(pic, L, el.l, align, p, arrow, legend, marker); + else draw(pic, L, (path)el, align, p, arrow, bar, margin, legend, marker); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,parabola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", parabola parabola, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw the parabola 'p' on 'pic' without (if possible) altering the + size of picture pic.</documentation></function></asyxml>*/ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen and the margins. + m -= min(p); M -= max(p); + parabola.bmin = inverse(t) * m; + parabola.bmax = inverse(t) * M; + picture tmp; + path pp = t * ((path) (T * parabola)); + + if (pp != nullpath) { + draw(tmp, L, pp, align, p, arrow, bar, NoMargin, legend, marker); + add(f, tmp.fit()); + } + }, true); + + pair m = pic.userMin(), M = pic.userMax(); + if(m != M) { + pic.addBox(truepoint(SW), truepoint(NE)); + } +} + +/*<asyxml><operator type = "path" signature="cast(hyperbola)"><code></asyxml>*/ +path operator cast(hyperbola h) +{/*<asyxml></code><documentation>Cast hyperbola to path. + If possible, the returned path is restricted to the actual bounding box + of the current picture unless the variables 'h.bmin' and 'h.bmax' + are set; in this case the bounding box of box(h.bmin, h.bmax) is used instead. + Only the branch on the side of 'h.F1' is considered.</documentation></operator></asyxml>*/ + real[][] bangles = bangles(h); + int n = hyperbolanodesnumber(h, bangles[0][0], bangles[0][1]); + return arcfromfocus(h, bangles[0][0], bangles[0][1], n, CCW); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,hyperbola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", hyperbola h, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw the hyperbola 'h' on 'pic' without (if possible) altering the + size of the picture pic.</documentation></function></asyxml>*/ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen and the margins. + m -= min(p); M -= max(p); + h.bmin = inverse(t) * m; + h.bmax = inverse(t) * M; + path hp; + + picture tmp; + hp = t * ((path) (T * h)); + if (hp != nullpath) { + draw(tmp, L, hp, align, p, arrow, bar, NoMargin, legend, marker); + } + + hyperbola ht = hyperbola(h.F2, h.F1, h.a); + ht.bmin = h.bmin; + ht.bmax = h.bmax; + + hp = t * ((path) (T * ht)); + if (hp != nullpath) { + draw(tmp, "", hp, align, p, arrow, bar, NoMargin, marker); + } + + add(f, tmp.fit()); + }, true); + + pair m = pic.userMin(), M = pic.userMax(); + if(m != M) + pic.addBox(truepoint(SW), truepoint(NE)); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,explicit conic,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", explicit conic co, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Use one of the routine 'draw(ellipse, ...)', + 'draw(parabola, ...)' or 'draw(hyperbola, ...)' depending of the value of eccentricity of 'co'.</documentation></function></asyxml>*/ + if(co.e == 0) + draw(pic, L, (circle)co, align, p, arrow, bar, margin, legend, marker); + else + if(co.e < 1) draw(pic, L, (ellipse)co, align, p, arrow, bar, margin, legend, marker); + else + if(co.e == 1) draw(pic, L, (parabola)co, align, p, arrow, bar, margin, legend, marker); + else + if(co.e > 1) draw(pic, L, (hyperbola)co, align, p, arrow, bar, margin, legend, marker); + else abort("draw: unknown conic."); +} + +/*<asyxml><function type="int" signature="conicnodesnumber(conic,real,real)"><code></asyxml>*/ +int conicnodesnumber(conic co, real angle1, real angle2, bool dir = CCW) +{/*<asyxml></code><documentation>Return the number of node to draw a conic arc.</documentation></function></asyxml>*/ + int oi; + if(co.e == 0) { + circle c = (circle)co; + oi = circlenodesnumber(c.r, angle1, angle2); + } else if(co.e < 1) { + ellipse el = (ellipse)co; + oi = ellipsenodesnumber(el.a, el.b, angle1, angle2, dir); + } else if(co.e == 1) { + parabola p = (parabola)co; + oi = parabolanodesnumber(p, angle1, angle2); + } else { + hyperbola h = (hyperbola)co; + oi = hyperbolanodesnumber(h, angle1, angle2); + } + return oi; +} + +/*<asyxml><operator type = "path" signature="cast(conic)"><code></asyxml>*/ +path operator cast(conic co) +{/*<asyxml></code><documentation>Cast conic section to path.</documentation></operator></asyxml>*/ + if(co.e < 1) return (path)((ellipse)co); + if(co.e == 1) return (path)((parabola)co); + return (path)((hyperbola)co); +} + +/*<asyxml><function type="bqe" signature="equation(explicit conic)"><code></asyxml>*/ +bqe equation(explicit conic co) +{/*<asyxml></code><documentation>Return the coefficients of the equation of conic section in its coordinate system: + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0. + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + bqe obqe; + if(co.e == 0) + obqe = equation((circle)co); + else + if(co.e < 1) obqe = equation((ellipse)co); + else + if(co.e == 1) obqe = equation((parabola)co); + else + if(co.e > 1) obqe = equation((hyperbola)co); + else abort("draw: unknown conic."); + return obqe; +} + +/*<asyxml><function type="string" signature="conictype(bqe)"><code></asyxml>*/ +string conictype(bqe bqe) +{/*<asyxml></code><documentation>Returned values are "ellipse" or "parabola" or "hyperbola" + depending of the conic section represented by 'bqe'.</documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + string os = "degenerated"; + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + if(abs(delta) < 10 * epsgeo) return os; + real J = a * c - b^2; + real I = a + c; + if(J > epsgeo) { + if(delta/I < -epsgeo); + os = "ellipse"; + } else { + if(abs(J) < epsgeo) os = "parabola"; else os = "hyperbola"; + } + return os; +} + +/*<asyxml><function type="conic" signature="conic(point,point,point,point,point)"><code></asyxml>*/ +conic conic(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the conic passing through 'M1', 'M2', 'M3', 'M4' and 'M5' if the conic is not degenerated.</documentation></function></asyxml>*/ + bqe bqe = bqe(M1, M2, M3, M4, M5); + string ct = conictype(bqe); + if(ct == "degenerated") abort("conic: degenerated conic passing through five points."); + if(ct == "ellipse") return ellipse(bqe); + if(ct == "parabola") return parabola(bqe); + return hyperbola(bqe); +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(explicit conic co) +{/*<asyxml></code><documentation>Return the canonical cartesian system of the conic 'co'.</documentation></function></asyxml>*/ + if(co.e < 1) return canonicalcartesiansystem((ellipse)co); + else if(co.e == 1) return canonicalcartesiansystem((parabola)co); + return canonicalcartesiansystem((hyperbola)co); +} + +/*<asyxml><function type="bqe" signature="canonical(bqe)"><code></asyxml>*/ +bqe canonical(bqe bqe) +{/*<asyxml></code><documentation>Return the bivariate quadratic equation relative to the + canonical coordinate system of the conic section represented by 'bqe'.</documentation></function></asyxml>*/ + string type = conictype(bqe); + if(type == "") abort("canonical: the equation can not be performed."); + bqe obqe; + if(type == "ellipse") { + ellipse el = ellipse(bqe); + obqe = changecoordsys(canonicalcartesiansystem(el), equation(el)); + } else { + if(type == "parabola") { + parabola p = parabola(bqe); + obqe = changecoordsys(canonicalcartesiansystem(p), equation(p)); + } else { + hyperbola h = hyperbola(bqe); + obqe = changecoordsys(canonicalcartesiansystem(h), equation(h)); + } + } + return obqe; +} + +/*<asyxml><function type="conic" signature="conic(bqe)"><code></asyxml>*/ +conic conic(bqe bqe) +{/*<asyxml></code><documentation>Return the conic section represented by the bivariate quartic equation 'bqe'.</documentation></function></asyxml>*/ + string type = conictype(bqe); + if(type == "") abort("canonical: the equation can not be performed."); + conic oc; + if(type == "ellipse") { + oc = ellipse(bqe); + } else { + if(type == "parabola") oc = parabola(bqe); else oc = hyperbola(bqe); + } + return oc; +} + +/*<asyxml><function type="real" signature="arclength(circle)"><code></asyxml>*/ +real arclength(circle c) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return c.r * 2 * pi; +} + +/*<asyxml><function type="real" signature="focusToCenter(ellipse,real)"><code></asyxml>*/ +real focusToCenter(ellipse el, real a) +{/*<asyxml></code><documentation>Return the angle relatively to the center of 'el' for the angle 'a' + given relatively to the focus of 'el'.</documentation></function></asyxml>*/ + pair p = point(fromFocus(el, a, a, 1, CCW), 0); + pair c = locate(el.C); + real d = degrees(p - c) - el.angle; + d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15 + return d%(sgnd(a) * 360); +} + +/*<asyxml><function type="real" signature="centerToFocus(ellipse,real)"><code></asyxml>*/ +real centerToFocus(ellipse el, real a) +{/*<asyxml></code><documentation>Return the angle relatively to the focus of 'el' for the angle 'a' + given relatively to the center of 'el'.</documentation></function></asyxml>*/ + pair P = point(fromCenter(el, a, a, 1, CCW), 0); + pair F1 = locate(el.F1); + pair F2 = locate(el.F2); + real d = degrees(P - F1) - degrees(F2 - F1); + d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15 + return d%(sgnd(a) * 360); +} + +/*<asyxml><function type="real" signature="arclength(ellipse)"><code></asyxml>*/ +real arclength(ellipse el) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return degenerate(el) ? infinity : 4 * el.a * elle(pi/2, el.e); +} + +/*<asyxml><function type="real" signature="arclength(ellipse,real,real,bool,polarconicroutine)"><code></asyxml>*/ +real arclength(ellipse el, real angle1, real angle2, + bool direction = CCW, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the length of the arc of the ellipse between 'angle1' + and 'angle2'. + 'angle1' and 'angle2' must be in the interval ]-360;+oo[ if polarconicroutine = fromFocus, + ]-oo;+oo[ if polarconicroutine = fromCenter.</documentation></function></asyxml>*/ + if(degenerate(el)) return infinity; + if(angle1 > angle2) return arclength(el, angle2, angle1, !direction, polarconicroutine); + // path g;int n = 1000; + // if(el.e == 0) g = arcfromcenter(el, angle1, angle2, n, direction); + // if(el.e != 1) g = polarconicroutine(el, angle1, angle2, n, direction); + // write("with path = ", arclength(g)); + if(polarconicroutine == fromFocus) { + // dot(point(fromFocus(el, angle1, angle1, 1, CCW), 0), 2mm + blue); + // dot(point(fromFocus(el, angle2, angle2, 1, CCW), 0), 2mm + blue); + // write("fromfocus1 = ", angle1); + // write("fromfocus2 = ", angle2); + real gle1 = focusToCenter(el, angle1); + real gle2 = focusToCenter(el, angle2); + if((gle1 - gle2) * (angle1 - angle2) > 0) { + angle1 = gle1; angle2 = gle2; + } else { + angle1 = gle2; angle2 = gle1; + } + // dot(point(fromCenter(el, angle1, angle1, 1, CCW), 0), 1mm + red); + // dot(point(fromCenter(el, angle2, angle2, 1, CCW), 0), 1mm + red); + // write("fromcenter1 = ", angle1); + // write("fromcenter2 = ", angle2); + } + if(angle1 < 0 || angle2 < 0) return arclength(el, 180 + angle1, 180 + angle2, direction, fromCenter); + real a1 = direction ? angle1 : angle2; + real a2 = direction ? angle2 : angle1 + 360; + real elleq = el.a * elle(pi/2, el.e); + real S(real a) + {//Return the arclength from 0 to the angle 'a' (in degrees) + // given form the center of the ellipse. + real gle = atan(el.a * tan(radians(a))/el.b)+ + pi * (((a%90 == 0 && a != 0) ? floor(a/90) - 1 : floor(a/90)) - + ((a%180 == 0) ? 0 : floor(a/180)) - + (a%360 == 0 ? floor(a/(360)) : 0)); + /* // Uncomment to visualize the used branches + unitsize(2cm, 1cm); + import graph; + + real xmin = 0, xmax = 3pi; + + xlimits( xmin, xmax); + ylimits( 0, 10); + yaxis( "y" , LeftRight(), RightTicks(pTick=.8red, ptick = lightgrey, extend = true)); + xaxis( "x - value", BottomTop(), Ticks(Label("$%.2f$", red), Step = pi/2, step = pi/4, pTick=.8red, ptick = lightgrey, extend = true)); + + real p2 = pi/2; + real f(real t) + { + return atan(0.6 * tan(t))+ + pi * ((t%p2 == 0 && t != 0) ? floor(t/p2) - 1 : floor(t/p2)) - + ((t%pi == 0) ? 0 : pi * floor(t/pi)) - (t%(2pi) == 0 ? pi * floor(t/(2 * pi)) : 0); + } + + draw(graph(f, xmin, xmax, 100)); + write(degrees(f(pi/2))); + write(degrees(f(pi))); + write(degrees(f(3pi/2))); + write(degrees(f(2pi))); + draw(graph(new real(real t){return t;}, xmin, xmax, 3)); + */ + return elleq - el.a * elle(pi/2 - gle, el.e); + } + return S(a2) - S(a1); +} + +/*<asyxml><function type="real" signature="arclength(parabola,real)"><code></asyxml>*/ +real arclength(parabola p, real angle) +{/*<asyxml></code><documentation>Return the arclength from 180 to 'angle' given from focus in the + canonical coordinate system of 'p'.</documentation></function></asyxml>*/ + real a = p.a; /* In canonicalcartesiansystem(p) the equation of p + is x = y^2/(4a) */ + // integrate(sqrt(1 + (x/(2 * a))^2), x); + real S(real t){return 0.5 * t * sqrt(1 + t^2/(4 * a^2)) + a * asinh(t/(2 * a));} + real R(real gle){return 2 * a/(1 - Cos(gle));} + real t = Sin(angle) * R(angle); + return S(t); +} + +/*<asyxml><function type="real" signature="arclength(parabola,real,real)"><code></asyxml>*/ +real arclength(parabola p, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the arclength from 'angle1' to 'angle2' given from + focus in the canonical coordinate system of 'p'</documentation></function></asyxml>*/ + return arclength(p, angle1) - arclength(p, angle2); +} + +/*<asyxml><function type="real" signature="arclength(parabola p)"><code></asyxml>*/ +real arclength(parabola p) +{/*<asyxml></code><documentation>Return the length of the arc of the parabola bounded to the bounding + box of the current picture.</documentation></function></asyxml>*/ + real[] b = bangles(p); + return arclength(p, b[0], b[1]); +} +// *........................CONICS.........................* +// *=======================================================* + +// *=======================================================* +// *.......................ABSCISSA........................* +/*<asyxml><struct signature="abscissa"><code></asyxml>*/ +struct abscissa +{/*<asyxml></code><documentation>Provide abscissa structure on a curve used in the routine-like 'point(object, abscissa)' + where object can be 'line','segment','ellipse','circle','conic'...</documentation><property type = "real" signature="x"><code></asyxml>*/ + real x;/*<asyxml></code><documentation>The abscissa value.</documentation></property><property type = "int" signature="system"><code></asyxml>*/ + int system;/*<asyxml></code><documentation>0 = relativesystem; 1 = curvilinearsystem; 2 = angularsystem; 3 = nodesystem</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/ + polarconicroutine polarconicroutine = fromCenter;/*<asyxml></code><documentation>The routine used with angular system and two foci conic section. + Possible values are 'formCenter' and 'formFocus'.</documentation></property></asyxml>*/ + /*<asyxml><method type = "abscissa" signature="copy()"><code></asyxml>*/ + abscissa copy() + {/*<asyxml></code><documentation>Return a copy of this abscissa.</documentation></method></asyxml>*/ + abscissa oa = new abscissa; + oa.x = this.x; + oa.system = this.system; + oa.polarconicroutine = this.polarconicroutine; + return oa; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><constant type = "int" signature="relativesystem,curvilinearsystem,angularsystem,nodesystem"><code></asyxml>*/ +restricted int relativesystem = 0, curvilinearsystem = 1, angularsystem = 2, nodesystem = 3;/*<asyxml></code><documentation>Constant used to set the abscissa system.</documentation></constant></asyxml>*/ + +/*<asyxml><operator type = "abscissa" signature="cast(explicit position)"><code></asyxml>*/ +abscissa operator cast(explicit position position) +{/*<asyxml></code><documentation>Cast position to abscissa. + If 'position' is relative, the abscissa is relative else it's a curvilinear abscissa.</documentation></operator></asyxml>*/ + abscissa oarcc; + oarcc.x = position.position.x; + oarcc.system = position.relative ? relativesystem : curvilinearsystem; + return oarcc; +} + +/*<asyxml><operator type = "abscissa" signature="+(real,explicit abscissa)"><code></asyxml>*/ +abscissa operator +(real x, explicit abscissa a) +{/*<asyxml></code><documentation>Provide 'real + abscissa'. + Return abscissa b so that b.x = a.x + x. + +(explicit abscissa, real), -(real, explicit abscissa) and -(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/ + abscissa oa = a.copy(); + oa.x = a.x + x; + return oa; +} + +abscissa operator +(explicit abscissa a, real x) +{ + return x + a; +} +abscissa operator +(int x, explicit abscissa a) +{ + return ((real)x) + a; +} + +/*<asyxml><operator type = "abscissa" signature="-(explicit abscissa a)"><code></asyxml>*/ +abscissa operator -(explicit abscissa a) +{/*<asyxml></code><documentation>Return the abscissa b so that b.x = -a.x.</documentation></operator></asyxml>*/ + abscissa oa; + oa.system = a.system; + oa.x = -a.x; + return oa; +} + +abscissa operator -(real x, explicit abscissa a) +{ + abscissa oa; + oa.system = a.system; + oa.x = x - a.x; + return oa; +} +abscissa operator -(explicit abscissa a, real x) +{ + abscissa oa; + oa.system = a.system; + oa.x = a.x - x; + return oa; +} +abscissa operator -(int x, explicit abscissa a) +{ + return ((real)x) - a; +} + +/*<asyxml><operator type = "abscissa" signature="*(real,abscissa)"><code></asyxml>*/ +abscissa operator *(real x, explicit abscissa a) +{/*<asyxml></code><documentation>Provide 'real * abscissa'. + Return abscissa b so that b.x = x * a.x. + *(explicit abscissa, real), /(real, explicit abscissa) and /(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/ + abscissa oa; + oa.system = a.system; + oa.x = a.x * x; + return oa; +} +abscissa operator *(explicit abscissa a, real x) +{ + return x * a; +} + +abscissa operator /(real x, explicit abscissa a) +{ + abscissa oa; + oa.system = a.system; + oa.x = x/a.x; + return oa; +} +abscissa operator /(explicit abscissa a, real x) +{ + abscissa oa; + oa.system = a.system; + oa.x = a.x/x; + return oa; +} + +abscissa operator /(int x, explicit abscissa a) +{ + return ((real)x)/a; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(real)"><code></asyxml>*/ +abscissa relabscissa(real x) +{/*<asyxml></code><documentation>Return a relative abscissa.</documentation></function></asyxml>*/ + return (abscissa)(Relative(x)); +} +abscissa relabscissa(int x) +{ + return (abscissa)(Relative(x)); +} + +/*<asyxml><function type="abscissa" signature="curabscissa(real)"><code></asyxml>*/ +abscissa curabscissa(real x) +{/*<asyxml></code><documentation>Return a curvilinear abscissa.</documentation></function></asyxml>*/ + return (abscissa)((position)x); +} +abscissa curabscissa(int x) +{ + return (abscissa)((position)x); +} + +/*<asyxml><function type="abscissa" signature="angabscissa(real,polarconicroutine)"><code></asyxml>*/ +abscissa angabscissa(real x, polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return a angular abscissa.</documentation></function></asyxml>*/ + abscissa oarcc; + oarcc.x = x; + oarcc.polarconicroutine = polarconicroutine; + oarcc.system = angularsystem; + return oarcc; +} +abscissa angabscissa(int x, polarconicroutine polarconicroutine = currentpolarconicroutine) +{ + return angabscissa((real)x, polarconicroutine); +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(real)"><code></asyxml>*/ +abscissa nodabscissa(real x) +{/*<asyxml></code><documentation>Return an abscissa as time on the path.</documentation></function></asyxml>*/ + abscissa oarcc; + oarcc.x = x; + oarcc.system = nodesystem; + return oarcc; +} +abscissa nodabscissa(int x) +{ + return nodabscissa((real)x); +} + +/*<asyxml><operator type = "abscissa" signature="cast(real)"><code></asyxml>*/ +abscissa operator cast(real x) +{/*<asyxml></code><documentation>Cast real to abscissa, precisely 'nodabscissa'.</documentation></operator></asyxml>*/ + return nodabscissa(x); +} +abscissa operator cast(int x) +{ + return nodabscissa((real)x); +} + +/*<asyxml><function type="point" signature="point(circle,abscissa)"><code></asyxml>*/ +point point(circle c, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'c' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + coordsys R = c.C.coordsys; + if (l.system == nodesystem) + return point(R, point((path)c, l.x)/R); + if (l.system == relativesystem) + return c.C + point(R, R.polar(c.r, 2 * pi * l.x)); + if (l.system == curvilinearsystem) + return c.C + point(R, R.polar(c.r, l.x/c.r)); + if (l.system == angularsystem) + return c.C + point(R, R.polar(c.r, radians(l.x))); + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(ellipse,abscissa)"><code></asyxml>*/ +point point(ellipse el, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'el' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + if(el.e == 0) return point((circle)el, l); + coordsys R = coordsys(el); + if (l.system == nodesystem) + return point(R, point((path)el, l.x)/R); + if (l.system == relativesystem) { + return point(el, curabscissa((l.x%1) * arclength(el))); + } + if (l.system == curvilinearsystem) { + real a1 = 0, a2 = 360, cx = 0; + real aout = a1; + real x = abs(l.x)%arclength(el); + while (abs(cx - x) > epsgeo) { + aout = (a1 + a2)/2; + cx = arclength(el, 0, aout, CCW, fromCenter); //fromCenter is speeder + if(cx > x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2; + } + path pel = fromCenter(el, sgn(l.x) * aout, sgn(l.x) * aout, 1, CCW); + return point(R, point(pel, 0)/R); + } + if (l.system == angularsystem) { + return point(R, point(l.polarconicroutine(el, l.x, l.x, 1, CCW), 0)/R); + } + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(parabola,abscissa)"><code></asyxml>*/ +point point(parabola p, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'p' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + coordsys R = coordsys(p); + if (l.system == nodesystem) + return point(R, point((path)p, l.x)/R); + if (l.system == relativesystem) { + real[] b = bangles(p); + real al = sgn(l.x) > 0 ? arclength(p, 180, b[1]) : arclength(p, 180, b[0]); + return point(p, curabscissa(abs(l.x) * al)); + } + if (l.system == curvilinearsystem) { + real a1 = 1e-3, a2 = 360 - 1e-3, cx = infinity; + while (abs(cx - l.x) > epsgeo) { + cx = arclength(p, 180, (a1 + a2)/2); + if(cx > l.x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2; + } + path pp = fromFocus(p, a1, a1, 1, CCW); + return point(R, point(pp, 0)/R); + } + if (l.system == angularsystem) { + return point(R, point(fromFocus(p, l.x, l.x, 1, CCW), 0)/R); + } + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(hyperbola,abscissa)"><code></asyxml>*/ +point point(hyperbola h, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'h' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + coordsys R = coordsys(h); + if (l.system == nodesystem) + return point(R, point((path)h, l.x)/R); + if (l.system == relativesystem) { + abort("point(hyperbola, relativeSystem) is not implemented... +Try relpoint((path)your_hyperbola, x);"); + } + if (l.system == curvilinearsystem) { + abort("point(hyperbola, curvilinearSystem) is not implemented..."); + } + if (l.system == angularsystem) { + return point(R, point(l.polarconicroutine(h, l.x, l.x, 1, CCW), 0)/R); + } + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="abscissa" signature="point(conic,point)"><code></asyxml>*/ +point point(explicit conic co, abscissa l) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e == 0) return point((circle)co, l); + if(co.e < 1) return point((ellipse)co, l); + if(co.e == 1) return point((parabola)co, l); + return point((hyperbola)co, l); +} + + +/*<asyxml><function type="point" signature="point(line,abscissa)"><code></asyxml>*/ +point point(line l, abscissa x) +{/*<asyxml></code><documentation>Return the point of 'l' which has the abscissa 'l.x' according to the abscissa system 'l.system'. + Note that the origin is l.A, and point(l, relabscissa(x)) returns l.A + x.x * vector(l.B - l.A).</documentation></function></asyxml>*/ + coordsys R = l.A.coordsys; + if (x.system == nodesystem) + return l.A + (x.x < 0 ? 0 : x.x > 1 ? 1 : x.x) * vector(l.B - l.A); + if (x.system == relativesystem) + return l.A + x.x * vector(l.B - l.A); + if (x.system == curvilinearsystem) + return l.A + x.x * l.u; + if (x.system == angularsystem) + abort("point: what the meaning of angular abscissa on line ?."); + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(line,real)"><code></asyxml>*/ +point point(line l, explicit real x) +{/*<asyxml></code><documentation>Return the point between node l.A and l.B (x <= 0 means l.A, x >=1 means l.B).</documentation></function></asyxml>*/ + return point(l, nodabscissa(x)); +} +point point(line l, explicit int x) +{ + return point(l, nodabscissa(x)); +} + +/*<asyxml><function type="circle" signature="point(explicit circle,explicit real)"><code></asyxml>*/ +point point(explicit circle c, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(c, nodabscissa(x)); +} +point point(explicit circle c, explicit int x) +{ + return point(c, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit ellipse,explicit real)"><code></asyxml>*/ +point point(explicit ellipse el, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(el, nodabscissa(x)); +} +point point(explicit ellipse el, explicit int x) +{ + return point(el, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit parabola,explicit real)"><code></asyxml>*/ +point point(explicit parabola p, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(p, nodabscissa(x)); +} +point point(explicit parabola p, explicit int x) +{ + return point(p, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit hyperbola,explicit real)"><code></asyxml>*/ +point point(explicit hyperbola h, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(h, nodabscissa(x)); +} +point point(explicit hyperbola h, explicit int x) +{ + return point(h, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit conic,explicit real)"><code></asyxml>*/ +point point(explicit conic co, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + point op; + if(co.e == 0) op = point((circle)co, nodabscissa(x)); + else if(co.e < 1) op = point((ellipse)co, nodabscissa(x)); + else if(co.e == 1) op = point((parabola)co, nodabscissa(x)); + else op = point((hyperbola)co, nodabscissa(x)); + return op; +} +point point(explicit conic co, explicit int x) +{ + return point(co, (real)x); +} + +/*<asyxml><function type="point" signature="relpoint(line,real)"><code></asyxml>*/ +point relpoint(line l, real x) +{/*<asyxml></code><documentation>Return the relative point of 'l' (0 means l.A, + 1 means l.B, x means l.A + x * vector(l.B - l.A) ).</documentation></function></asyxml>*/ + return point(l, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit circle,real)"><code></asyxml>*/ +point relpoint(explicit circle c, real x) +{/*<asyxml></code><documentation>Return the relative point of 'c' (0 means origin, 1 means end). + Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/ + return point(c, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit ellipse,real)"><code></asyxml>*/ +point relpoint(explicit ellipse el, real x) +{/*<asyxml></code><documentation>Return the relative point of 'el' (0 means origin, 1 means end).</documentation></function></asyxml>*/ + return point(el, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit parabola,real)"><code></asyxml>*/ +point relpoint(explicit parabola p, real x) +{/*<asyxml></code><documentation>Return the relative point of the path of the parabola + bounded by the bounding box of the current picture. + 0 means origin, 1 means end, where the origin is the vertex of 'p'.</documentation></function></asyxml>*/ + return point(p, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit hyperbola,real)"><code></asyxml>*/ +point relpoint(explicit hyperbola h, real x) +{/*<asyxml></code><documentation>Not yet implemented... <look href = "point(hyperbola, abscissa)"/></documentation></function></asyxml>*/ + return point(h, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit conic,explicit real)"><code></asyxml>*/ +point relpoint(explicit conic co, explicit real x) +{/*<asyxml></code><documentation>Return the relative point of 'co' (0 means origin, 1 means end).</documentation></function></asyxml>*/ + point op; + if(co.e == 0) op = point((circle)co, Relative(x)); + else if(co.e < 1) op = point((ellipse)co, Relative(x)); + else if(co.e == 1) op = point((parabola)co, Relative(x)); + else op = point((hyperbola)co, Relative(x)); + return op; +} +point relpoint(explicit conic co, explicit int x) +{ + return relpoint(co, (real)x); +} + +/*<asyxml><function type="point" signature="angpoint(explicit circle,real)"><code></asyxml>*/ +point angpoint(explicit circle c, real x) +{/*<asyxml></code><documentation>Return the point of 'c' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/ + return point(c, angabscissa(x)); +} + +/*<asyxml><function type="point" signature="angpoint(explicit ellipse,real,polarconicroutine)"><code></asyxml>*/ +point angpoint(explicit ellipse el, real x, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the point of 'el' in the direction 'x' + measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/ + return el.e == 0 ? angpoint((circle) el, x) : point(el, angabscissa(x, polarconicroutine)); +} + +/*<asyxml><function type="point" signature="angpoint(explicit parabola,real)"><code></asyxml>*/ +point angpoint(explicit parabola p, real x) +{/*<asyxml></code><documentation>Return the point of 'p' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/ + return point(p, angabscissa(x)); +} + +/*<asyxml><function type="point" signature="angpoint(explicit hyperbola,real,polarconicroutine)"><code></asyxml>*/ +point angpoint(explicit hyperbola h, real x, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the point of 'h' in the direction 'x' + measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/ + return point(h, angabscissa(x, polarconicroutine)); +} + +/*<asyxml><function type="point" signature="curpoint(line,real)"><code></asyxml>*/ +point curpoint(line l, real x) +{/*<asyxml></code><documentation>Return the point of 'l' which has the curvilinear abscissa 'x'. + Origin is l.A.</documentation></function></asyxml>*/ + return point(l, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(explicit circle,real)"><code></asyxml>*/ +point curpoint(explicit circle c, real x) +{/*<asyxml></code><documentation>Return the point of 'c' which has the curvilinear abscissa 'x'. + Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/ + return point(c, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(explicit ellipse,real)"><code></asyxml>*/ +point curpoint(explicit ellipse el, real x) +{/*<asyxml></code><documentation>Return the point of 'el' which has the curvilinear abscissa 'el'.</documentation></function></asyxml>*/ + return point(el, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(explicit parabola,real)"><code></asyxml>*/ +point curpoint(explicit parabola p, real x) +{/*<asyxml></code><documentation>Return the point of 'p' which has the curvilinear abscissa 'x'. + Origin is the vertex of 'p'.</documentation></function></asyxml>*/ + return point(p, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(conic,real)"><code></asyxml>*/ +point curpoint(conic co, real x) +{/*<asyxml></code><documentation>Return the point of 'co' which has the curvilinear abscissa 'x'.</documentation></function></asyxml>*/ + point op; + if(co.e == 0) op = point((circle)co, curabscissa(x)); + else if(co.e < 1) op = point((ellipse)co, curabscissa(x)); + else if(co.e == 1) op = point((parabola)co, curabscissa(x)); + else op = point((hyperbola)co, curabscissa(x)); + return op; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(circle,point)"><code></asyxml>*/ +abscissa angabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("angabscissa: the point is not on the circle."); + abscissa oa; + oa.system = angularsystem; + oa.x = degrees(M - c.C); + if(oa.x < 0) oa.x+=360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(ellipse,point,polarconicroutine)"><code></asyxml>*/ +abscissa angabscissa(ellipse el, point M, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the ellipse 'el' according to 'polarconicroutine'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("angabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = polarconicroutine; + oa.x = polarconicroutine == fromCenter ? degrees(M - el.C) : degrees(M - el.F1); + oa.x -= el.angle; + if(oa.x < 0) oa.x += 360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(hyperbola,point,polarconicroutine)"><code></asyxml>*/ +abscissa angabscissa(hyperbola h, point M, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the hyperbola 'h' according to 'polarconicroutine'.</documentation></function></asyxml>*/ + if(!(M @ h)) abort("angabscissa: the point is not on the hyperbola."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = polarconicroutine; + oa.x = polarconicroutine == fromCenter ? degrees(M - h.C) : degrees(M - h.F1) + 180; + oa.x -= h.angle; + if(oa.x < 0) oa.x += 360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(parabola,point)"><code></asyxml>*/ +abscissa angabscissa(parabola p, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/ + if(!(M @ p)) abort("angabscissa: the point is not on the parabola."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = fromFocus;// Not used + oa.x = degrees(M - p.F); + oa.x -= p.angle; + if(oa.x < 0) oa.x += 360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(conic,point)"><code></asyxml>*/ +abscissa angabscissa(explicit conic co, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e == 0) return angabscissa((circle)co, M); + if(co.e < 1) return angabscissa((ellipse)co, M); + if(co.e == 1) return angabscissa((parabola)co, M); + return angabscissa((hyperbola)co, M); +} + +/*<asyxml><function type="abscissa" signature="curabscissa(line,point)"><code></asyxml>*/ +abscissa curabscissa(line l, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/ + if(!(M @ extend(l))) abort("curabscissa: the point is not on the line."); + abscissa oa; + oa.system = curvilinearsystem; + oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A); + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(circle,point)"><code></asyxml>*/ +abscissa curabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("curabscissa: the point is not on the circle."); + abscissa oa; + oa.system = curvilinearsystem; + oa.x = pi * angabscissa(c, M).x * c.r/180; + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(ellipse,point)"><code></asyxml>*/ +abscissa curabscissa(ellipse el, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("curabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = curvilinearsystem; + real a = angabscissa(el, M, fromCenter).x; + oa.x = arclength(el, 0, a, fromCenter); + oa.polarconicroutine = fromCenter; + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(parabola,point)"><code></asyxml>*/ +abscissa curabscissa(parabola p, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/ + if(!(M @ p)) abort("curabscissa: the point is not on the parabola."); + abscissa oa; + oa.system = curvilinearsystem; + real a = angabscissa(p, M).x; + oa.x = arclength(p, 180, a); + oa.polarconicroutine = fromFocus; // Not used. + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(conic,point)"><code></asyxml>*/ +abscissa curabscissa(conic co, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e > 1) abort("curabscissa: not implemented for this hyperbola."); + if(co.e == 0) return curabscissa((circle)co, M); + if(co.e < 1) return curabscissa((ellipse)co, M); + return curabscissa((parabola)co, M); +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(line,point)"><code></asyxml>*/ +abscissa nodabscissa(line l, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/ + if(!(M @ (segment)l)) abort("nodabscissa: the point is not on the segment."); + abscissa oa; + oa.system = nodesystem; + oa.x = abs(M - l.A)/abs(l.A - l.B); + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(circle,point)"><code></asyxml>*/ +abscissa nodabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("nodabscissa: the point is not on the circle."); + abscissa oa; + oa.system = nodesystem; + oa.x = intersect((path)c, locate(M))[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(ellipse,point)"><code></asyxml>*/ +abscissa nodabscissa(ellipse el, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("nodabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = nodesystem; + oa.x = intersect((path)el, M)[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(parabola,point)"><code></asyxml>*/ +abscissa nodabscissa(parabola p, point M) +{/*<asyxml></code><documentation>Return the node abscissa OF 'M' on the parabola 'p'.</documentation></function></asyxml>*/ + if(!(M @ p)) abort("nodabscissa: the point is not on the parabola."); + abscissa oa; + oa.system = nodesystem; + path pg = p; + real[] t = intersect(pg, M, 1e-5); + if(t.length == 0) abort("nodabscissa: the point is not on the path of the parabola."); + oa.x = t[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(conic,point)"><code></asyxml>*/ +abscissa nodabscissa(conic co, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e > 1) abort("nodabscissa: not implemented for hyperbola."); + if(co.e == 0) return nodabscissa((circle)co, M); + if(co.e < 1) return nodabscissa((ellipse)co, M); + return nodabscissa((parabola)co, M); +} + + +/*<asyxml><function type="abscissa" signature="relabscissa(line,point)"><code></asyxml>*/ +abscissa relabscissa(line l, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/ + if(!(M @ extend(l))) abort("relabscissa: the point is not on the line."); + abscissa oa; + oa.system = relativesystem; + oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A)/abs(l.A - l.B); + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(circle,point)"><code></asyxml>*/ +abscissa relabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("relabscissa: the point is not on the circle."); + abscissa oa; + oa.system = relativesystem; + oa.x = angabscissa(c, M).x/360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(ellipse,point)"><code></asyxml>*/ +abscissa relabscissa(ellipse el, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("relabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = relativesystem; + oa.x = curabscissa(el, M).x/arclength(el); + oa.polarconicroutine = fromFocus; + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(conic,point)"><code></asyxml>*/ +abscissa relabscissa(conic co, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' + on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e > 1) abort("relabscissa: not implemented for hyperbola and parabola."); + if(co.e == 1) return relabscissa((parabola)co, M); + if(co.e == 0) return relabscissa((circle)co, M); + return relabscissa((ellipse)co, M); +} +// *.......................ABSCISSA........................* +// *=======================================================* + +// *=======================================================* +// *.........................ARCS..........................* +/*<asyxml><struct signature="arc"><code></asyxml>*/ +struct arc { + /*<asyxml></code><documentation>Implement oriented ellipse (included circle) arcs. + All the calculus with this structure will be as exact as Asymptote can do. + For a full precision, you must not cast 'arc' to 'path' excepted for drawing routines. + </documentation><property type = "ellipse" signature="el"><code></asyxml>*/ + ellipse el;/*<asyxml></code><documentation>The support of the arc.</documentation></property><property type = "real" signature="angle0"><code></asyxml>*/ + restricted real angle0 = 0;/*<asyxml></code><documentation>Internal use: rotating a circle does not modify the origin point,this variable stocks the eventual angle rotation. This value is not used for ellipses which are not circles.</documentation></property><property type = "real" signature="angle1,angle2"><code></asyxml>*/ + restricted real angle1, angle2;/*<asyxml></code><documentation>Values (in degrees) in ]-360, 360[.</documentation></property><property type = "bool" signature="direction"><code></asyxml>*/ + bool direction = CCW;/*<asyxml></code><documentation>The arc will be drawn from 'angle1' to 'angle2' rotating in the direction 'direction'.</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/ + polarconicroutine polarconicroutine = currentpolarconicroutine;/*<asyxml></code><documentation>The routine to which the angles refer. + If 'el' is a circle 'fromCenter' is always used.</documentation></property></asyxml>*/ + + /*<asyxml><method type = "void" signature="setangles(real,real,real)"><code></asyxml>*/ + void setangles(real a0, real a1, real a2) + {/*<asyxml></code><documentation>Set the angles.</documentation></method></asyxml>*/ + if (a1 < 0 && a2 < 0) { + a1 += 360; + a2 += 360; + } + this.angle0 = a0%(sgnd(a0) * 360); + this.angle1 = a1%(sgnd(a1) * 360); + this.angle2 = a2%(sgnd(2) * 360); + } + + /*<asyxml><method type = "void" signature="init(ellipse,real,real,real,polarconicroutine,bool)"><code></asyxml>*/ + void init(ellipse el, real angle0 = 0, real angle1, real angle2, + polarconicroutine polarconicroutine, + bool direction = CCW) + {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/ + if(abs(angle1 - angle2) > 360) abort("arc: |angle1 - angle2| > 360."); + this.el = el; + this.setangles(angle0, angle1, angle2); + this.polarconicroutine = polarconicroutine; + this.direction = direction; + } + + /*<asyxml><method type = "arc" signature="copy()"><code></asyxml>*/ + arc copy() + {/*<asyxml></code><documentation>Copy the arc.</documentation></method></asyxml>*/ + arc oa = new arc; + oa.el = this.el; + oa.direction = this.direction; + oa.polarconicroutine = this.polarconicroutine; + oa.angle1 = this.angle1; + oa.angle2 = this.angle2; + oa.angle0 = this.angle0; + return oa; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="polarconicroutine" signature="polarconicroutine(ellipse)"><code></asyxml>*/ +polarconicroutine polarconicroutine(conic co) +{/*<asyxml></code><documentation>Return the default routine used to draw a conic.</documentation></function></asyxml>*/ + if(co.e == 0) return fromCenter; + if(co.e == 1) return fromFocus; + return currentpolarconicroutine; +} + +/*<asyxml><function type="arc" signature="arc(ellipse,real,real,polarconicroutine,bool)"><code></asyxml>*/ +arc arc(ellipse el, real angle1, real angle2, + polarconicroutine polarconicroutine = polarconicroutine(el), + bool direction = CCW) +{/*<asyxml></code><documentation>Return the ellipse arc from 'angle1' to 'angle2' with respect to 'polarconicroutine' and rotating in the direction 'direction'.</documentation></function></asyxml>*/ + arc oa; + oa.init(el, 0, angle1, angle2, polarconicroutine, direction); + return oa; +} + +/*<asyxml><function type="arc" signature="complementary(arc)"><code></asyxml>*/ +arc complementary(arc a) +{/*<asyxml></code><documentation>Return the complementary of 'a'.</documentation></function></asyxml>*/ + arc oa; + oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, a.direction); + return oa; +} + +/*<asyxml><function type="arc" signature="reverse(arc)"><code></asyxml>*/ +arc reverse(arc a) +{/*<asyxml></code><documentation>Return arc 'a' oriented in reverse direction.</documentation></function></asyxml>*/ + arc oa; + oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, !a.direction); + return oa; +} + +/*<asyxml><function type="real" signature="degrees(arc)"><code></asyxml>*/ +real degrees(arc a) +{/*<asyxml></code><documentation>Return the measure in degrees of the oriented arc 'a'.</documentation></function></asyxml>*/ + real or; + real da = a.angle2 - a.angle1; + if(a.direction) { + or = a.angle1 < a.angle2 ? da : 360 + da; + } else { + or = a.angle1 < a.angle2 ? -360 + da : da; + } + return or; +} + +/*<asyxml><function type="real" signature="angle(a)"><code></asyxml>*/ +real angle(arc a) +{/*<asyxml></code><documentation>Return the measure in radians of the oriented arc 'a'.</documentation></function></asyxml>*/ + return radians(degrees(a)); +} + +/*<asyxml><function type="int" signature="arcnodesnumber(explicit arc)"><code></asyxml>*/ +int arcnodesnumber(explicit arc a) +{/*<asyxml></code><documentation>Return the number of nodes to draw the arc 'a'.</documentation></function></asyxml>*/ + return ellipsenodesnumber(a.el.a, a.el.b, a.angle1, a.angle2, a.direction); +} + +private path arctopath(arc a, int n) +{ + if(a.el.e == 0) return arcfromcenter(a.el, a.angle0 + a.angle1, a.angle0 + a.angle2, a.direction, n); + if(a.el.e != 1) return a.polarconicroutine(a.el, a.angle1, a.angle2, n, a.direction); + return arcfromfocus(a.el, a.angle1, a.angle2, n, a.direction); +} + +/*<asyxml><function type="point" signature="angpoint(arc,real)"><code></asyxml>*/ +point angpoint(arc a, real angle) +{/*<asyxml></code><documentation>Return the point given by its angular position (in degrees) relative to the arc 'a'. + If 'angle > degrees(a)' or 'angle < 0' the returned point is on the extended arc.</documentation></function></asyxml>*/ + pair p; + if(a.el.e == 0) { + real gle = a.angle0 + a.angle1 + (a.direction ? angle : -angle); + p = point(arcfromcenter(a.el, gle, gle, CCW, 1), 0); + } + else { + real gle = a.angle1 + (a.direction ? angle : -angle); + p = point(a.polarconicroutine(a.el, gle, gle, 1, CCW), 0); + } + return point(coordsys(a.el), p/coordsys(a.el)); +} + +/*<asyxml><operator type = "path" signature="cast(explicit arc)"><code></asyxml>*/ +path operator cast(explicit arc a) +{/*<asyxml></code><documentation>Cast arc to path.</documentation></operator></asyxml>*/ + return arctopath(a, arcnodesnumber(a)); +} + +/*<asyxml><operator type = "guide" signature="cast(explicit arc)"><code></asyxml>*/ +guide operator cast(explicit arc a) +{/*<asyxml></code><documentation>Cast arc to guide.</documentation></operator></asyxml>*/ + return arctopath(a, arcnodesnumber(a)); +} + +/*<asyxml><operator type = "arc" signature="*(transform,explicit arc)"><code></asyxml>*/ +arc operator *(transform t, explicit arc a) +{/*<asyxml></code><documentation>Provide transform * arc.</documentation></operator></asyxml>*/ + pair[] P, PP; + path g = arctopath(a, 3); + real a0, a1 = a.angle1, a2 = a.angle2, ap1, ap2; + bool dir = a.direction; + P[0] = t * point(g, 0); + P[1] = t * point(g, 2); + ellipse el = t * a.el; + arc oa; + a0 = (a.angle0 + angle(shiftless(t)))%360; + pair C; + if(a.polarconicroutine == fromCenter) C = el.C; else C = el.F1; + real d = abs(locate(el.F2 - el.F1)) > epsgeo ? + degrees(locate(el.F2 - el.F1)) : a0 + degrees(el.C.coordsys.i); + ap1 = (degrees(P[0]-C, false) - d)%360; + ap2 = (degrees(P[1]-C, false) - d)%360; + oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir); + g = arctopath(oa, 3); + PP[0] = point(g, 0); + PP[1] = point(g, 2); + if((a1 - a2) * (ap1 - ap2) < 0) {// Handle reflection. + dir=!a.direction; + oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir); + } + return oa; +} + +/*<asyxml><operator type = "arc" signature="*(real,explicit arc)"><code></asyxml>*/ +arc operator *(real x, explicit arc a) +{/*<asyxml></code><documentation>Provide real * arc. + Return the arc subtracting and adding '(x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/ + real a1, a2, gle; + gle = (x - 1) * degrees(a)/2; + a1 = a.angle1 - gle; + a2 = a.angle2 + gle; + arc oa; + oa.init(a.el, a.angle0, a1, a2, a.polarconicroutine, a.direction); + return oa; +} +arc operator *(int x, explicit arc a){return (real)x * a;} +/*<asyxml><operator type = "arc" signature="/(real,explicit arc)"><code></asyxml>*/ +arc operator /(explicit arc a, real x) +{/*<asyxml></code><documentation>Provide arc/real. + Return the arc subtracting and adding '(1/x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/ + return (1/x) * a; +} +/*<asyxml><operator type = "arc" signature="+(explicit arc,point)"><code></asyxml>*/ +arc operator +(explicit arc a, point M) +{/*<asyxml></code><documentation>Provide arc + point. + Return shifted arc. + 'operator +(explicit arc, point)', 'operator +(explicit arc, vector)' and 'operator -(explicit arc, vector)' are also defined.</documentation></operator></asyxml>*/ + return shift(M) * a; +} +arc operator -(explicit arc a, point M){return a + (-M);} +arc operator +(explicit arc a, vector v){return shift(locate(v)) * a;} +arc operator -(explicit arc a, vector v){return a + (-v);} + + +/*<asyxml><operator type = "bool" signature="@(point,arc)"><code></asyxml>*/ +bool operator @(point M, arc a) +{/*<asyxml></code><documentation>Return true iff 'M' is on the arc 'a'.</documentation></operator></asyxml>*/ + if (!(M @ a.el)) return false; + coordsys R = defaultcoordsys; + path ap = arctopath(a, 3); + line l = line(point(R, point(ap, 0)), point(R, point(ap, 2))); + return sameside(M, point(R, point(ap, 1)), l); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,arc,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", arc a, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, margin margin = NoMargin, + Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw 'arc' adding the pen returned by 'addpenarc(p)' to the pen 'p'. + <look href = "#addpenarc"/></documentation></function></asyxml>*/ + draw(pic, L, (path)a, align, addpenarc(p), arrow, bar, margin, legend, marker); +} + +/*<asyxml><function type="real" signature="arclength(arc)"><code></asyxml>*/ +real arclength(arc a) +{/*<asyxml></code><documentation>The arc length of 'a'.</documentation></function></asyxml>*/ + return arclength(a.el, a.angle1, a.angle2, a.direction, a.polarconicroutine); +} + +private point ppoint(arc a, real x) +{// Return the point of the arc proportionally to its length. + point oP; + if(a.el.e == 0) { // Case of circle. + oP = angpoint(a, x * abs(degrees(a))); + } else { // Ellipse and not circle. + if(!a.direction) { + transform t = reflect(line(a.el.F1, a.el.F2)); + return t * ppoint(t * a, x); + } + + real angle1 = a.angle1, angle2 = a.angle2; + if(a.polarconicroutine == fromFocus) { + // dot(point(fromFocus(a.el, angle1, angle1, 1, CCW), 0), 2mm + blue); + // dot(point(fromFocus(a.el, angle2, angle2, 1, CCW), 0), 2mm + blue); + // write("fromfocus1 = ", angle1); + // write("fromfocus2 = ", angle2); + real gle1 = focusToCenter(a.el, angle1); + real gle2 = focusToCenter(a.el, angle2); + if((gle1 - gle2) * (angle1 - angle2) > 0) { + angle1 = gle1; angle2 = gle2; + } else { + angle1 = gle2; angle2 = gle1; + } + // write("fromcenter1 = ", angle1); + // write("fromcenter2 = ", angle2); + // dot(point(fromCenter(a.el, angle1, angle1, 1, CCW), 0), 1mm + red); + // dot(point(fromCenter(a.el, angle2, angle2, 1, CCW), 0), 1mm + red); + } + + if(angle1 > angle2) { + arc ta = a.copy(); + ta.polarconicroutine = fromCenter; + ta.setangles(a0 = a.angle0, a1 = angle1 - 360, a2 = angle2); + return ppoint(ta, x); + } + ellipse co = a.el; + real gle, a1, a2, cx = 0; + bool direction; + if(x >= 0) { + a1 = angle1; + a2 = a1 + 360; + direction = CCW; + } else { + a1 = angle1 - 360; + a2 = a1 - 360; + direction = CW; + } + gle = a1; + real L = arclength(co, angle1, angle2, a.direction, fromCenter); + real tx = L * abs(x)%arclength(co); + real aout = a1; + while(abs(cx - tx) > epsgeo) { + aout = (a1 + a2)/2; + cx = abs(arclength(co, gle, aout, direction, fromCenter)); + if(cx > tx) a2 = (a1 + a2)/2 ; else a1 = (a1 + a2)/2; + } + pair p = point(arcfromcenter(co, aout, aout, CCW, 1), 0); + oP = point(coordsys(co), p/coordsys(co)); + } + return oP; +} + +/*<asyxml><function type="point" signature="point(arc,abscissa)"><code></asyxml>*/ +point point(arc a, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'a' which has the abscissa 'l.x' + according to the abscissa system 'l.system'. + Note that 'a.polarconicroutine' is used instead of 'l.polarconicroutine'. + <look href = "#struct abscissa"/></documentation></function></asyxml>*/ + real posx; + arc ta = a.copy(); + ellipse co = a.el; + if (l.system == relativesystem) { + posx = l.x; + } else + if (l.system == curvilinearsystem) { + real tl; + if(co.e == 0) { + tl = curabscissa(a.el, angpoint(a.el, a.angle0 + a.angle1)).x; + return curpoint(a.el, tl + (a.direction ? l.x : -l.x)); + } else { + tl = curabscissa(a.el, angpoint(a.el, a.angle1, a.polarconicroutine)).x; + return curpoint(a.el, tl + (a.direction ? l.x : -l.x)); + } + } else + if (l.system == nodesystem) { + coordsys R = coordsys(co); + return point(R, point((path)a, l.x)/R); + } else + if (l.system == angularsystem) { + return angpoint(a, l.x); + } else abort("point: bad abscissa system."); + return ppoint(ta, posx); +} + + +/*<asyxml><function type="point" signature="point(arc,real)"><code></asyxml>*/ +point point(arc a, real x) +{/*<asyxml></code><documentation>Return the point between node floor(t) and floor(t) + 1.</documentation></function></asyxml>*/ + return point(a, nodabscissa(x)); +} +pair point(explicit arc a, int x) +{ + return point(a, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="relpoint(arc,real)"><code></asyxml>*/ +point relpoint(arc a, real x) +{/*<asyxml></code><documentation>Return the relative point of 'a'. + If x > 1 or x < 0, the returned point is on the extended arc.</documentation></function></asyxml>*/ + return point(a, relabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(arc,real)"><code></asyxml>*/ +point curpoint(arc a, real x) +{/*<asyxml></code><documentation>Return the point of 'a' which has the curvilinear abscissa 'x'. + If x < 0 or x > arclength(a), the returned point is on the extended arc.</documentation></function></asyxml>*/ + return point(a, curabscissa(x)); +} + +/*<asyxml><function type="abscissa" signature="angabscissa(arc,point)"><code></asyxml>*/ +abscissa angabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' according to the arc 'a'.</documentation></function></asyxml>*/ + if(!(M @ a.el)) + abort("angabscissa: the point is not on the extended arc."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = a.polarconicroutine; + real am = angabscissa(a.el, M, a.polarconicroutine).x; + oa.x = (am - a.angle1 - (a.el.e == 0 ? a.angle0 : 0))%360; + oa.x = a.direction ? oa.x : 360 - oa.x; + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(arc,point)"><code></asyxml>*/ +abscissa curabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa according to the arc 'a'.</documentation></function></asyxml>*/ + ellipse el = a.el; + if(!(M @ el)) + abort("angabscissa: the point is not on the extended arc."); + abscissa oa; + oa.system = curvilinearsystem; + real xm = curabscissa(el, M).x; + real a0 = el.e == 0 ? a.angle0 : 0; + real am = curabscissa(el, angpoint(el, a.angle1 + a0, a.polarconicroutine)).x; + real l = arclength(el); + oa.x = (xm - am)%l; + oa.x = a.direction ? oa.x : l - oa.x; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(arc,point)"><code></asyxml>*/ +abscissa nodabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the node abscissa according to the arc 'a'.</documentation></function></asyxml>*/ + if(!(M @ a)) + abort("nodabscissa: the point is not on the arc."); + abscissa oa; + oa.system = nodesystem; + oa.x = intersect((path)a, M)[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(arc,point)"><code></asyxml>*/ +abscissa relabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the relative abscissa according to the arc 'a'.</documentation></function></asyxml>*/ + ellipse el = a.el; + if(!( M @ el)) + abort("relabscissa: the point is not on the prolonged arc."); + abscissa oa; + oa.system = relativesystem; + oa.x = curabscissa(a, M).x/arclength(a); + return oa; +} + +/*<asyxml><function type="void" signature="markarc(picture,Label,int,real,real,arc,arrowbar,pen,pen,margin,marker)"><code></asyxml>*/ +void markarc(picture pic = currentpicture, + Label L = "", + int n = 1, real radius = 0, real space = 0, + arc a, + pen sectorpen = currentpen, + pen markpen = sectorpen, + margin margin = NoMargin, + arrowbar arrow = None, + marker marker = nomarker) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + real Da = degrees(a); + pair p1 = point(a, 0); + pair p2 = relpoint(a, 1); + pair c = a.polarconicroutine == fromCenter ? locate(a.el.C) : locate(a.el.F1); + if(radius == 0) radius = markangleradius(markpen); + if(abs(Da) > 180) radius = -radius; + radius = (a.direction ? 1 : -1) * sgnd(Da) * radius; + draw(c--p1^^c--p2, sectorpen); + markangle(pic = pic, L = L, n = n, radius = radius, space = space, + A = p1, O = c, B = p2, + arrow = arrow, p = markpen, margin = margin, + marker = marker); +} +// *.........................ARCS..........................* +// *=======================================================* + +// *=======================================================* +// *........................MASSES.........................* +/*<asyxml><struct signature="mass"><code></asyxml>*/ +struct mass {/*<asyxml></code><documentation></documentation><property type = "point" signature="M"><code></asyxml>*/ + point M;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="m"><code></asyxml>*/ + real m;/*<asyxml></code><documentation></documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="mass" signature="mass(point,real)"><code></asyxml>*/ +mass mass(point M, real m) +{/*<asyxml></code><documentation>Constructor of mass point.</documentation></function></asyxml>*/ + mass om; + om.M = M; + om.m = m; + return om; +} + +/*<asyxml><operator type = "point" signature="cast(mass)"><code></asyxml>*/ +point operator cast(mass m) +{/*<asyxml></code><documentation>Cast mass point to point.</documentation></operator></asyxml>*/ + point op; + op = m.M; + op.m = m.m; + return op; +} +/*<asyxml><function type="point" signature="point(explicit mass)"><code></asyxml>*/ +point point(explicit mass m){return m;}/*<asyxml></code><documentation>Cast + 'm' to point</documentation></function></asyxml>*/ + +/*<asyxml><operator type = "mass" signature="cast(point)"><code></asyxml>*/ +mass operator cast(point M) +{/*<asyxml></code><documentation>Cast point to mass point.</documentation></operator></asyxml>*/ + mass om; + om.M = M; + om.m = M.m; + return om; +} +/*<asyxml><function type="mass" signature="mass(explicit point)"><code></asyxml>*/ +mass mass(explicit point P) +{/*<asyxml></code><documentation>Cast 'P' to mass.</documentation></function></asyxml>*/ + return mass(P, P.m); +} + +/*<asyxml><operator type = "point[]" signature="cast(mass[])"><code></asyxml>*/ +point[] operator cast(mass[] m) +{/*<asyxml></code><documentation>Cast mass[] to point[].</documentation></operator></asyxml>*/ + point[] op; + for(mass am : m) op.push(point(am)); + return op; +} + +/*<asyxml><operator type = "mass[]" signature="cast(point[])"><code></asyxml>*/ +mass[] operator cast(point[] P) +{/*<asyxml></code><documentation>Cast point[] to mass[].</documentation></operator></asyxml>*/ + mass[] om; + for(point op : P) om.push(mass(op)); + return om; +} + +/*<asyxml><function type="mass" signature="mass(coordsys,explicit pair,real)"><code></asyxml>*/ +mass mass(coordsys R, explicit pair p, real m) +{/*<asyxml></code><documentation>Return the mass which has coordinates + 'p' with respect to 'R' and weight 'm'.</documentation></function></asyxml>*/ + return point(R, p, m);// Using casting. +} + +/*<asyxml><operator type = "mass" signature="cast(pair)"><code></asyxml>*/ +mass operator cast(pair m){return mass((point)m, 1);}/*<asyxml></code><documentation>Cast pair to mass point.</documentation></operator></asyxml>*/ +/*<asyxml><operator type = "path" signature="cast(mass)"><code></asyxml>*/ +path operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass point to path.</documentation></operator></asyxml>*/ +/*<asyxml><operator type = "guide" signature="cast(mass)"><code></asyxml>*/ +guide operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass to guide.</documentation></operator></asyxml>*/ + +/*<asyxml><operator type = "mass" signature="+(mass,mass)"><code></asyxml>*/ +mass operator +(mass M1, mass M2) +{/*<asyxml></code><documentation>Provide mass + mass. + mass - mass is also defined.</documentation></operator></asyxml>*/ + return mass(M1.M + M2.M, M1.m + M2.m); +} +mass operator -(mass M1, mass M2) +{ + return mass(M1.M - M2.M, M1.m - M2.m); +} + +/*<asyxml><operator type = "mass" signature="*(real,mass)"><code></asyxml>*/ +mass operator *(real x, explicit mass M) +{/*<asyxml></code><documentation>Provide real * mass. + The resulted mass is the mass of 'M' multiplied by 'x' . + mass/real, mass + real and mass - real are also defined.</documentation></operator></asyxml>*/ + return mass(M.M, x * M.m); +} +mass operator *(int x, explicit mass M){return mass(M.M, x * M.m);} +mass operator /(explicit mass M, real x){return mass(M.M, M.m/x);} +mass operator /(explicit mass M, int x){return mass(M.M, M.m/x);} +mass operator +(explicit mass M, real x){return mass(M.M, M.m + x);} +mass operator +(explicit mass M, int x){return mass(M.M, M.m + x);} +mass operator -(explicit mass M, real x){return mass(M.M, M.m - x);} +mass operator -(explicit mass M, int x){return mass(M.M, M.m - x);} +/*<asyxml><operator type = "mass" signature="*(transform,mass)"><code></asyxml>*/ +mass operator *(transform t, mass M) +{/*<asyxml></code><documentation>Provide transform * mass.</documentation></operator></asyxml>*/ + return mass(t * M.M, M.m); +} + +/*<asyxml><function type="mass" signature="masscenter(... mass[])"><code></asyxml>*/ +mass masscenter(... mass[] M) +{/*<asyxml></code><documentation>Return the center of the masses 'M'.</documentation></function></asyxml>*/ + point[] P; + for (int i = 0; i < M.length; ++i) + P.push(M[i].M); + P = standardizecoordsys(currentcoordsys, true ... P); + real m = M[0].m; + point oM = M[0].m * P[0]; + for (int i = 1; i < M.length; ++i) { + oM += M[i].m * P[i]; + m += M[i].m; + } + if (m == 0) abort("masscenter: the sum of masses is null."); + return mass(oM/m, m); +} + +/*<asyxml><function type="string" signature="massformat(string,string,mass)"><code></asyxml>*/ +string massformat(string format = defaultmassformat, + string s, mass M) +{/*<asyxml></code><documentation>Return the string formated by 'format' with the mass value. + In the parameter 'format', %L will be replaced by 's'. + <look href = "#defaultmassformat"/>.</documentation></function></asyxml>*/ + return format == "" ? s : + format(replace(format, "%L", replace(s, "$", "")), M.m); +} + +/*<asyxml><function type="void" signature="label(picture,Label,explicit mass,align,string,pen,filltype)"><code></asyxml>*/ +void label(picture pic = currentpicture, Label L, explicit mass M, + align align = NoAlign, string format = defaultmassformat, + pen p = nullpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw label returned by massformat(format, L, M) at coordinates of M. + <look href = "#massformat(string, string, mass)"/>.</documentation></function></asyxml>*/ + Label lL = L.copy(); + lL.s = massformat(format, lL.s, M); + Label L = Label(lL, M.M, align, p, filltype); + add(pic, L); +} + +/*<asyxml><function type="void" signature="dot(picture,Label,explicit mass,align,string,pen)"><code></asyxml>*/ +void dot(picture pic = currentpicture, Label L, explicit mass M, align align = NoAlign, + string format = defaultmassformat, pen p = currentpen) +{/*<asyxml></code><documentation>Draw a dot with label 'L' as + label(picture, Label, explicit mass, align, string, pen, filltype) does. + <look href = "#label(picture, Label, mass, align, string, pen, filltype)"/>.</documentation></function></asyxml>*/ + Label lL = L.copy(); + lL.s = massformat(format, lL.s, M); + lL.position(locate(M.M)); + lL.align(align, E); + lL.p(p); + dot(pic, M.M, p); + add(pic, lL); +} +// *........................MASSES.........................* +// *=======================================================* + +// *=======================================================* +// *.......................TRIANGLES.......................* +/*<asyxml><function type="point" signature="orthocentercenter(point,point,point)"><code></asyxml>*/ +point orthocentercenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the orthocenter of the triangle ABC.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair pp = extension(A, projection(P[1], P[2]) * P[0], B, projection(P[0], P[2]) * P[1]); + return point(R, pp/R); +} + +/*<asyxml><function type="point" signature="centroid(point,point,point)"><code></asyxml>*/ +point centroid(point A, point B, point C) +{/*<asyxml></code><documentation>Return the centroid of the triangle ABC.</documentation></function></asyxml>*/ + return (A + B + C)/3; +} + +/*<asyxml><function type="point" signature="incenter(point,point,point)"><code></asyxml>*/ +point incenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the center of the incircle of the triangle ABC.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair a = A, b = B, c = C; + pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c)); + return point(R, pp/R); +} + +/*<asyxml><function type="real" signature="inradius(point,point,point)"><code></asyxml>*/ +real inradius(point A, point B, point C) +{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle ABC.</documentation></function></asyxml>*/ + point IC = incenter(A, B, C); + return abs(IC - projection(A, B) * IC); +} + +/*<asyxml><function type="circle" signature="incircle(point,point,point)"><code></asyxml>*/ +circle incircle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the incircle of the triangle ABC.</documentation></function></asyxml>*/ + point IC = incenter(A, B, C); + return circle(IC, abs(IC - projection(A, B) * IC)); +} + +/*<asyxml><function type="point" signature="excenter(point,point,point)"><code></asyxml>*/ +point excenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the center of the excircle of the triangle tangent with (AB).</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair a = A, b = B, c = C; + pair pp = extension(a, a + rotate(90) * dir(a--b, a--c), b, b + rotate(90) * dir(b--a, b--c)); + return point(R, pp/R); +} + +/*<asyxml><function type="real" signature="exradius(point,point,point)"><code></asyxml>*/ +real exradius(point A, point B, point C) +{/*<asyxml></code><documentation>Return the radius of the excircle of the triangle ABC with (AB).</documentation></function></asyxml>*/ + point EC = excenter(A, B, C); + return abs(EC - projection(A, B) * EC); +} + +/*<asyxml><function type="circle" signature="excircle(point,point,point)"><code></asyxml>*/ +circle excircle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the excircle of the triangle ABC tangent with (AB).</documentation></function></asyxml>*/ + point center = excenter(A, B, C); + real radius = abs(center - projection(B, C) * center); + return circle(center, radius); +} + +private int[] numarray = {1, 2, 3}; +numarray.cyclic = true; + +/*<asyxml><struct signature="triangle"><code></asyxml>*/ +struct triangle {/*<asyxml></code><documentation></documentation></asyxml>*/ + + /*<asyxml><struct signature="vertex"><code></asyxml>*/ + struct vertex {/*<asyxml></code><documentation>Structure used to communicate the vertex of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/ + int n;/*<asyxml></code><documentation>1 means VA,2 means VB,3 means VC,4 means VA etc...</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/ + triangle t;/*<asyxml></code><documentation>The triangle to which the vertex refers.</documentation></property></asyxml>*/ + }/*<asyxml></struct></asyxml>*/ + + /*<asyxml><property type = "point" signature="A,B,C"><code></asyxml>*/ + restricted point A, B, C;/*<asyxml></code><documentation>The vertices of the triangle (as point).</documentation></property><property type = "vertex" signature="VA, VB, VC"><code></asyxml>*/ + restricted vertex VA, VB, VC;/*<asyxml></code><documentation>The vertices of the triangle (as vertex). + Note that the vertex structure contains the triangle to wish it refers.</documentation></property></asyxml>*/ + VA.n = 1;VB.n = 2;VC.n = 3; + + /*<asyxml><method type = "vertex" signature="vertex(int)"><code></asyxml>*/ + vertex vertex(int n) + {/*<asyxml></code><documentation>Return numbered vertex. + 'n' is 1 means VA, 2 means VB, 3 means VC, 4 means VA etc...</documentation></method></asyxml>*/ + n = numarray[n - 1]; + if(n == 1) return VA; + else if(n == 2) return VB; + return VC; + } + + /*<asyxml><method type = "point" signature="point(int)"><code></asyxml>*/ + point point(int n) + {/*<asyxml></code><documentation>Return numbered point. + n is 1 means A, 2 means B, 3 means C, 4 means A etc...</documentation></method></asyxml>*/ + n = numarray[n - 1]; + if(n == 1) return A; + else if(n == 2) return B; + return C; + } + + /*<asyxml><method type = "void" signature="init(point,point,point)"><code></asyxml>*/ + void init(point A, point B, point C) + {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + this.A = P[0]; + this.B = P[1]; + this.C = P[2]; + VA.t = this; VB.t = this; VC.t = this; + } + + /*<asyxml><method type = "void" signature="operator init(point,point,point)"><code></asyxml>*/ + void operator init(point A, point B, point C) + {/*<asyxml></code><documentation>For backward compatibility. + Provide the routine 'triangle(point A, point B, point C)'.</documentation></method></asyxml>*/ + this.init(A, B, C); + } + + /*<asyxml><method type = "void" signature="init(real,real,real,real,point)"><code></asyxml>*/ + void operator init(real b, real alpha, real c, real angle = 0, point A = (0, 0)) + {/*<asyxml></code><documentation>For backward compatibility. + Provide the routine 'triangle(real b, real alpha, real c, real angle = 0, point A = (0, 0)) + which returns the triangle ABC rotated by 'angle' (in degrees) and where b = AC, degrees(A) = alpha, AB = c.</documentation></method></asyxml>*/ + coordsys R = A.coordsys; + this.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha))); + } + + /*<asyxml><method type = "real" signature="a(),b(),c()"><code></asyxml>*/ + real a() + {/*<asyxml></code><documentation>Return the length BC. + b() and c() are also defined and return the length AC and AB respectively.</documentation></method></asyxml>*/ + return length(C - B); + } + real b() {return length(A - C);} + real c() {return length(B - A);} + + private real det(pair a, pair b) {return a.x * b.y - a.y * b.x;} + + /*<asyxml><method type = "real" signature="area()"><code></asyxml>*/ + real area() + {/*<asyxml></code><documentation></documentation></method></asyxml>*/ + pair a = locate(A), b = locate(B), c = locate(C); + return 0.5 * abs(det(a, b) + det(b, c) + det(c, a)); + } + + /*<asyxml><method type = "real" signature="alpha(),beta(),gamma()"><code></asyxml>*/ + real alpha() + {/*<asyxml></code><documentation>Return the measure (in degrees) of the angle A. + beta() and gamma() are also defined and return the measure of the angles B and C respectively.</documentation></method></asyxml>*/ + return degrees(acos((b()^2 + c()^2 - a()^2)/(2b() * c()))); + } + real beta() {return degrees(acos((c()^2 + a()^2 - b()^2)/(2c() * a())));} + real gamma() {return degrees(acos((a()^2 + b()^2 - c()^2)/(2a() * b())));} + + /*<asyxml><method type = "path" signature="Path()"><code></asyxml>*/ + path Path() + {/*<asyxml></code><documentation>The path of the triangle.</documentation></method></asyxml>*/ + return A--C--B--cycle; + } + + /*<asyxml><struct signature="side"><code></asyxml>*/ + struct side + {/*<asyxml></code><documentation>Structure used to communicate the side of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/ + int n;/*<asyxml></code><documentation>1 or 0 means [AB],-1 means [BA],2 means [BC],-2 means [CB] etc.</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/ + triangle t;/*<asyxml></code><documentation>The triangle to which the side refers.</documentation></property></asyxml>*/ + }/*<asyxml></struct></asyxml>*/ + + /*<asyxml><property type = "side" signature="AB"><code></asyxml>*/ + side AB;/*<asyxml></code><documentation>For the routines using the structure 'side', triangle.AB means 'side AB'. + BA, AC, CA etc are also defined.</documentation></property></asyxml>*/ + AB.n = 1; AB.t = this; + side BA; BA.n = -1; BA.t = this; + side BC; BC.n = 2; BC.t = this; + side CB; CB.n = -2; CB.t = this; + side CA; CA.n = 3; CA.t = this; + side AC; AC.n = -3; AC.t = this; + + /*<asyxml><method type = "side" signature="side(int)"><code></asyxml>*/ + side side(int n) + {/*<asyxml></code><documentation>Return numbered side. + n is 1 means AB, -1 means BA, 2 means BC, -2 means CB, etc.</documentation></method></asyxml>*/ + if(n == 0) abort('Invalid side number.'); + int an = numarray[abs(n)-1]; + if(an == 1) return n > 0 ? AB : BA; + else if(an == 2) return n > 0 ? BC : CB; + return n > 0 ? CA : AC; + } + + /*<asyxml><method type = "line" signature="line(int)"><code></asyxml>*/ + line line(int n) + {/*<asyxml></code><documentation>Return the numbered line.</documentation></method></asyxml>*/ + if(n == 0) abort('Invalid line number.'); + int an = numarray[abs(n)-1]; + if(an == 1) return n > 0 ? line(A, B) : line(B, A); + else if(an == 2) return n > 0 ? line(B, C) : line(C, B); + return n > 0 ? line(C, A) : line(A, C); + } + +}/*<asyxml></struct></asyxml>*/ + +from triangle unravel side; // The structure 'side' is now available outside the triangle structure. +from triangle unravel vertex; // The structure 'vertex' is now available outside the triangle structure. + +triangle[] operator ^^(triangle[] t1, triangle t2) +{ + triangle[] T; + for (int i = 0; i < t1.length; ++i) T.push(t1[i]); + T.push(t2); + return T; +} + +triangle[] operator ^^(... triangle[] t) +{ + triangle[] T; + for (int i = 0; i < t.length; ++i) { + T.push(t[i]); + } + return T; +} + +/*<asyxml><operator type = "line" signature="cast(side)"><code></asyxml>*/ +line operator cast(side side) +{/*<asyxml></code><documentation>Cast side to (infinite) line. + Most routine with line parameters works with side parameters. + One can use the code 'segment(a_side)' to obtain a line segment.</documentation></operator></asyxml>*/ + triangle t = side.t; + return t.line(side.n); +} + +/*<asyxml><function type="line" signature="line(explicit side)"><code></asyxml>*/ +line line(explicit side side) +{/*<asyxml></code><documentation>Return 'side' as line.</documentation></function></asyxml>*/ + return (line)side; +} + +/*<asyxml><function type="segment" signature="segment(explicit side)"><code></asyxml>*/ +segment segment(explicit side side) +{/*<asyxml></code><documentation>Return 'side' as segment.</documentation></function></asyxml>*/ + return (segment)(line)side; +} + +/*<asyxml><operator type = "point" signature="cast(vertex)"><code></asyxml>*/ +point operator cast(vertex V) +{/*<asyxml></code><documentation>Cast vertex to point. + Most routine with point parameters works with vertex parameters.</documentation></operator></asyxml>*/ + return V.t.point(V.n); +} + +/*<asyxml><function type="point" signature="point(explicit vertex)"><code></asyxml>*/ +point point(explicit vertex V) +{/*<asyxml></code><documentation>Return the point corresponding to the vertex 'V'.</documentation></function></asyxml>*/ + return (point)V; +} + +/*<asyxml><function type="side" signature="opposite(vertex)"><code></asyxml>*/ +side opposite(vertex V) +{/*<asyxml></code><documentation>Return the opposite side of vertex 'V'.</documentation></function></asyxml>*/ + return V.t.side(numarray[abs(V.n)]); +} + +/*<asyxml><function type="vertex" signature="opposite(side)"><code></asyxml>*/ +vertex opposite(side side) +{/*<asyxml></code><documentation>Return the opposite vertex of side 'side'.</documentation></function></asyxml>*/ + return side.t.vertex(numarray[abs(side.n) + 1]); +} + +/*<asyxml><function type="point" signature="midpoint(side)"><code></asyxml>*/ +point midpoint(side side) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return midpoint(segment(side)); +} + +/*<asyxml><operator type = "triangle" signature="*(transform,triangle)"><code></asyxml>*/ +triangle operator *(transform T, triangle t) +{/*<asyxml></code><documentation>Provide transform * triangle.</documentation></operator></asyxml>*/ + return triangle(T * t.A, T * t.B, T * t.C); +} + +/*<asyxml><function type="triangle" signature="triangleAbc(real,real,real,real,point)"><code></asyxml>*/ +triangle triangleAbc(real alpha, real b, real c, real angle = 0, point A = (0, 0)) +{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BAC = alpha, AC = b and AB = c.</documentation></function></asyxml>*/ + triangle T; + coordsys R = A.coordsys; + T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha))); + return T; +} + +/*<asyxml><function type="triangle" signature="triangleabc(real,real,real,real,point)"><code></asyxml>*/ +triangle triangleabc(real a, real b, real c, real angle = 0, point A = (0, 0)) +{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BC = a, AC = b and AB = c.</documentation></function></asyxml>*/ + triangle T; + coordsys R = A.coordsys; + T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle) + acos((b^2 + c^2 - a^2)/(2 * b * c)))); + return T; +} + +/*<asyxml><function type="triangle" signature="triangle(line,line,line)"><code></asyxml>*/ +triangle triangle(line l1, line l2, line l3) +{/*<asyxml></code><documentation>Return the triangle defined by three line.</documentation></function></asyxml>*/ + point P1, P2, P3; + P1 = intersectionpoint(l1, l2); + P2 = intersectionpoint(l1, l3); + P3 = intersectionpoint(l2, l3); + if(!(defined(P1) && defined(P2) && defined(P3))) abort("triangle: two lines are parallel."); + return triangle(P1, P2, P3); +} + +/*<asyxml><function type="point" signature="foot(vertex)"><code></asyxml>*/ +point foot(vertex V) +{/*<asyxml></code><documentation>Return the endpoint of the altitude from V.</documentation></function></asyxml>*/ + return projection((line)opposite(V)) * ((point)V); +} + +/*<asyxml><function type="point" signature="foot(side)"><code></asyxml>*/ +point foot(side side) +{/*<asyxml></code><documentation>Return the endpoint of the altitude on 'side'.</documentation></function></asyxml>*/ + return projection((line)side) * point(opposite(side)); +} + +/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/ +line altitude(vertex V) +{/*<asyxml></code><documentation>Return the altitude passing through 'V'.</documentation></function></asyxml>*/ + return line(point(V), foot(V)); +} + +/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/ +line altitude(side side) +{/*<asyxml></code><documentation>Return the altitude cutting 'side'.</documentation></function></asyxml>*/ + return altitude(opposite(side)); +} + +/*<asyxml><function type="point" signature="orthocentercenter(triangle)"><code></asyxml>*/ +point orthocentercenter(triangle t) +{/*<asyxml></code><documentation>Return the orthocenter of the triangle t.</documentation></function></asyxml>*/ + return orthocentercenter(t.A, t.B, t.C); +} + +/*<asyxml><function type="point" signature="centroid(triangle)"><code></asyxml>*/ +point centroid(triangle t) +{/*<asyxml></code><documentation>Return the centroid of the triangle 't'.</documentation></function></asyxml>*/ + return (t.A + t.B + t.C)/3; +} + +/*<asyxml><function type="point" signature="circumcenter(triangle)"><code></asyxml>*/ +point circumcenter(triangle t) +{/*<asyxml></code><documentation>Return the circumcenter of the triangle 't'.</documentation></function></asyxml>*/ + return circumcenter(t.A, t.B, t.C); +} + +/*<asyxml><function type="circle" signature="circle(triangle)"><code></asyxml>*/ +circle circle(triangle t) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/ + return circle(t.A, t.B, t.C); +} + +/*<asyxml><function type="circle" signature="circumcircle(triangle)"><code></asyxml>*/ +circle circumcircle(triangle t) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/ + return circle(t.A, t.B, t.C); +} + +/*<asyxml><function type="point" signature="incenter(triangle)"><code></asyxml>*/ +point incenter(triangle t) +{/*<asyxml></code><documentation>Return the center of the incircle of the triangle 't'.</documentation></function></asyxml>*/ + return incenter(t.A, t.B, t.C); +} + +/*<asyxml><function type="real" signature="inradius(triangle)"><code></asyxml>*/ +real inradius(triangle t) +{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle 't'.</documentation></function></asyxml>*/ + return inradius(t.A, t.B, t.C); +} + +/*<asyxml><function type="circle" signature="incircle(triangle)"><code></asyxml>*/ +circle incircle(triangle t) +{/*<asyxml></code><documentation>Return the the incircle of the triangle 't'.</documentation></function></asyxml>*/ + return incircle(t.A, t.B, t.C); +} + +/*<asyxml><function type="point" signature="excenter(side,triangle)"><code></asyxml>*/ +point excenter(side side) +{/*<asyxml></code><documentation>Return the center of the excircle tangent with the side 'side' of its triangle. + side = 0 means AB, 1 means AC, other means BC. + One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/ + point op; + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) op = excenter(t.A, t.B, t.C); + else if(n == 2) op = excenter(t.B, t.C, t.A); + else op = excenter(t.C, t.A, t.B); + return op; +} + +/*<asyxml><function type="real" signature="exradius(side,triangle)"><code></asyxml>*/ +real exradius(side side) +{/*<asyxml></code><documentation>Return radius of the excircle tangent with the side 'side' of its triangle. + side = 0 means AB, 1 means BC, other means CA. + One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/ + real or; + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) or = exradius(t.A, t.B, t.C); + else if(n == 2) or = exradius(t.B, t.C, t.A); + else or = exradius(t.A, t.C, t.B); + return or; +} + +/*<asyxml><function type="circle" signature="excircle(side,triangle)"><code></asyxml>*/ +circle excircle(side side) +{/*<asyxml></code><documentation>Return the excircle tangent with the side 'side' of its triangle. + side = 0 means AB, 1 means AC, other means BC. + One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/ + circle oc; + int n = numarray[abs(side.n) - 1]; + triangle t = side.t; + if(n == 1) oc = excircle(t.A, t.B, t.C); + else if(n == 2) oc = excircle(t.B, t.C, t.A); + else oc = excircle(t.A, t.C, t.B); + return oc; +} + +/*<asyxml><struct signature="trilinear"><code></asyxml>*/ +struct trilinear +{/*<asyxml></code><documentation>Trilinear coordinates 'a:b:c' relative to triangle 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation><property type = "real" signature="a,b,c"><code></asyxml>*/ + real a,b,c;/*<asyxml></code><documentation>The trilinear coordinates.</documentation></property><property type = "triangle" signature="t"><code></asyxml>*/ + triangle t;/*<asyxml></code><documentation>The reference triangle.</documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="trilinear" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/ +trilinear trilinear(triangle t, real a, real b, real c) +{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/ + trilinear ot; + ot.a = a; ot.b = b; ot.c = c; + ot.t = t; + return ot; +} + +/*<asyxml><function type="trilinear" signature="trilinear(triangle,point)"><code></asyxml>*/ +trilinear trilinear(triangle t, point M) +{/*<asyxml></code><documentation>Return the trilinear coordinates of 'M' relative to 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/ + trilinear ot; + pair m = locate(M); + int sameside(pair A, pair B, pair m, pair p) + {// Return 1 if 'm' and 'p' are same side of line (AB) else return -1. + pair mil = (A + B)/2; + pair mA = rotate(90, mil) * A; + pair mB = rotate(-90, mil) * A; + return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB)) ? 1 : -1; + } + real det(pair a, pair b) {return a.x * b.y - a.y * b.x;} + real area(pair a, pair b, pair c){return 0.5 * abs(det(a, b) + det(b, c) + det(c, a));} + pair A = t.A, B = t.B, C = t.C; + real t1 = area(B, C, m), t2 = area(C, A, m), t3 = area(A, B, m); + ot.a = sameside(B, C, A, m) * t1/t.a(); + ot.b = sameside(A, C, B, m) * t2/t.b(); + ot.c = sameside(A, B, C, m) * t3/t.c(); + ot.t = t; + return ot; +} + +/*<asyxml><function type="void" signature="write(trilinear)"><code></asyxml>*/ +void write(trilinear tri) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + write(format("%f : ", tri.a) + format("%f : ", tri.b) + format("%f", tri.c)); +} + +/*<asyxml><function type="point" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/ +point point(trilinear tri) +{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/ + triangle t = tri.t; + return masscenter(0.5 * t.a() * mass(t.A, tri.a), + 0.5 * t.b() * mass(t.B, tri.b), + 0.5 * t.c() * mass(t.C, tri.c)); +} + +/*<asyxml><function type="int[]" signature="tricoef(side)"><code></asyxml>*/ +int[] tricoef(side side) +{/*<asyxml></code><documentation>Return an array of integer (values are 0 or 1) which represents 'side'. + For example, side = t.BC will be represented by {0, 1, 1}.</documentation></function></asyxml>*/ + int[] oi; + int n = numarray[abs(side.n) - 1]; + oi.push((n == 1 || n == 3) ? 1 : 0); + oi.push((n == 1 || n == 2) ? 1 : 0); + oi.push((n == 2 || n == 3) ? 1 : 0); + return oi; +} + +/*<asyxml><operator type = "point" signature="cast(trilinear)"><code></asyxml>*/ +point operator cast(trilinear tri) +{/*<asyxml></code><documentation>Cast trilinear to point. + One may use the routine 'point(trilinear)' to force the casting.</documentation></operator></asyxml>*/ + return point(tri); +} + +/*<asyxml><typedef type = "centerfunction" return = "real" params = "real, real, real"><code></asyxml>*/ +typedef real centerfunction(real, real, real);/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></typedef></asyxml>*/ + +/*<asyxml><function type="trilinear" signature="trilinear(triangle,centerfunction,real,real,real)"><code></asyxml>*/ +trilinear trilinear(triangle t, centerfunction f, real a = t.a(), real b = t.b(), real c = t.c()) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></function></asyxml>*/ + return trilinear(t, f(a, b, c), f(b, c, a), f(c, a, b)); +} + +/*<asyxml><function type="point" signature="symmedian(triangle)"><code></asyxml>*/ +point symmedian(triangle t) +{/*<asyxml></code><documentation>Return the symmedian point of 't'.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, b, c); + B = trilinear(t, a, 0, c); + return intersectionpoint(line(t.A, A), line(t.B, B)); +} + +/*<asyxml><function type="point" signature="symmedian(side)"><code></asyxml>*/ +point symmedian(side side) +{/*<asyxml></code><documentation>The symmedian point on the side 'side'.</documentation></function></asyxml>*/ + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) return trilinear(t, t.a(), t.b(), 0); + if(n == 2) return trilinear(t, 0, t.b(), t.c()); + return trilinear(t, t.a(), 0, t.c()); +} + +/*<asyxml><function type="line" signature="symmedian(vertex)"><code></asyxml>*/ +line symmedian(vertex V) +{/*<asyxml></code><documentation>Return the symmedian passing through 'V'.</documentation></function></asyxml>*/ + return line(point(V), symmedian(V.t)); +} + +/*<asyxml><function type="triangle" signature="cevian(triangle,point)"><code></asyxml>*/ +triangle cevian(triangle t, point P) +{/*<asyxml></code><documentation>Return the Cevian triangle with respect of 'P' + <url href = "http://mathworld.wolfram.com/CevianTriangle.html"/>.</documentation></function></asyxml>*/ + trilinear tri = trilinear(t, locate(P)); + point A = point(trilinear(t, 0, tri.b, tri.c)); + point B = point(trilinear(t, tri.a, 0, tri.c)); + point C = point(trilinear(t, tri.a, tri.b, 0)); + return triangle(A, B, C); +} + +/*<asyxml><function type="point" signature="cevian(side,point)"><code></asyxml>*/ +point cevian(side side, point P) +{/*<asyxml></code><documentation>Return the Cevian point on 'side' with respect of 'P'.</documentation></function></asyxml>*/ + triangle t = side.t; + trilinear tri = trilinear(t, locate(P)); + int[] s = tricoef(side); + return point(trilinear(t, s[0] * tri.a, s[1] * tri.b, s[2] * tri.c)); +} + +/*<asyxml><function type="line" signature="cevian(vertex,point)"><code></asyxml>*/ +line cevian(vertex V, point P) +{/*<asyxml></code><documentation>Return line passing through 'V' and its Cevian image with respect of 'P'.</documentation></function></asyxml>*/ + return line(point(V), cevian(opposite(V), P)); +} + +/*<asyxml><function type="point" signature="gergonne(triangle)"><code></asyxml>*/ +point gergonne(triangle t) +{/*<asyxml></code><documentation>Return the Gergonne point of 't'.</documentation></function></asyxml>*/ + real f(real a, real b, real c){return 1/(a * (b + c - a));} + return point(trilinear(t, f)); +} + +/*<asyxml><function type="point[]" signature="fermat(triangle)"><code></asyxml>*/ +point[] fermat(triangle t) +{/*<asyxml></code><documentation>Return the Fermat points of 't'.</documentation></function></asyxml>*/ + point[] P; + real A = t.alpha(), B = t.beta(), C = t.gamma(); + P.push(point(trilinear(t, 1/Sin(A + 60), 1/Sin(B + 60), 1/Sin(C + 60)))); + P.push(point(trilinear(t, 1/Sin(A - 60), 1/Sin(B - 60), 1/Sin(C - 60)))); + return P; +} + +/*<asyxml><function type="point" signature="isotomicconjugate(triangle,point)"><code></asyxml>*/ +point isotomicconjugate(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/ + if(!inside(t.Path(), locate(M))) abort("isotomic: the point must be inside the triangle."); + trilinear tr = trilinear(t, M); + return point(trilinear(t, 1/(t.a()^2 * tr.a), 1/(t.b()^2 * tr.b), 1/(t.c()^2 * tr.c))); +} + +/*<asyxml><function type="line" signature="isotomic(vertex,point)"><code></asyxml>*/ +line isotomic(vertex V, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/>.</documentation></function></asyxml>*/ + side op = opposite(V); + return line(V, rotate(180, midpoint(op)) * cevian(op, M)); +} + +/*<asyxml><function type="point" signature="isotomic(side,point)"><code></asyxml>*/ +point isotomic(side side, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/ + return intersectionpoint(isotomic(opposite(side), M), side); +} + +/*<asyxml><function type="triangle" signature="isotomic(triangle,point)"><code></asyxml>*/ +triangle isotomic(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/ + return triangle(isotomic(t.BC, M), isotomic(t.CA, M), isotomic(t.AB, M)); +} + +/*<asyxml><function type="point" signature="isogonalconjugate(triangle,point)"><code></asyxml>*/ +point isogonalconjugate(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + trilinear tr = trilinear(t, M); + return point(trilinear(t, 1/tr.a, 1/tr.b, 1/tr.c)); +} + +/*<asyxml><function type="point" signature="isogonal(side,point)"><code></asyxml>*/ +point isogonal(side side, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + return cevian(side, isogonalconjugate(side.t, M)); +} + +/*<asyxml><function type="line" signature="isogonal(vertex,point)"><code></asyxml>*/ +line isogonal(vertex V, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + return line(V, isogonal(opposite(V), M)); +} + +/*<asyxml><function type="triangle" signature="isogonal(triangle,point)"><code></asyxml>*/ +triangle isogonal(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + return triangle(isogonal(t.BC, M), isogonal(t.CA, M), isogonal(t.AB, M)); +} + +/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/ +triangle pedal(triangle t, point M) +{/*<asyxml></code><documentation>Return the pedal triangle of 'M' in 't'. + <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/ + return triangle(projection(t.BC) * M, projection(t.AC) * M, projection(t.AB) * M); +} + +/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/ +line pedal(side side, point M) +{/*<asyxml></code><documentation>Return the pedal line of 'M' cutting 'side'. + <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/ + return line(M, projection(side) * M); +} + +/*<asyxml><function type="triangle" signature="antipedal(triangle,point)"><code></asyxml>*/ +triangle antipedal(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/AntipedalTriangle.html"/></documentation></function></asyxml>*/ + trilinear Tm = trilinear(t, M); + real a = Tm.a, b = Tm.b, c = Tm.c; + real CA = Cos(t.alpha()), CB = Cos(t.beta()), CC = Cos(t.gamma()); + point A = trilinear(t, -(b + a * CC) * (c + a * CB), (c + a * CB) * (a + b * CC), (b + a * CC) * (a + c * CB)); + point B = trilinear(t, (c + b * CA) * (b + a * CC), -(c + b * CA) * (a + b * CC), (a + b * CC) * (b + c * CA)); + point C = trilinear(t, (b + c * CA) * (c + a * CB), (a + c * CB) * (c + b * CA), -(a + c * CB) * (b + c * CA)); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/ +triangle extouch(triangle t) +{/*<asyxml></code><documentation>Return the extouch triangle of the triangle 't'. + The extouch triangle of 't' is the triangle formed by the points + of tangency of a triangle 't' with its excircles.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, (a - b + c)/b, (a + b - c)/c); + B = trilinear(t, (-a + b + c)/a, 0, (a + b - c)/c); + C = trilinear(t, (-a + b + c)/a, (a - b + c)/b, 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/ +triangle incentral(triangle t) +{/*<asyxml></code><documentation>Return the incentral triangle of the triangle 't'. + It is the triangle whose vertices are determined by the intersections of the + reference triangle's angle bisectors with the respective opposite sides.</documentation></function></asyxml>*/ + point A, B, C; + // real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, 1, 1); + B = trilinear(t, 1, 0, 1); + C = trilinear(t, 1, 1, 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="extouch(side)"><code></asyxml>*/ +triangle extouch(side side) +{/*<asyxml></code><documentation>Return the triangle formed by the points of tangency of the triangle referenced by 'side' with its excircles. + One vertex of the returned triangle is on the segment 'side'.</documentation></function></asyxml>*/ + triangle t = side.t; + transform p1 = projection((line)t.AB); + transform p2 = projection((line)t.AC); + transform p3 = projection((line)t.BC); + point EP = excenter(side); + return triangle(p3 * EP, p2 * EP, p1 * EP); +} + +/*<asyxml><function type="point" signature="bisectorpoint(side)"><code></asyxml>*/ +point bisectorpoint(side side) +{/*<asyxml></code><documentation>The intersection point of the angle bisector from the + opposite point of 'side' with the side 'side'.</documentation></function></asyxml>*/ + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) return trilinear(t, 1, 1, 0); + if(n == 2) return trilinear(t, 0, 1, 1); + return trilinear(t, 1, 0, 1); +} + +/*<asyxml><function type="line" signature="bisector(vertex,real)"><code></asyxml>*/ +line bisector(vertex V, real angle = 0) +{/*<asyxml></code><documentation>Return the interior bisector passing through 'V' rotated by angle (in degrees) + around 'V'.</documentation></function></asyxml>*/ + return rotate(angle, point(V)) * line(point(V), incenter(V.t)); +} + +/*<asyxml><function type="line" signature="bisector(side)"><code></asyxml>*/ +line bisector(side side) +{/*<asyxml></code><documentation>Return the bisector of the line segment 'side'.</documentation></function></asyxml>*/ + return bisector(segment(side)); +} + +/*<asyxml><function type="point" signature="intouch(side)"><code></asyxml>*/ +point intouch(side side) +{/*<asyxml></code><documentation>The point of tangency on the side 'side' of its incircle.</documentation></function></asyxml>*/ + triangle t = side.t; + real a = t.a(), b = t.b(), c = t.c(); + int n = numarray[abs(side.n) - 1]; + if(n == 1) return trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0); + if(n == 2) return trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c)); + return trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c)); +} + +/*<asyxml><function type="triangle" signature="intouch(triangle)"><code></asyxml>*/ +triangle intouch(triangle t) +{/*<asyxml></code><documentation>Return the intouch triangle of the triangle 't'. + The intouch triangle of 't' is the triangle formed by the points + of tangency of a triangle 't' with its incircles.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c)); + B = trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c)); + C = trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="tangential(triangle)"><code></asyxml>*/ +triangle tangential(triangle t) +{/*<asyxml></code><documentation>Return the tangential triangle of the triangle 't'. + The tangential triangle of 't' is the triangle formed by the lines + tangent to the circumcircle of the given triangle 't' at its vertices.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, -a, b, c); + B = trilinear(t, a, -b, c); + C = trilinear(t, a, b, -c); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="medial(triangle t)"><code></asyxml>*/ +triangle medial(triangle t) +{/*<asyxml></code><documentation>Return the triangle whose vertices are midpoints of the sides of 't'.</documentation></function></asyxml>*/ + return triangle(midpoint(t.BC), midpoint(t.AC), midpoint(t.AB)); +} + +/*<asyxml><function type="line" signature="median(vertex)"><code></asyxml>*/ +line median(vertex V) +{/*<asyxml></code><documentation>Return median from 'V'.</documentation></function></asyxml>*/ + return line(point(V), midpoint(segment(opposite(V)))); +} + +/*<asyxml><function type="line" signature="median(side)"><code></asyxml>*/ +line median(side side) +{/*<asyxml></code><documentation>Return median from the opposite vertex of 'side'.</documentation></function></asyxml>*/ + return median(opposite(side)); +} + +/*<asyxml><function type="triangle" signature="orthic(triangle)"><code></asyxml>*/ +triangle orthic(triangle t) +{/*<asyxml></code><documentation>Return the triangle whose vertices are endpoints of the altitudes from each of the vertices of 't'.</documentation></function></asyxml>*/ + return triangle(foot(t.BC), foot(t.AC), foot(t.AB)); +} + +/*<asyxml><function type="triangle" signature="symmedial(triangle)"><code></asyxml>*/ +triangle symmedial(triangle t) +{/*<asyxml></code><documentation>Return the symmedial triangle of 't'.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, b, c); + B = trilinear(t, a, 0, c); + C = trilinear(t, a, b, 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="anticomplementary(triangle)"><code></asyxml>*/ +triangle anticomplementary(triangle t) +{/*<asyxml></code><documentation>Return the triangle which has the given triangle 't' as its medial triangle.</documentation></function></asyxml>*/ + real a = t.a(), b = t.b(), c = t.c(); + real ab = a * b, bc = b * c, ca = c * a; + point A = trilinear(t, -bc, ca, ab); + point B = trilinear(t, bc, -ca, ab); + point C = trilinear(t, bc, ca, -ab); + return triangle(A, B, C); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,line,bool)"><code></asyxml>*/ +point[] intersectionpoints(triangle t, line l, bool extended = false) +{/*<asyxml></code><documentation>Return the intersection points. + If 'extended' is true, the sides are lines else the sides are segments. + intersectionpoints(line, triangle, bool) is also defined.</documentation></function></asyxml>*/ + point[] OP; + void addpoint(point P) + { + if(defined(P)) { + bool exist = false; + for (int i = 0; i < OP.length; ++i) { + if(P == OP[i]) {exist = true; break;} + } + if(!exist) OP.push(P); + } + } + if(extended) { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoint(t.line(i), l)); + } + } else { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoint((segment)t.line(i), l)); + } + } + return OP; +} + +point[] intersectionpoints(line l, triangle t, bool extended = false) +{ + return intersectionpoints(t, l, extended); +} + +/*<asyxml><function type="vector" signature="dir(vertex)"><code></asyxml>*/ +vector dir(vertex V) +{/*<asyxml></code><documentation>The direction (towards the outside of the triangle) of the interior angle bisector of 'V'.</documentation></function></asyxml>*/ + triangle t = V.t; + if(V.n == 1) return vector(defaultcoordsys, (-dir(t.A--t.B, t.A--t.C))); + if(V.n == 2) return vector(defaultcoordsys, (-dir(t.B--t.A, t.B--t.C))); + return vector(defaultcoordsys, (-dir(t.C--t.A, t.C--t.B))); +} + +/*<asyxml><function type="void" signature="lvoid label(picture,Label,vertex,pair,real,pen,filltype)"><code></asyxml>*/ +void label(picture pic = currentpicture, Label L, vertex V, + pair align = dir(V), + real alignFactor = 1, + pen p = nullpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw 'L' on picture 'pic' at vertex 'V' aligned by 'alignFactor * align'.</documentation></function></asyxml>*/ + label(pic, L, locate(point(V)), alignFactor * align, p, filltype); +} + +/*<asyxml><function type="void" signature="label(picture,Label,Label,Label,triangle,real,real,pen,filltype)"><code></asyxml>*/ +void label(picture pic = currentpicture, Label LA = "$A$", + Label LB = "$B$", Label LC = "$C$", + triangle t, + real alignAngle = 0, + real alignFactor = 1, + pen p = nullpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw labels LA, LB and LC aligned in the rotated (by 'alignAngle' in degrees) direction + (towards the outside of the triangle) of the interior angle bisector of vertices. + One can individually modify the alignment by setting the Label parameter 'align'.</documentation></function></asyxml>*/ + Label lla = LA.copy(); + lla.align(lla.align, rotate(alignAngle) * locate(dir(t.VA))); + label(pic, LA, t.VA, align = lla.align.dir, alignFactor = alignFactor, p, filltype); + Label llb = LB.copy(); + llb.align(llb.align, rotate(alignAngle) * locate(dir(t.VB))); + label(pic, llb, t.VB, align = llb.align.dir, alignFactor = alignFactor, p, filltype); + Label llc = LC.copy(); + llc.align(llc.align, rotate(alignAngle) * locate(dir(t.VC))); + label(pic, llc, t.VC, align = llc.align.dir, alignFactor = alignFactor, p, filltype); +} + +/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,Label,Label,Label,triangle,pen,filltype)"><code></asyxml>*/ +void show(picture pic = currentpicture, + Label LA = "$A$", Label LB = "$B$", Label LC = "$C$", + Label La = "$a$", Label Lb = "$b$", Label Lc = "$c$", + triangle t, pen p = currentpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw triangle and labels of sides and vertices.</documentation></function></asyxml>*/ + pair a = locate(t.A), b = locate(t.B), c = locate(t.C); + draw(pic, a--b--c--cycle, p); + label(pic, LA, a, -dir(a--b, a--c), p, filltype); + label(pic, LB, b, -dir(b--a, b--c), p, filltype); + label(pic, LC, c, -dir(c--a, c--b), p, filltype); + pair aligna = I * unit(c - b), alignb = I * unit(c - a), alignc = I * unit(b - a); + pair mAB = locate(midpoint(t.AB)), mAC = locate(midpoint(t.AC)), mBC = locate(midpoint(t.BC)); + label(pic, La, b--c, align = rotate(dot(a - mBC, aligna) > 0 ? 180 :0) * aligna, p); + label(pic, Lb, a--c, align = rotate(dot(b - mAC, alignb) > 0 ? 180 :0) * alignb, p); + label(pic, Lc, a--b, align = rotate(dot(c - mAB, alignc) > 0 ? 180 :0) * alignc, p); +} + +/*<asyxml><function type="void" signature="draw(picture,triangle,pen,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, triangle t, pen p = currentpen, marker marker = nomarker) +{/*<asyxml></code><documentation>Draw sides of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/ + draw(pic, t.Path(), p, marker); +} + +/*<asyxml><function type="void" signature="draw(picture,triangle[],pen,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, triangle[] t, pen p = currentpen, marker marker = nomarker) +{/*<asyxml></code><documentation>Draw sides of the triangles 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/ + for(int i = 0; i < t.length; ++i) draw(pic, t[i], p, marker); +} + +/*<asyxml><function type="void" signature="drawline(picture,triangle,pen)"><code></asyxml>*/ +void drawline(picture pic = currentpicture, triangle t, pen p = currentpen) +{/*<asyxml></code><documentation>Draw lines of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/ + draw(t, p); + draw(pic, line(t.A, t.B), p); + draw(pic, line(t.A, t.C), p); + draw(pic, line(t.B, t.C), p); +} + +/*<asyxml><function type="void" signature="dot(picture,triangle,pen)"><code></asyxml>*/ +void dot(picture pic = currentpicture, triangle t, pen p = currentpen) +{/*<asyxml></code><documentation>Draw a dot at each vertex of 't'.</documentation></function></asyxml>*/ + dot(pic, t.A^^t.B^^t.C, p); +} +// *.......................TRIANGLES.......................* +// *=======================================================* + +// *=======================================================* +// *.......................INVERSIONS......................* +/*<asyxml><function type="point" signature="inverse(real k,point,point)"><code></asyxml>*/ +point inverse(real k, point A, point M) +{/*<asyxml></code><documentation>Return the inverse point of 'M' with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/ + return A + k/conj(M - A); +} + +/*<asyxml><function type="point" signature="radicalcenter(circle,circle)"><code></asyxml>*/ +point radicalcenter(circle c1, circle c2) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/ + point[] P = standardizecoordsys(c1.C, c2.C); + real k = c1.r^2 - c2.r^2; + pair C1 = locate(c1.C); + pair C2 = locate(c2.C); + pair oop = C2 - C1; + pair K = (abs(oop) == 0) ? + (infinity, infinity) : + midpoint(C1--C2) + 0.5 * k * oop/dot(oop, oop); + return point(P[0].coordsys, K/P[0].coordsys); +} + +/*<asyxml><function type="line" signature="radicalline(circle,circle)"><code></asyxml>*/ +line radicalline(circle c1, circle c2) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/ + if (c1.C == c2.C) abort("radicalline: the centers must be distinct"); + return perpendicular(radicalcenter(c1, c2), line(c1.C, c2.C)); +} + +/*<asyxml><function type="point" signature="radicalcenter(circle,circle,circle)"><code></asyxml>*/ +point radicalcenter(circle c1, circle c2, circle c3) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/ + return intersectionpoint(radicalline(c1, c2), radicalline(c1, c3)); +} + +/*<asyxml><struct signature="inversion"><code></asyxml>*/ +struct inversion +{/*<asyxml></code><documentation>http://mathworld.wolfram.com/Inversion.html</documentation></asyxml>*/ + point C; + real k; +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/ +inversion inversion(real k, point C) +{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/ + inversion oi; + oi.k = k; + oi.C = C; + return oi; +} +/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/ +inversion inversion(point C, real k) +{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/ + return inversion(k, C); +} + +/*<asyxml><function type="inversion" signature="inversion(circle,circle)"><code></asyxml>*/ +inversion inversion(circle c1, circle c2, real sgn = 1) +{/*<asyxml></code><documentation>Return the inversion which transforms 'c1' to + . 'c2' and positive inversion radius if 'sgn > 0'; + . 'c2' and negative inversion radius if 'sgn < 0'; + . 'c1' and 'c2' to 'c2' if 'sgn = 0'.</documentation></function></asyxml>*/ + if(sgn == 0) { + point O = radicalcenter(c1, c2); + return inversion(O^c1, O); + } + real a = abs(c1.r/c2.r); + if(sgn > 0) { + point O = c1.C + a/abs(1 - a) * (c2.C - c1.C); + return inversion(a * abs(abs(O - c2.C)^2 - c2.r^2), O); + } + point O = c1.C + a/abs(1 + a) * (c2.C - c1.C); + return inversion(-a * abs(abs(O - c2.C)^2 - c2.r^2), O); +} + +/*<asyxml><function type="inversion" signature="inversion(circle,circle,circle)"><code></asyxml>*/ +inversion inversion(circle c1, circle c2, circle c3) +{/*<asyxml></code><documentation>Return the inversion which transform 'c1' to 'c1', 'c2' to 'c2' and 'c3' to 'c3'.</documentation></function></asyxml>*/ + point Rc = radicalcenter(c1, c2, c3); + return inversion(Rc, Rc^c1); +} + +circle operator cast(inversion i){return circle(i.C, sgn(i.k) * sqrt(abs(i.k)));} +/*<asyxml><function type="circle" signature="circle(inversion)"><code></asyxml>*/ +circle circle(inversion i) +{/*<asyxml></code><documentation>Return the inversion circle of 'i'.</documentation></function></asyxml>*/ + return i; +} + +inversion operator cast(circle c) +{ + return inversion(sgn(c.r) * c.r^2, c.C); +} +/*<asyxml><function type="inversion" signature="inversion(circle)"><code></asyxml>*/ +inversion inversion(circle c) +{/*<asyxml></code><documentation>Return the inversion represented by the circle of 'c'.</documentation></function></asyxml>*/ + return c; +} + +/*<asyxml><operator type = "point" signature="*(inversion,point)"><code></asyxml>*/ +point operator *(inversion i, point P) +{/*<asyxml></code><documentation>Provide inversion * point.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, P); +} + +void lineinversion() +{ + warning("lineinversion", "the inversion of the line is not a circle. +The returned circle has an infinite radius, circle.l has been set."); +} + + +/*<asyxml><function type="circle" signature="inverse(real,point,line)"><code></asyxml>*/ +circle inverse(real k, point A, line l) +{/*<asyxml></code><documentation>Return the inverse circle of 'l' with + respect to point 'A' and inversion radius 'k'.</documentation></function></asyxml>*/ + if(A @ l) { + lineinversion(); + circle C = circle(A, infinity); + C.l = l; + return C; + } + point Ap = inverse(k, A, l.A), Bp = inverse(k, A, l.B); + return circle(A, Ap, Bp); +} + +/*<asyxml><operator type = "circle" signature="*(inversion,line)"><code></asyxml>*/ +circle operator *(inversion i, line l) +{/*<asyxml></code><documentation>Provide inversion * line for lines that don't pass through the inversion center.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, l); +} + +/*<asyxml><function type="circle" signature="inverse(real,point,circle)"><code></asyxml>*/ +circle inverse(real k, point A, circle c) +{/*<asyxml></code><documentation>Return the inverse circle of 'c' with + respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/ + if(degenerate(c)) return inverse(k, A, c.l); + if(A @ c) { + lineinversion(); + point M = rotate(180, c.C) * A, Mp = rotate(90, c.C) * A; + circle oc = circle(A, infinity); + oc.l = line(inverse(k, A, M), inverse(k, A, Mp)); + return oc; + } + point[] P = standardizecoordsys(A, c.C); + real s = k/((P[1].x - P[0].x)^2 + (P[1].y - P[0].y)^2 - c.r^2); + return circle(P[0] + s * (P[1]-P[0]), abs(s) * c.r); +} + +/*<asyxml><operator type = "circle" signature="*(inversion,circle)"><code></asyxml>*/ +circle operator *(inversion i, circle c) +{/*<asyxml></code><documentation>Provide inversion * circle.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, c); +} +// *.......................INVERSIONS......................* +// *=======================================================* + +// *=======================================================* +// *........................FOOTER.........................* +/*<asyxml><function type="point[]" signature="intersectionpoints(line,circle)"><code></asyxml>*/ +point[] intersectionpoints(line l, circle c) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(circle, line) is also defined.</documentation></function></asyxml>*/ + if(degenerate(c)) return new point[]{intersectionpoint(l, c.l)}; + point[] op; + coordsys R = samecoordsys(l.A, c.C) ? + l.A.coordsys : defaultcoordsys; + coordsys Rp = defaultcoordsys; + circle cc = circle(changecoordsys(Rp, c.C), c.r); + point proj = projection(l) * c.C; + if(proj @ cc) { // The line is a tangente of the circle. + if(proj @ l) op.push(proj);// line may be a segement... + } else { + coordsys Rc = cartesiansystem(c.C, (1, 0), (0, 1)); + line ll = changecoordsys(Rc, l); + pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates, + 1, 0, 1, 0, 0, -c.r^2); + for (int i = 0; i < P.length; ++i) { + point inter = changecoordsys(R, point(Rc, P[i])); + if(inter @ l) op.push(inter); + } + } + return op; +} + +point[] intersectionpoints(circle c, line l) +{ + return intersectionpoints(l, c); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(line l, ellipse el) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(ellipse, line) is also defined.</documentation></function></asyxml>*/ + if(el.e == 0) return intersectionpoints(l, (circle)el); + if(degenerate(el)) return new point[]{intersectionpoint(l, el.l)}; + point[] op; + coordsys R = samecoordsys(l.A, el.C) ? l.A.coordsys : defaultcoordsys; + coordsys Rp = defaultcoordsys; + line ll = changecoordsys(Rp, l); + ellipse ell = (ellipse) 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 + // Definition of the tangent at the antipodal point on the circle. + // '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 = 0.5*(A + B); + bool tgt = Finite(M) ? M @ h : false; + 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(bqe,bqe)"><code></asyxml>*/ +point[] intersectionpoints(bqe bqe1, bqe bqe2) +{/*<asyxml></code><documentation>Return the intersection of the two conic sections whose equations are 'bqe1' and 'bqe2'.</documentation></function></asyxml>*/ + coordsys R=canonicalcartesiansystem(conic(bqe1)); + real[] a=changecoordsys(R,bqe1).a; + real[] b=changecoordsys(R,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[] {a[0]*a[2]*(-2*b[0]*b[2]+b[1]^2)+a[0]^2*b[2]^2+a[2]^2*b[0]^2, + + 2*a[0]*a[2]*b[1]*b[4]-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+2*a[2]^2*b[0]*b[3], + + a[2]*a[5]*b[1]^2-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[0]*a[2]*b[2]*b[5]+a[2]^2*b[3]^2+2*a[2]*a[3]*b[1]*b[4] + +a[0]*a[2]*b[4]^2, + + a[2]*a[3]*b[4]^2+2*a[2]^2*b[3]*b[5]-2*a[2]*a[3]*b[2]*b[5] + -2*a[2]*a[5]*b[2]*b[3]+2*a[2]*a[5]*b[1]*b[4], + + -2*a[2]*a[5]*b[2]*b[5]+a[5]^2*b[2]^2+a[2]*a[5]*b[4]^2 + +a[2]^2*b[5]^2}; + x=realquarticroots(c[0],c[1],c[2],c[3],c[4]); + } else { + if(abs(b[4]) > e) { + real D=b[4]^2; + c=new real[] {(a[0]*b[4]^2+a[2]*b[3]^2+ + (-2*a[2]*a[3])*b[3]+a[2]*a[3]^2)/D, + -((-2*a[2]*b[3]+2*a[2]*a[3])*b[5]-a[3]*b[4]^2+ + (2*a[2]*a[5])*b[3])/D,a[2]*(a[5]-b[5])^2/D+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],0,a[0]*(a[5]-b[5])^2/D^2-a[3]*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[3],a[2]*y[i]^2+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(changecoordsys(currentcoordsys,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],0,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(changecoordsys(currentcoordsys,point(R,(x[i],y[j])))); + } + } + return P; +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(conic,conic)"><code></asyxml>*/ +point[] intersectionpoints(conic co1, conic co2) +{/*<asyxml></code><documentation>Return the intersection points of the two conics.</documentation></function></asyxml>*/ + if(degenerate(co1)) return intersectionpoints(co1.l[0], co2); + if(degenerate(co2)) return intersectionpoints(co1, co2.l[0]); + return intersectionpoints(equation(co1), equation(co2)); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,conic,bool)"><code></asyxml>*/ +point[] intersectionpoints(triangle t, conic co, bool extended = false) +{/*<asyxml></code><documentation>Return the intersection points. + If 'extended' is true, the sides are lines else the sides are segments. + intersectionpoints(conic, triangle, bool) is also defined.</documentation></function></asyxml>*/ + if(degenerate(co)) return intersectionpoints(t, co.l[0], extended); + point[] OP; + void addpoint(point P[]) + { + for (int i = 0; i < P.length; ++i) { + if(defined(P[i])) { + bool exist = false; + for (int j = 0; j < OP.length; ++j) { + if(P[i] == OP[j]) {exist = true; break;} + } + if(!exist) OP.push(P[i]); + }}} + if(extended) { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoints(t.line(i), co)); + } + } else { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoints((segment)t.line(i), co)); + } + } + return OP; +} + +point[] intersectionpoints(conic co, triangle t, bool extended = false) +{ + return intersectionpoints(t, co, extended); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + // if(degenerate(b)) return intersectionpoints(a, b.l);; + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,circle)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, circle b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + // if(degenerate(b)) return intersectionpoints(a, b.l);; + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(circle a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints(b, a); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,parabola)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints(b, a); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints(b, a); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,parabola)"><code></asyxml>*/ +point[] intersectionpoints(circle a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,circle)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, circle b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(circle a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,circle)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, circle b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,parabola)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,parabola)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,circle)"><code></asyxml>*/ +point[] intersectionpoints(circle c1, circle c2) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + if(degenerate(c1)) + return degenerate(c2) ? + new point[]{intersectionpoint(c1.l, c2.l)} : intersectionpoints(c1.l, c2); + if(degenerate(c2)) return intersectionpoints(c1, c2.l); + return (c1.C == c2.C) ? + new point[] : + intersectionpoints(radicalline(c1, c2), c1); +} + +/*<asyxml><function type="line" signature="tangent(circle,abscissa)"><code></asyxml>*/ +line tangent(circle c, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'c' at 'point(c, x)'.</documentation></function></asyxml>*/ + if(c.r == 0) abort("tangent: a circle with a radius equals zero has no tangent."); + point M = point(c, x); + return line(rotate(90, M) * c.C, M); +} + +/*<asyxml><function type="line[]" signature="tangents(circle,point)"><code></asyxml>*/ +line[] tangents(circle c, point M) +{/*<asyxml></code><documentation>Return the tangents of 'c' passing through 'M'.</documentation></function></asyxml>*/ + line[] ol; + if(inside(c, M)) return ol; + if(M @ c) { + ol.push(tangent(c, relabscissa(c, M))); + } else { + circle cc = circle(c.C, M); + point[] inter = intersectionpoints(c, cc); + for (int i = 0; i < inter.length; ++i) + ol.push(tangents(c, inter[i])[0]); + } + return ol; +} + +/*<asyxml><function type="point" signature="point(circle,point)"><code></asyxml>*/ +point point(circle c, point M) +{/*<asyxml></code><documentation>Return the intersection point of 'c' + with the half-line '[c.C M)'.</documentation></function></asyxml>*/ + return intersectionpoints(c, line(c.C, false, M))[0]; +} + +/*<asyxml><function type="line" signature="tangent(circle,point)"><code></asyxml>*/ +line tangent(circle c, point M) +{/*<asyxml></code><documentation>Return the tangent of 'c' at the + intersection point of the half-line'[c.C M)'.</documentation></function></asyxml>*/ + return tangents(c, point(c, M))[0]; +} + +/*<asyxml><function type="point" signature="point(circle,explicit vector)"><code></asyxml>*/ +point point(circle c, explicit vector v) +{/*<asyxml></code><documentation>Return the intersection point of 'c' + with the half-line '[c.C v)'.</documentation></function></asyxml>*/ + return point(c, c.C + v); +} + +/*<asyxml><function type="line" signature="tangent(circle,explicit vector)"><code></asyxml>*/ +line tangent(circle c, explicit vector v) +{/*<asyxml></code><documentation>Return the tangent of 'c' at the + point M so that vec(c.C M) is collinear to 'v' with the same sense.</documentation></function></asyxml>*/ + line ol = tangent(c, c.C + v); + return dot(ol.v, v) > 0 ? ol : reverse(ol); +} + +/*<asyxml><function type="line" signature="tangent(ellipse,abscissa)"><code></asyxml>*/ +line tangent(ellipse el, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'el' at 'point(el, x)'.</documentation></function></asyxml>*/ + point M = point(el, x); + line l1 = line(el.F1, M); + line l2 = line(el.F2, M); + line ol = (l1 == l2) ? perpendicular(M, l1) : bisector(l1, l2, 90, false); + return ol; +} + +/*<asyxml><function type="line[]" signature="tangents(ellipse,point)"><code></asyxml>*/ +line[] tangents(ellipse el, point M) +{/*<asyxml></code><documentation>Return the tangents of 'el' passing through 'M'.</documentation></function></asyxml>*/ + line[] ol; + if(inside(el, M)) return ol; + if(M @ el) { + ol.push(tangent(el, relabscissa(el, M))); + } else { + point Mp = samecoordsys(M, el.F2) ? + M : changecoordsys(el.F2.coordsys, M); + circle c = circle(Mp, abs(el.F1 - Mp)); + circle cc = circle(el.F2, 2 * el.a); + point[] inter = intersectionpoints(c, cc); + for (int i = 0; i < inter.length; ++i) { + line tl = line(inter[i], el.F2, false); + point[] P = intersectionpoints(tl, el); + ol.push(line(Mp, P[0])); + } + } + return ol; +} + +/*<asyxml><function type="line" signature="tangent(parabola,abscissa)"><code></asyxml>*/ +line tangent(parabola p, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'p' at 'point(p, x)' (use the Wells method).</documentation></function></asyxml>*/ + line lt = rotate(90, p.V) * line(p.V, p.F); + point P = point(p, x); + if(P == p.V) return lt; + point M = midpoint(segment(P, p.F)); + line l = rotate(90, M) * line(P, p.F); + return line(P, projection(lt) * M); +} + +/*<asyxml><function type="line[]" signature="tangents(parabola,point)"><code></asyxml>*/ +line[] tangents(parabola p, point M) +{/*<asyxml></code><documentation>Return the tangent of 'p' at 'M' (use the Wells method).</documentation></function></asyxml>*/ + line[] ol; + if(inside(p, M)) return ol; + if(M @ p) { + ol.push(tangent(p, angabscissa(p, M))); + } + else { + point Mt = changecoordsys(coordsys(p), M); + circle c = circle(Mt, p.F); + line l = rotate(90, p.V) * line(p.V, p.F); + point[] R = intersectionpoints(l, c); + for (int i = 0; i < R.length; ++i) { + ol.push(line(Mt, R[i])); + } + // An other method: http://www.du.edu/~jcalvert/math/parabola.htm + // point[] R = intersectionpoints(p.directrix, c); + // for (int i = 0; i < R.length; ++i) { + // ol.push(bisector(segment(p.F, R[i]))); + // } + } + return ol; +} + +/*<asyxml><function type="line" signature="tangent(hyperbola,abscissa)"><code></asyxml>*/ +line tangent(hyperbola h, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'h' at 'point(p, x)'.</documentation></function></asyxml>*/ + point M = point(h, x); + line ol = bisector(line(M, h.F1), line(M, h.F2)); + if(sameside(h.F1, h.F2, ol) || ol == line(h.F1, h.F2)) ol = rotate(90, M) * ol; + return ol; +} + +/*<asyxml><function type="line[]" signature="tangents(hyperbola,point)"><code></asyxml>*/ +line[] tangents(hyperbola h, point M) +{/*<asyxml></code><documentation>Return the tangent of 'h' at 'M'.</documentation></function></asyxml>*/ + line[] ol; + if(M @ h) { + ol.push(tangent(h, angabscissa(h, M, fromCenter))); + } else { + coordsys cano = canonicalcartesiansystem(h); + bqe bqe = changecoordsys(cano, equation(h)); + real a = abs(1/(bqe.a[5] * bqe.a[0])), b = abs(1/(bqe.a[5] * bqe.a[2])); + point Mp = changecoordsys(cano, M); + real x0 = Mp.x, y0 = Mp.y; + if(abs(x0) > epsgeo) { + real c0 = a * y0^2/(b * x0)^2 - 1/b, + c1 = 2 * a * y0/(b * x0^2), c2 = a/x0^2 - 1; + real[] sol = quadraticroots(c0, c1, c2); + for (real y:sol) { + point tmp = changecoordsys(coordsys(h), point(cano, (a * (1 + y * y0/b)/x0, y))); + ol.push(line(M, tmp)); + } + } else if(abs(y0) > epsgeo) { + real y = -b/y0, x = sqrt(a * (1 + b/y0^2)); + ol.push(line(M, changecoordsys(coordsys(h), point(cano, (x, y))))); + ol.push(line(M, changecoordsys(coordsys(h), point(cano, (-x, y))))); + }} + return ol; +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(conic,arc)"><code></asyxml>*/ +point[] intersectionpoints(conic co, arc a) +{/*<asyxml></code><documentation>intersectionpoints(arc, circle) is also defined.</documentation></function></asyxml>*/ + point[] op; + point[] tp = intersectionpoints(co, (conic)a.el); + for (int i = 0; i < tp.length; ++i) + if(tp[i] @ a) op.push(tp[i]); + return op; +} + +point[] intersectionpoints(arc a, conic co) +{ + return intersectionpoints(co, a); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(arc,arc)"><code></asyxml>*/ +point[] intersectionpoints(arc a1, arc a2) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + point[] op; + point[] tp = intersectionpoints(a1.el, a2.el); + for (int i = 0; i < tp.length; ++i) + if(tp[i] @ a1 && tp[i] @ a2) op.push(tp[i]); + return op; +} + + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,arc)"><code></asyxml>*/ +point[] intersectionpoints(line l, arc a) +{/*<asyxml></code><documentation>intersectionpoints(arc, line) is also defined.</documentation></function></asyxml>*/ + point[] op; + point[] tp = intersectionpoints(a.el, l); + for (int i = 0; i < tp.length; ++i) + if(tp[i] @ a && tp[i] @ l) op.push(tp[i]); + return op; +} + +point[] intersectionpoints(arc a, line l) +{ + return intersectionpoints(l, a); +} + +/*<asyxml><function type="point" signature="arcsubtendedcenter(point,point,real)"><code></asyxml>*/ +point arcsubtendedcenter(point A, point B, real angle) +{/*<asyxml></code><documentation>Return the center of the arc retuned + by the 'arcsubtended' routine.</documentation></function></asyxml>*/ + point OM; + point[] P = standardizecoordsys(A, B); + angle = angle%(sgnd(angle) * 180); + line bis = bisector(P[0], P[1]); + line AB = line(P[0], P[1]); + return intersectionpoint(bis, rotate(90 - angle, A) * AB); +} + +/*<asyxml><function type="arc" signature="arcsubtended(point,point,real)"><code></asyxml>*/ +arc arcsubtended(point A, point B, real angle) +{/*<asyxml></code><documentation>Return the arc circle from which the segment AB is saw with + the angle 'angle'. + If the point 'M' is on this arc, the oriented angle (MA, MB) is + equal to 'angle'.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B); + line AB = line(P[0], P[1]); + angle = angle%(sgnd(angle) * 180); + point C = arcsubtendedcenter(P[0], P[1], angle); + real BC = degrees(B - C)%360; + real AC = degrees(A - C)%360; + return arc(circle(C, abs(B - C)), BC, AC, angle > 0 ? CCW : CW); +} + +/*<asyxml><function type="arc" signature="arccircle(point,point,point)"><code></asyxml>*/ +arc arccircle(point A, point M, point B) +{/*<asyxml></code><documentation>Return the CCW arc circle 'AB' passing through 'M'.</documentation></function></asyxml>*/ + circle tc = circle(A, M, B); + real a = degrees(A - tc.C); + real b = degrees(B - tc.C); + real m = degrees(M - tc.C); + + arc oa = arc(tc, a, b); + // TODO: use cross product to determine CWW or CW + if (!(M @ oa)) { + oa.direction = !oa.direction; + } + + return oa; +} + +/*<asyxml><function type="arc" signature="arc(ellipse,abscissa,abscissa,bool)"><code></asyxml>*/ +arc arc(ellipse el, explicit abscissa x1, explicit abscissa x2, bool direction = CCW) +{/*<asyxml></code><documentation>Return the arc from 'point(c, x1)' to 'point(c, x2)' in the direction 'direction'.</documentation></function></asyxml>*/ + real a = degrees(point(el, x1) - el.C); + real b = degrees(point(el, x2) - el.C); + arc oa = arc(el, a - el.angle, b - el.angle, fromCenter, direction); + return oa; +} + +/*<asyxml><function type="arc" signature="arc(ellipse,point,point,bool)"><code></asyxml>*/ +arc arc(ellipse el, point M, point N, bool direction = CCW) +{/*<asyxml></code><documentation>Return the arc from 'M' to 'N' in the direction 'direction'. + The points 'M' and 'N' must belong to the ellipse 'el'.</documentation></function></asyxml>*/ + return arc(el, relabscissa(el, M), relabscissa(el, N), direction); +} + +/*<asyxml><function type="arc" signature="arccircle(point,point,real,bool)"><code></asyxml>*/ +arc arccircle(point A, point B, real angle, bool direction = CCW) +{/*<asyxml></code><documentation>Return the arc circle centered on A + from B to rotate(angle, A) * B in the direction 'direction'.</documentation></function></asyxml>*/ + point M = rotate(angle, A) * B; + return arc(circle(A, abs(A - B)), B, M, direction); +} + +/*<asyxml><function type="arc" signature="arc(explicit arc,abscissa,abscissa)"><code></asyxml>*/ +arc arc(explicit arc a, abscissa x1, abscissa x2) +{/*<asyxml></code><documentation>Return the arc from 'point(a, x1)' to 'point(a, x2)' traversed in the direction of the arc direction.</documentation></function></asyxml>*/ + real a1 = angabscissa(a.el, point(a, x1), a.polarconicroutine).x; + real a2 = angabscissa(a.el, point(a, x2), a.polarconicroutine).x; + return arc(a.el, a1, a2, a.polarconicroutine, a.direction); +} + +/*<asyxml><function type="arc" signature="arc(explicit arc,point,point)"><code></asyxml>*/ +arc arc(explicit arc a, point M, point N) +{/*<asyxml></code><documentation>Return the arc from 'M' to 'N'. + The points 'M' and 'N' must belong to the arc 'a'.</documentation></function></asyxml>*/ + return arc(a, relabscissa(a, M), relabscissa(a, N)); +} + +/*<asyxml><function type="arc" signature="inverse(real,point,segment)"><code></asyxml>*/ +arc inverse(real k, point A, segment s) +{/*<asyxml></code><documentation>Return the inverse arc circle of 's' + with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/ + point Ap = inverse(k, A, s.A), Bp = inverse(k, A, s.B), + M = inverse(k, A, midpoint(s)); + return arccircle(Ap, M, Bp); +} + +/*<asyxml><operator type = "arc" signature="*(inversion,segment)"><code></asyxml>*/ +arc operator *(inversion i, segment s) +{/*<asyxml></code><documentation>Provide + inversion * segment.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, s); +} + +/*<asyxml><operator type = "path" signature="*(inversion,triangle)"><code></asyxml>*/ +path operator *(inversion i, triangle t) +{/*<asyxml></code><documentation>Provide inversion * triangle.</documentation></operator></asyxml>*/ + return (path)(i * segment(t.AB))-- + (path)(i * segment(t.BC))-- + (path)(i * segment(t.CA))&cycle; +} + +/*<asyxml><function type="path" signature="compassmark(pair,pair,real,real)"><code></asyxml>*/ +path compassmark(pair O, pair A, real position, real angle = 10) +{/*<asyxml></code><documentation>Return an arc centered on O with the angle 'angle' so that the position + of 'A' on this arc makes an angle 'position * angle'.</documentation></function></asyxml>*/ + real a = degrees(A - O); + real pa = (a - position * angle)%360, + pb = (a - (position - 1) * angle)%360; + real t1 = intersect(unitcircle, (0, 0)--2 * dir(pa))[0]; + real t2 = intersect(unitcircle, (0, 0)--2 * dir(pb))[0]; + int n = length(unitcircle); + if(t1 >= t2) t1 -= n; + return shift(O) * scale(abs(O - A)) * subpath(unitcircle, t1, t2); +} + +/*<asyxml><function type="line" signature="tangent(explicit arc,abscissa)"><code></asyxml>*/ +line tangent(explicit arc a, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'a' at 'point(a, x)'.</documentation></function></asyxml>*/ + abscissa ag = angabscissa(a, point(a, x)); + return tangent(a.el, ag + a.angle1 + (a.el.e == 0 ? a.angle0 : 0)); +} + +/*<asyxml><function type="line" signature="tangent(explicit arc,point)"><code></asyxml>*/ +line tangent(explicit arc a, point M) +{/*<asyxml></code><documentation>Return the tangent of 'a' at 'M'. + The points 'M' must belong to the arc 'a'.</documentation></function></asyxml>*/ + return tangent(a, angabscissa(a, M)); +} + +// *=======================================================* +// *.......Routines for compatibility with original geometry module........* + +path square(pair z1, pair z2) +{ + pair v = z2 - z1; + pair z3 = z2 + I * v; + pair z4 = z3 - v; + return z1--z2--z3--z4--cycle; +} + +// Draw a perpendicular symbol at z aligned in the direction align +// relative to the path z--z + dir. +void perpendicular(picture pic = currentpicture, pair z, pair align, + pair dir = E, real size = 0, pen p = currentpen, + margin margin = NoMargin, filltype filltype = NoFill) +{ + perpendicularmark(pic, (point) z, align, dir, size, p, margin, filltype); +} + + +// Draw a perpendicular symbol at z aligned in the direction align +// relative to the path z--z + dir(g, 0) +void perpendicular(picture pic = currentpicture, pair z, pair align, path g, + real size = 0, pen p = currentpen, margin margin = NoMargin, + filltype filltype = NoFill) +{ + perpendicularmark(pic, (point) z, align, dir(g, 0), size, p, margin, filltype); +} + +// Return an interior arc BAC of triangle ABC, given a radius r > 0. +// If r < 0, return the corresponding exterior arc of radius |r|. +path arc(explicit pair B, explicit pair A, explicit pair C, real r) +{ + real BA = degrees(B - A); + real CA = degrees(C - A); + return arc(A, abs(r), BA, CA, (r < 0) ^ ((BA-CA) % 360 < 180) ? CW : CCW); +} + +// *.......End of compatibility routines........* +// *=======================================================* + +// *........................FOOTER.........................* +// *=======================================================* + diff --git a/Build/source/utils/asymptote/base/graph.asy b/Build/source/utils/asymptote/base/graph.asy new file mode 100644 index 00000000000..46a5d7d0647 --- /dev/null +++ b/Build/source/utils/asymptote/base/graph.asy @@ -0,0 +1,2243 @@ +private import math; +import graph_splinetype; +import graph_settings; + +scaleT Linear; + +scaleT Log=scaleT(log10,pow10,logarithmic=true); +scaleT Logarithmic=Log; + +string baselinetemplate="$10^4$"; + +// A linear scale, with optional autoscaling of minimum and maximum values, +// scaling factor s and intercept. +scaleT Linear(bool automin=false, bool automax=automin, real s=1, + real intercept=0) +{ + real sinv=1/s; + scalefcn T,Tinv; + if(s == 1 && intercept == 0) + T=Tinv=identity; + else { + T=new real(real x) {return (x-intercept)*s;}; + Tinv=new real(real x) {return x*sinv+intercept;}; + } + return scaleT(T,Tinv,logarithmic=false,automin,automax); +} + +// A logarithmic scale, with optional autoscaling of minimum and maximum +// values. +scaleT Log(bool automin=false, bool automax=automin) +{ + return scaleT(Log.T,Log.Tinv,logarithmic=true,automin,automax); +} + +// A "broken" linear axis omitting the segment [a,b]. +scaleT Broken(real a, real b, bool automin=false, bool automax=automin) +{ + real skip=b-a; + real T(real x) { + if(x <= a) return x; + if(x <= b) return a; + return x-skip; + } + real Tinv(real x) { + if(x <= a) return x; + return x+skip; + } + return scaleT(T,Tinv,logarithmic=false,automin,automax); +} + +// A "broken" logarithmic axis omitting the segment [a,b], where a and b are +// automatically rounded to the nearest integral power of the base. +scaleT BrokenLog(real a, real b, bool automin=false, bool automax=automin) +{ + real A=round(Log.T(a)); + real B=round(Log.T(b)); + a=Log.Tinv(A); + b=Log.Tinv(B); + real skip=B-A; + real T(real x) { + if(x <= a) return Log.T(x); + if(x <= b) return A; + return Log.T(x)-skip; + } + real Tinv(real x) { + real X=Log.Tinv(x); + if(X <= a) return X; + return Log.Tinv(x+skip); + } + return scaleT(T,Tinv,logarithmic=true,automin,automax); +} + +Label Break=Label("$\approx$",UnFill(0.2mm)); + +void scale(picture pic=currentpicture, scaleT x, scaleT y=x, scaleT z=y) +{ + pic.scale.x.scale=x; + pic.scale.y.scale=y; + pic.scale.z.scale=z; + pic.scale.x.automin=x.automin; + pic.scale.y.automin=y.automin; + pic.scale.z.automin=z.automin; + pic.scale.x.automax=x.automax; + pic.scale.y.automax=y.automax; + pic.scale.z.automax=z.automax; +} + +void scale(picture pic=currentpicture, bool xautoscale=false, + bool yautoscale=xautoscale, bool zautoscale=yautoscale) +{ + scale(pic,Linear(xautoscale,xautoscale),Linear(yautoscale,yautoscale), + Linear(zautoscale,zautoscale)); +} + +struct scientific +{ + int sign; + real mantissa; + int exponent; + int ceil() {return sign*ceil(mantissa);} + real scale(real x, real exp) { + static real max=0.1*realMax; + static real limit=-log10(max); + return x*(exp > limit ? 10^-exp : max); + } + real ceil(real x, real exp) {return ceil(sign*scale(abs(x),exp));} + real floor(real x, real exp) {return floor(sign*scale(abs(x),exp));} +} + +// Convert x to scientific notation +scientific scientific(real x) +{ + scientific s; + s.sign=sgn(x); + x=abs(x); + if(x == 0) {s.mantissa=0; s.exponent=-intMax; return s;} + real logx=log10(x); + s.exponent=floor(logx); + s.mantissa=s.scale(x,s.exponent); + return s; +} + +// Autoscale limits and tick divisor. +struct bounds { + real min; + real max; + + // Possible tick intervals: + int[] divisor; + + void operator init(real min, real max, int[] divisor=new int[]) { + this.min=min; + this.max=max; + this.divisor=divisor; + } +} + +// Compute tick divisors. +int[] divisors(int a, int b) +{ + int[] dlist; + int n=b-a; + dlist[0]=1; + if(n == 1) {dlist[1]=10; dlist[2]=100; return dlist;} + if(n == 2) {dlist[1]=2; return dlist;} + int sqrtn=floor(sqrt(n)); + int i=0; + for(int d=2; d <= sqrtn; ++d) + if(n % d == 0 && (a*b >= 0 || b % (n/d) == 0)) dlist[++i]=d; + for(int d=sqrtn; d >= 1; --d) + if(n % d == 0 && (a*b >= 0 || b % d == 0)) dlist[++i]=quotient(n,d); + return dlist; +} + +real upscale(real b, real a) +{ + if(b <= 5) b=5; + else if (b > 10 && a >= 0 && b <= 12) b=12; + else if (b > 10 && (a >= 0 || 15 % -a == 0) && b <= 15) b=15; + else b=ceil(b/10)*10; + return b; +} + +// Compute autoscale limits and tick divisor. +bounds autoscale(real Min, real Max, scaleT scale=Linear) +{ + bounds m; + if(scale.logarithmic) { + m.min=floor(Min); + m.max=ceil(Max); + return m; + } + if(!(finite(Min) && finite(Max))) + abort("autoscale requires finite limits"); + Min=scale.Tinv(Min); + Max=scale.Tinv(Max); + m.min=Min; + m.max=Max; + if(Min > Max) {real temp=Min; Min=Max; Max=temp;} + if(Min == Max) { + if(Min == 0) {m.max=1; return m;} + if(Min > 0) {Min=0; Max *= 2;} + else {Min *= 2; Max=0;} + } + + int sign; + if(Min < 0 && Max <= 0) {real temp=-Min; Min=-Max; Max=temp; sign=-1;} + else sign=1; + scientific sa=scientific(Min); + scientific sb=scientific(Max); + int exp=max(sa.exponent,sb.exponent); + real a=sa.floor(Min,exp); + real b=sb.ceil(Max,exp); + + void zoom() { + --exp; + a=sa.floor(Min,exp); + b=sb.ceil(Max,exp); + } + + if(sb.mantissa <= 1.5) + zoom(); + + while((b-a)*10.0^exp > 10*(Max-Min)) + zoom(); + + real bsave=b; + if(b-a > (a >= 0 ? 8 : 6)) { + b=upscale(b,a); + if(a >= 0) { + if(a <= 5) a=0; else a=floor(a/10)*10; + } else a=-upscale(-a,-1); + } + + // Redo b in case the value of a has changed + if(bsave-a > (a >= 0 ? 8 : 6)) + b=upscale(bsave,a); + + if(sign == -1) {real temp=-a; a=-b; b=temp;} + real Scale=10.0^exp; + m.min=scale.T(a*Scale); + m.max=scale.T(b*Scale); + if(m.min > m.max) {real temp=m.min; m.min=m.max; m.max=temp;} + m.divisor=divisors(round(a),round(b)); + return m; +} + +typedef string ticklabel(real); + +ticklabel Format(string s=defaultformat) +{ + return new string(real x) {return format(s,x);}; +} + +ticklabel OmitFormat(string s=defaultformat ... real[] x) +{ + return new string(real v) { + string V=format(s,v); + for(real a : x) + if(format(s,a) == V) return ""; + return V; + }; +} + +string trailingzero="$%#$"; +string signedtrailingzero="$%+#$"; + +ticklabel DefaultFormat=Format(); +ticklabel NoZeroFormat=OmitFormat(0); + +// Format tick values as integral powers of base; otherwise with DefaultFormat. +ticklabel DefaultLogFormat(int base) { + return new string(real x) { + string exponent=format("%.4f",log(x)/log(base)); + return find(exponent,".") == -1 ? + "$"+(string) base+"^{"+exponent+"}$" : format(x); + }; +} + +// Format all tick values as powers of base. +ticklabel LogFormat(int base) { + return new string(real x) { + return format("$"+(string) base+"^{%g}$",log(x)/log(base)); + }; +} + +ticklabel LogFormat=LogFormat(10); +ticklabel DefaultLogFormat=DefaultLogFormat(10); + +// The default direction specifier. +pair zero(real) {return 0;} + +struct ticklocate { + real a,b; // Tick values at point(g,0), point(g,length(g)). + autoscaleT S; // Autoscaling transformation. + pair dir(real t); // Absolute 2D tick direction. + triple dir3(real t); // Absolute 3D tick direction. + real time(real v); // Returns the time corresponding to the value v. + ticklocate copy() { + ticklocate T=new ticklocate; + T.a=a; + T.b=b; + T.S=S.copy(); + T.dir=dir; + T.dir3=dir3; + T.time=time; + return T; + } +} + +autoscaleT defaultS; + +typedef real valuetime(real); + +valuetime linear(scalefcn S=identity, real Min, real Max) +{ + real factor=Max == Min ? 0.0 : 1.0/(Max-Min); + return new real(real v) {return (S(v)-Min)*factor;}; +} + +ticklocate ticklocate(real a, real b, autoscaleT S=defaultS, + real tickmin=-infinity, real tickmax=infinity, + real time(real)=null, pair dir(real)=zero) +{ + if((valuetime) time == null) time=linear(S.T(),a,b); + ticklocate locate; + locate.a=a; + locate.b=b; + locate.S=S.copy(); + if(finite(tickmin)) locate.S.tickMin=tickmin; + if(finite(tickmax)) locate.S.tickMax=tickmax; + locate.time=time; + locate.dir=dir; + return locate; +} + +private struct locateT { + real t; // tick location time + pair Z; // tick location in frame coordinates + pair pathdir; // path direction in frame coordinates + pair dir; // tick direction in frame coordinates + + void dir(transform T, path g, ticklocate locate, real t) { + pathdir=unit(shiftless(T)*dir(g,t)); + pair Dir=locate.dir(t); + dir=Dir == 0 ? -I*pathdir : unit(Dir); + } + // Locate the desired position of a tick along a path. + void calc(transform T, path g, ticklocate locate, real val) { + t=locate.time(val); + Z=T*point(g,t); + dir(T,g,locate,t); + } +} + +pair ticklabelshift(pair align, pen p=currentpen) +{ + return 0.25*unit(align)*labelmargin(p); +} + +void drawtick(frame f, transform T, path g, path g2, ticklocate locate, + real val, real Size, int sign, pen p, bool extend) +{ + locateT locate1,locate2; + locate1.calc(T,g,locate,val); + if(extend && size(g2) > 0) { + locate2.calc(T,g2,locate,val); + draw(f,locate1.Z--locate2.Z,p); + } else + if(sign == 0) + draw(f,locate1.Z-Size*locate1.dir--locate1.Z+Size*locate1.dir,p); + else + draw(f,locate1.Z--locate1.Z+Size*sign*locate1.dir,p); +} + +real zerotickfuzz=10*epsilon; + +// Label a tick on a frame. +pair labeltick(frame d, transform T, path g, ticklocate locate, real val, + pair side, int sign, real Size, ticklabel ticklabel, + Label F, real norm=0) +{ + locateT locate1; + locate1.calc(T,g,locate,val); + pair align=side*locate1.dir; + pair perp=I*locate1.pathdir; + + // Adjust tick label alignment + pair adjust=unit(align+0.75perp*sgn(dot(align,perp))); + // Project align onto adjusted direction. + align=adjust*dot(align,adjust); + pair shift=dot(align,-sign*locate1.dir) <= 0 ? align*Size : + ticklabelshift(align,F.p); + + real label; + if(locate.S.scale.logarithmic) + label=locate.S.scale.Tinv(val); + else { + label=val; + if(abs(label) < zerotickfuzz*norm) label=0; + // Fix epsilon errors at +/-1e-4 + // default format changes to scientific notation here + if(abs(abs(label)-1e-4) < epsilon) label=sgn(label)*1e-4; + } + + string s=ticklabel(label); + if(s != "") + label(d,F.T*baseline(s,baselinetemplate),locate1.Z+shift,align,F.p, + F.filltype); + return locate1.pathdir; +} + +// Add axis label L to frame f. +void labelaxis(frame f, transform T, Label L, path g, + ticklocate locate=null, int sign=1, bool ticklabels=false) +{ + Label L0=L.copy(); + real t=L0.relative(g); + pair z=point(g,t); + pair dir=dir(g,t); + pair perp=I*dir; + if(locate != null) { + locateT locate1; + locate1.dir(T,g,locate,t); + L0.align(L0.align,unit(-sgn(dot(sign*locate1.dir,perp))*perp)); + } + pair align=L0.align.dir; + if(L0.align.relative) align *= -perp; + pair alignperp=dot(align,perp)*perp; + pair offset; + if(ticklabels) { + if(piecewisestraight(g)) { + real angle=degrees(dir,warn=false); + transform S=rotate(-angle,z); + frame F=S*f; + pair Align=rotate(-angle)*alignperp; + offset=unit(alignperp-sign*locate.dir(t))* + abs((Align.y >= 0 ? max(F).y : (Align.y < 0 ? min(F).y : 0))-z.y); + } + z += offset; + } + + L0.align(align); + L0.position(z); + frame d; + add(d,L0); + pair width=0.5*size(d); + int n=length(g); + real t=L.relative(); + pair s=realmult(width,dir(g,t)); + if(t <= 0) { + if(L.align.default) s *= -axislabelfactor; + d=shift(s)*d; + } else if(t >= n) { + if(L.align.default) s *= -axislabelfactor; + d=shift(-s)*d; + } else if(offset == 0 && L.align.default) { + pair s=realmult(width,I*dir(g,t)); + s=axislabelfactor*s; + d=shift(s)*d; + } + add(f,d); +} + +// Check the tick coverage of a linear axis. +bool axiscoverage(int N, transform T, path g, ticklocate locate, real Step, + pair side, int sign, real Size, Label F, ticklabel ticklabel, + real norm, real limit) +{ + real coverage=0; + bool loop=cyclic(g); + real a=locate.S.Tinv(locate.a); + real b=locate.S.Tinv(locate.b); + real tickmin=finite(locate.S.tickMin) ? locate.S.Tinv(locate.S.tickMin) : a; + if(Size > 0) { + int count=0; + if(loop) count=N+1; + else { + for(int i=0; i <= N; ++i) { + real val=tickmin+i*Step; + if(val >= a && val <= b) + ++count; + } + } + if(count > 0) limit /= count; + for(int i=0; i <= N; ++i) { + real val=tickmin+i*Step; + if(loop || (val >= a && val <= b)) { + frame d; + pair dir=labeltick(d,T,g,locate,val,side,sign,Size,ticklabel,F,norm); + if(abs(dot(size(d),dir)) > limit) return false; + } + } + } + return true; +} + +// Check the tick coverage of a logarithmic axis. +bool logaxiscoverage(int N, transform T, path g, ticklocate locate, pair side, + int sign, real Size, Label F, ticklabel ticklabel, + real limit, int first, int last) +{ + bool loop=cyclic(g); + real coverage=0; + real a=locate.a; + real b=locate.b; + int count=0; + for(int i=first-1; i <= last+1; i += N) { + if(loop || i >= a && i <= b) + ++count; + } + if(count > 0) limit /= count; + for(int i=first-1; i <= last+1; i += N) { + if(loop || i >= a && i <= b) { + frame d; + pair dir=labeltick(d,T,g,locate,i,side,sign,Size,ticklabel,F); + if(abs(dot(size(d),dir)) > limit) return false; + } + } + return true; +} + +struct tickvalues { + real[] major; + real[] minor; + int N; // For logarithmic axes: number of decades between tick labels. +} + +// Determine a format that distinguishes adjacent pairs of ticks, optionally +// adding trailing zeros. +string autoformat(string format="", real norm ... real[] a) +{ + bool trailingzero=(format == trailingzero); + bool signedtrailingzero=(format == signedtrailingzero); + if(!trailingzero && !signedtrailingzero && format != "") return format; + + real[] A=sort(a); + real[] a=abs(A); + + bool signchange=(A.length > 1 && A[0] < 0 && A[A.length-1] >= 0); + + for(int i=0; i < A.length; ++i) + if(a[i] < zerotickfuzz*norm) A[i]=a[i]=0; + + int n=0; + + bool Fixed=find(a >= 1e4-epsilon | (a > 0 & a <= 1e-4-epsilon)) < 0; + + string Format=defaultformat(4,fixed=Fixed); + + if(Fixed && n < 4) { + for(int i=0; i < A.length; ++i) { + real a=A[i]; + while(format(defaultformat(n,fixed=Fixed),a) != format(Format,a)) + ++n; + } + } + + string trailing=trailingzero ? (signchange ? "# " : "#") : + signedtrailingzero ? "#+" : ""; + + string format=defaultformat(n,trailing,Fixed); + + for(int i=0; i < A.length-1; ++i) { + real a=A[i]; + real b=A[i+1]; + // Check if an extra digit of precision should be added. + string fixedformat="%#."+string(n+1)+"f"; + string A=format(fixedformat,a); + string B=format(fixedformat,b); + if(substr(A,length(A)-1,1) != "0" || substr(B,length(B)-1,1) != "0") { + a *= 0.1; + b *= 0.1; + } + if(a != b) { + while(format(format,a) == format(format,b)) + format=defaultformat(++n,trailing,Fixed); + } + } + + if(n == 0) return defaultformat; + return format; +} + +// Automatic tick generation routine. +tickvalues generateticks(int sign, Label F="", ticklabel ticklabel=null, + int N, int n=0, real Step=0, real step=0, + real Size=0, real size=0, + transform T, pair side, path g, real limit, + pen p, ticklocate locate, int[] divisor, + bool opposite) +{ + tickvalues tickvalues; + sign=opposite ? -sign : sign; + if(Size == 0) Size=Ticksize; + if(size == 0) size=ticksize; + F=F.copy(); + F.p(p); + + if(F.align.dir != 0) side=F.align.dir; + else if(side == 0) side=((sign == 1) ? left : right); + + bool ticklabels=false; + path G=T*g; + + if(!locate.S.scale.logarithmic) { + real a=locate.S.Tinv(locate.a); + real b=locate.S.Tinv(locate.b); + real norm=max(abs(a),abs(b)); + string format=autoformat(F.s,norm,a,b); + if(F.s == "%") F.s=""; + if(ticklabel == null) ticklabel=Format(format); + + if(a > b) {real temp=a; a=b; b=temp;} + + if(b-a < 100.0*epsilon*norm) b=a; + + bool autotick=Step == 0 && N == 0; + + real tickmin=finite(locate.S.tickMin) && (autotick || locate.S.automin) ? + locate.S.Tinv(locate.S.tickMin) : a; + real tickmax=finite(locate.S.tickMax) && (autotick || locate.S.automax) ? + locate.S.Tinv(locate.S.tickMax) : b; + if(tickmin > tickmax) {real temp=tickmin; tickmin=tickmax; tickmax=temp;} + + real inStep=Step; + + bool calcStep=true; + real len=tickmax-tickmin; + if(autotick) { + N=1; + if(divisor.length > 0) { + bool autoscale=locate.S.automin && locate.S.automax; + real h=0.5*(b-a); + if(h > 0) { + for(int d=divisor.length-1; d >= 0; --d) { + int N0=divisor[d]; + Step=len/N0; + int N1=N0; + int m=2; + while(Step > h) { + N0=m*N1; + Step=len/N0; + m *= 2; + } + if(axiscoverage(N0,T,g,locate,Step,side,sign,Size,F,ticklabel,norm, + limit)) { + N=N0; + if(N0 == 1 && !autoscale && d < divisor.length-1) { + // Try using 2 ticks (otherwise 1); + int div=divisor[d+1]; + Step=quotient(div,2)*len/div; + calcStep=false; + if(axiscoverage(2,T,g,locate,Step,side,sign,Size,F,ticklabel, + norm,limit)) N=2; + else Step=len; + } + // Found a good divisor; now compute subtick divisor + if(n == 0) { + if(step != 0) n=ceil(Step/step); + else { + n=quotient(divisor[divisor.length-1],N); + if(N == 1) n=(a*b >= 0) ? 2 : 1; + if(n == 1) n=2; + } + } + break; + } + } + } + } + } + + if(inStep != 0 && !locate.S.automin) { + tickmin=floor(tickmin/Step)*Step; + len=tickmax-tickmin; + } + + if(calcStep) { + if(N == 1) N=2; + if(N == 0) N=(int) (len/Step); + else Step=len/N; + } + + if(n == 0) { + if(step != 0) n=ceil(Step/step); + } else step=Step/n; + + b += epsilon*norm; + + if(Size > 0) { + for(int i=0; i <= N; ++i) { + real val=tickmin+i*Step; + if(val >= a && val <= b) + tickvalues.major.push(val); + if(size > 0 && step > 0) { + real iStep=i*Step; + real jstop=(len-iStep)/step; + for(int j=1; j < n && j <= jstop; ++j) { + real val=tickmin+iStep+j*step; + if(val >= a && val <= b) + tickvalues.minor.push(val); + } + } + } + } + + } else { // Logarithmic + string format=F.s; + if(F.s == "%") F.s=""; + + int base=round(locate.S.scale.Tinv(1)); + + if(ticklabel == null) + ticklabel=format == "%" ? Format("") : DefaultLogFormat(base); + real a=locate.S.postscale.Tinv(locate.a); + real b=locate.S.postscale.Tinv(locate.b); + if(a > b) {real temp=a; a=b; b=temp;} + + int first=floor(a-epsilon); + int last=ceil(b+epsilon); + + if(N == 0) { + N=1; + while(N <= last-first) { + if(logaxiscoverage(N,T,g,locate,side,sign,Size,F,ticklabel,limit, + first,last)) break; + ++N; + } + } + + if(N <= 2 && n == 0) n=base; + tickvalues.N=N; + + if(N > 0) { + for(int i=first-1; i <= last+1; ++i) { + if(i >= a && i <= b) + tickvalues.major.push(locate.S.scale.Tinv(i)); + if(n > 0) { + for(int j=2; j < n; ++j) { + real val=(i+1+locate.S.scale.T(j/n)); + if(val >= a && val <= b) + tickvalues.minor.push(locate.S.scale.Tinv(val)); + } + } + } + } + } + return tickvalues; +} + +// Signature of routines that draw labelled paths with ticks and tick labels. +typedef void ticks(frame, transform, Label, pair, path, path, pen, + arrowbar, margin, ticklocate, int[], bool opposite=false); + +// Tick construction routine for a user-specified array of tick values. +ticks Ticks(int sign, Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks=new real[], real[] ticks=new real[], int N=1, + bool begin=true, bool end=true, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return new void(frame f, transform t, Label L, pair side, path g, path g2, + pen p, arrowbar arrow, margin margin, ticklocate locate, + int[] divisor, bool opposite) { + // Use local copy of context variables: + int sign=opposite ? -sign : sign; + pen pTick=pTick; + pen ptick=ptick; + ticklabel ticklabel=ticklabel; + + real Size=Size; + real size=size; + if(Size == 0) Size=Ticksize; + if(size == 0) size=ticksize; + + Label L=L.copy(); + Label F=F.copy(); + L.p(p); + F.p(p); + if(pTick == nullpen) pTick=p; + if(ptick == nullpen) ptick=pTick; + + if(F.align.dir != 0) side=F.align.dir; + else if(side == 0) side=F.T*((sign == 1) ? left : right); + + bool ticklabels=false; + path G=t*g; + path G2=t*g2; + + scalefcn T; + + real a,b; + if(locate.S.scale.logarithmic) { + a=locate.S.postscale.Tinv(locate.a); + b=locate.S.postscale.Tinv(locate.b); + T=locate.S.scale.T; + } else { + a=locate.S.Tinv(locate.a); + b=locate.S.Tinv(locate.b); + T=identity; + } + + if(a > b) {real temp=a; a=b; b=temp;} + + real norm=max(abs(a),abs(b)); + + string format=autoformat(F.s,norm...Ticks); + if(F.s == "%") F.s=""; + if(ticklabel == null) { + if(locate.S.scale.logarithmic) { + int base=round(locate.S.scale.Tinv(1)); + ticklabel=format == "%" ? Format("") : DefaultLogFormat(base); + } else ticklabel=Format(format); + } + + begingroup(f); + if(opposite) draw(f,G,p); + else draw(f,margin(G,p).g,p,arrow); + for(int i=(begin ? 0 : 1); i < (end ? Ticks.length : Ticks.length-1); ++i) { + real val=T(Ticks[i]); + if(val >= a && val <= b) + drawtick(f,t,g,g2,locate,val,Size,sign,pTick,extend); + } + for(int i=0; i < ticks.length; ++i) { + real val=T(ticks[i]); + if(val >= a && val <= b) + drawtick(f,t,g,g2,locate,val,size,sign,ptick,extend); + } + endgroup(f); + + if(N == 0) N=1; + if(Size > 0 && !opposite) { + for(int i=(beginlabel ? 0 : 1); + i < (endlabel ? Ticks.length : Ticks.length-1); i += N) { + real val=T(Ticks[i]); + if(val >= a && val <= b) { + ticklabels=true; + labeltick(f,t,g,locate,val,side,sign,Size,ticklabel,F,norm); + } + } + } + if(L.s != "" && !opposite) + labelaxis(f,t,L,G,locate,sign,ticklabels); + }; +} + +// Optional routine to allow modification of auto-generated tick values. +typedef tickvalues tickmodifier(tickvalues); +tickvalues None(tickvalues v) {return v;} + +// Tickmodifier that removes all ticks in the intervals [a[i],b[i]]. +tickmodifier OmitTickIntervals(real[] a, real[] b) { + return new tickvalues(tickvalues v) { + if(a.length != b.length) abort(differentlengths); + void omit(real[] A) { + if(A.length != 0) { + real norm=max(abs(A)); + for(int i=0; i < a.length; ++i) { + int j; + while((j=find(A > a[i]-zerotickfuzz*norm + & A < b[i]+zerotickfuzz*norm)) >= 0) { + A.delete(j); + } + } + } + } + omit(v.major); + omit(v.minor); + return v; + }; +} + +// Tickmodifier that removes all ticks in the interval [a,b]. +tickmodifier OmitTickInterval(real a, real b) { + return OmitTickIntervals(new real[] {a}, new real[] {b}); +} + +// Tickmodifier that removes the specified ticks. +tickmodifier OmitTick(... real[] x) { + return OmitTickIntervals(x,x); +} + +tickmodifier NoZero=OmitTick(0); + +tickmodifier Break(real, real)=OmitTickInterval; + +// Automatic tick construction routine. +ticks Ticks(int sign, Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return new void(frame f, transform T, Label L, pair side, path g, path g2, + pen p, arrowbar arrow, margin margin, ticklocate locate, + int[] divisor, bool opposite) { + real limit=Step == 0 ? axiscoverage*arclength(T*g) : 0; + tickvalues values=modify(generateticks(sign,F,ticklabel,N,n,Step,step, + Size,size,T,side,g, + limit,p,locate,divisor,opposite)); + + Ticks(sign,F,ticklabel,beginlabel,endlabel,values.major,values.minor, + values.N,begin,end,Size,size,extend,pTick,ptick) + (f,T,L,side,g,g2,p,arrow,margin,locate,divisor,opposite); + }; +} + +ticks NoTicks() +{ + return new void(frame f, transform T, Label L, pair, path g, path, pen p, + arrowbar arrow, margin margin, ticklocate, + int[], bool opposite) { + path G=T*g; + if(opposite) draw(f,G,p); + else { + draw(f,margin(G,p).g,p,arrow); + if(L.s != "") { + Label L=L.copy(); + L.p(p); + labelaxis(f,T,L,G); + } + } + }; +} + +ticks LeftTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks(-1,format,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,modify,Size,size,extend,pTick,ptick); +} + +ticks RightTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks(1,format,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,modify,Size,size,extend,pTick,ptick); +} + +ticks Ticks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks(0,format,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,modify,Size,size,extend,pTick,ptick); +} + +ticks LeftTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks, real[] ticks=new real[], + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks(-1,format,ticklabel,beginlabel,endlabel, + Ticks,ticks,Size,size,extend,pTick,ptick); +} + +ticks RightTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks, real[] ticks=new real[], + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks(1,format,ticklabel,beginlabel,endlabel, + Ticks,ticks,Size,size,extend,pTick,ptick); +} + +ticks Ticks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks, real[] ticks=new real[], + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks(0,format,ticklabel,beginlabel,endlabel, + Ticks,ticks,Size,size,extend,pTick,ptick); +} + +ticks NoTicks=NoTicks(), +LeftTicks=LeftTicks(), +RightTicks=RightTicks(), +Ticks=Ticks(); + +pair tickMin(picture pic) +{ + return minbound(pic.userMin(),(pic.scale.x.tickMin,pic.scale.y.tickMin)); +} + +pair tickMax(picture pic) +{ + return maxbound(pic.userMax(),(pic.scale.x.tickMax,pic.scale.y.tickMax)); +} + +int Min=-1; +int Value=0; +int Max=1; +int Both=2; + +// Structure used to communicate axis and autoscale settings to tick routines. +struct axisT { + int type; // -1 = min, 0 = given value, 1 = max, 2 = min/max + int type2; // for 3D axis + real value; + real value2; + pair side; // 2D tick label direction relative to path (left or right) + real position; // label position along axis + align align; // default axis label alignment and 3D tick label direction + int[] xdivisor; + int[] ydivisor; + int[] zdivisor; + bool extend; // extend axis to graph boundary? +}; + +axisT axis; +typedef void axis(picture, axisT); + +axis Bottom(bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Min; + axis.position=0.5; + axis.side=right; + axis.align=S; + axis.extend=extend; + }; +} + +axis Top(bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Max; + axis.position=0.5; + axis.side=left; + axis.align=N; + axis.extend=extend; + }; +} + +axis BottomTop(bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Both; + axis.position=0.5; + axis.side=right; + axis.align=S; + axis.extend=extend; + }; +} + +axis Left(bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Min; + axis.position=0.5; + axis.side=left; + axis.align=W; + axis.extend=extend; + }; +} + +axis Right(bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Max; + axis.position=0.5; + axis.side=right; + axis.align=E; + axis.extend=extend; + }; +} + +axis LeftRight(bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Both; + axis.position=0.5; + axis.side=left; + axis.align=W; + axis.extend=extend; + }; +} + +axis XEquals(real x, bool extend=true) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.value=pic.scale.x.T(x); + axis.position=1; + axis.side=left; + axis.align=W; + axis.extend=extend; + }; +} + +axis YEquals(real y, bool extend=true) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.value=pic.scale.y.T(y); + axis.position=1; + axis.side=right; + axis.align=S; + axis.extend=extend; + }; +} + +axis XZero(bool extend=true) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.value=pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); + axis.position=1; + axis.side=left; + axis.align=W; + axis.extend=extend; + }; +} + +axis YZero(bool extend=true) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.value=pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); + axis.position=1; + axis.side=right; + axis.align=S; + axis.extend=extend; + }; +} + +axis Bottom=Bottom(), +Top=Top(), +BottomTop=BottomTop(), +Left=Left(), +Right=Right(), +LeftRight=LeftRight(), +XZero=XZero(), +YZero=YZero(); + +// Draw a general axis. +void axis(picture pic=currentpicture, Label L="", path g, path g2=nullpath, + pen p=currentpen, ticks ticks, ticklocate locate, + arrowbar arrow=None, margin margin=NoMargin, + int[] divisor=new int[], bool above=false, bool opposite=false) +{ + Label L=L.copy(); + real t=reltime(g,0.5); + if(L.defaultposition) L.position(t); + divisor=copy(divisor); + locate=locate.copy(); + pic.add(new void (frame f, transform t, transform T, pair lb, pair rt) { + frame d; + ticks(d,t,L,0,g,g2,p,arrow,margin,locate,divisor,opposite); + (above ? add : prepend)(f,t*T*inverse(t)*d); + }); + + pic.addPath(g,p); + + if(L.s != "") { + frame f; + Label L0=L.copy(); + L0.position(0); + add(f,L0); + pair pos=point(g,L.relative()*length(g)); + pic.addBox(pos,pos,min(f),max(f)); + } +} + +real xtrans(transform t, real x) +{ + return (t*(x,0)).x; +} + +real ytrans(transform t, real y) +{ + return (t*(0,y)).y; +} + +// An internal routine to draw an x axis at a particular y value. +void xaxisAt(picture pic=currentpicture, Label L="", axis axis, + real xmin=-infinity, real xmax=infinity, pen p=currentpen, + ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin, + bool above=true, bool opposite=false) +{ + real y=axis.value; + real y2; + Label L=L.copy(); + int[] divisor=copy(axis.xdivisor); + pair side=axis.side; + int type=axis.type; + + pic.add(new void (frame f, transform t, transform T, pair lb, pair rt) { + transform tinv=inverse(t); + pair a=xmin == -infinity ? tinv*(lb.x-min(p).x,ytrans(t,y)) : (xmin,y); + pair b=xmax == infinity ? tinv*(rt.x-max(p).x,ytrans(t,y)) : (xmax,y); + pair a2=xmin == -infinity ? tinv*(lb.x-min(p).x,ytrans(t,y2)) : (xmin,y2); + pair b2=xmax == infinity ? tinv*(rt.x-max(p).x,ytrans(t,y2)) : (xmax,y2); + + if(xmin == -infinity || xmax == infinity) { + bounds mx=autoscale(a.x,b.x,pic.scale.x.scale); + pic.scale.x.tickMin=mx.min; + pic.scale.x.tickMax=mx.max; + divisor=mx.divisor; + } + + real fuzz=epsilon*max(abs(a.x),abs(b.x)); + a -= (fuzz,0); + b += (fuzz,0); + + frame d; + ticks(d,t,L,side,a--b,finite(y2) ? a2--b2 : nullpath,p,arrow,margin, + ticklocate(a.x,b.x,pic.scale.x),divisor,opposite); + (above ? add : prepend)(f,t*T*tinv*d); + }); + + void bounds() { + if(type == Both) { + y2=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax().y; + y=opposite ? y2 : + (pic.scale.y.automin() ? tickMin(pic).y : pic.userMin().y); + } + else if(type == Min) + y=pic.scale.y.automin() ? tickMin(pic).y : pic.userMin().y; + else if(type == Max) + y=pic.scale.y.automax() ? tickMax(pic).y : pic.userMax().y; + + real Xmin=finite(xmin) ? xmin : pic.userMin().x; + real Xmax=finite(xmax) ? xmax : pic.userMax().x; + + pair a=(Xmin,y); + pair b=(Xmax,y); + pair a2=(Xmin,y2); + pair b2=(Xmax,y2); + + if(finite(a)) { + pic.addPoint(a,min(p)); + pic.addPoint(a,max(p)); + } + + if(finite(b)) { + pic.addPoint(b,min(p)); + pic.addPoint(b,max(p)); + } + + if(finite(a) && finite(b)) { + frame d; + ticks(d,pic.scaling(warn=false),L,side, + (a.x,0)--(b.x,0),(a2.x,0)--(b2.x,0),p,arrow,margin, + ticklocate(a.x,b.x,pic.scale.x),divisor,opposite); + frame f; + if(L.s != "") { + Label L0=L.copy(); + L0.position(0); + add(f,L0); + } + pair pos=a+L.relative()*(b-a); + pic.addBox(pos,pos,(min(f).x,min(d).y),(max(f).x,max(d).y)); + } + } + + // Process any queued y axis bound calculation requests. + for(int i=0; i < pic.scale.y.bound.length; ++i) + pic.scale.y.bound[i](); + + pic.scale.y.bound.delete(); + + bounds(); + + // Request another x bounds calculation before final picture scaling. + pic.scale.x.bound.push(bounds); +} + +// An internal routine to draw a y axis at a particular x value. +void yaxisAt(picture pic=currentpicture, Label L="", axis axis, + real ymin=-infinity, real ymax=infinity, pen p=currentpen, + ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin, + bool above=true, bool opposite=false) +{ + real x=axis.value; + real x2; + Label L=L.copy(); + int[] divisor=copy(axis.ydivisor); + pair side=axis.side; + int type=axis.type; + + pic.add(new void (frame f, transform t, transform T, pair lb, pair rt) { + transform tinv=inverse(t); + pair a=ymin == -infinity ? tinv*(xtrans(t,x),lb.y-min(p).y) : (x,ymin); + pair b=ymax == infinity ? tinv*(xtrans(t,x),rt.y-max(p).y) : (x,ymax); + pair a2=ymin == -infinity ? tinv*(xtrans(t,x2),lb.y-min(p).y) : (x2,ymin); + pair b2=ymax == infinity ? tinv*(xtrans(t,x2),rt.y-max(p).y) : (x2,ymax); + + if(ymin == -infinity || ymax == infinity) { + bounds my=autoscale(a.y,b.y,pic.scale.y.scale); + pic.scale.y.tickMin=my.min; + pic.scale.y.tickMax=my.max; + divisor=my.divisor; + } + + real fuzz=epsilon*max(abs(a.y),abs(b.y)); + a -= (0,fuzz); + b += (0,fuzz); + + frame d; + ticks(d,t,L,side,a--b,finite(x2) ? a2--b2 : nullpath,p,arrow,margin, + ticklocate(a.y,b.y,pic.scale.y),divisor,opposite); + (above ? add : prepend)(f,t*T*tinv*d); + }); + + void bounds() { + if(type == Both) { + x2=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax().x; + x=opposite ? x2 : + (pic.scale.x.automin() ? tickMin(pic).x : pic.userMin().x); + } else if(type == Min) + x=pic.scale.x.automin() ? tickMin(pic).x : pic.userMin().x; + else if(type == Max) + x=pic.scale.x.automax() ? tickMax(pic).x : pic.userMax().x; + + real Ymin=finite(ymin) ? ymin : pic.userMin().y; + real Ymax=finite(ymax) ? ymax : pic.userMax().y; + + pair a=(x,Ymin); + pair b=(x,Ymax); + pair a2=(x2,Ymin); + pair b2=(x2,Ymax); + + if(finite(a)) { + pic.addPoint(a,min(p)); + pic.addPoint(a,max(p)); + } + + if(finite(b)) { + pic.addPoint(b,min(p)); + pic.addPoint(b,max(p)); + } + + if(finite(a) && finite(b)) { + frame d; + ticks(d,pic.scaling(warn=false),L,side, + (0,a.y)--(0,b.y),(0,a2.y)--(0,b2.y),p,arrow,margin, + ticklocate(a.y,b.y,pic.scale.y),divisor,opposite); + frame f; + if(L.s != "") { + Label L0=L.copy(); + L0.position(0); + add(f,L0); + } + pair pos=a+L.relative()*(b-a); + pic.addBox(pos,pos,(min(d).x,min(f).y),(max(d).x,max(f).y)); + } + } + + // Process any queued x axis bound calculation requests. + for(int i=0; i < pic.scale.x.bound.length; ++i) + pic.scale.x.bound[i](); + + pic.scale.x.bound.delete(); + + bounds(); + + // Request another y bounds calculation before final picture scaling. + pic.scale.y.bound.push(bounds); +} + +// Set the x limits of a picture. +void xlimits(picture pic=currentpicture, real min=-infinity, real max=infinity, + bool crop=NoCrop) +{ + if(min > max) return; + + pic.scale.x.automin=min <= -infinity; + pic.scale.x.automax=max >= infinity; + + bounds mx; + if(pic.scale.x.automin() || pic.scale.x.automax()) + mx=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale); + + if(pic.scale.x.automin) { + if(pic.scale.x.automin()) pic.userMinx(mx.min); + } else pic.userMinx(min(pic.scale.x.T(min),pic.scale.x.T(max))); + + if(pic.scale.x.automax) { + if(pic.scale.x.automax()) pic.userMaxx(mx.max); + } else pic.userMaxx(max(pic.scale.x.T(min),pic.scale.x.T(max))); + + if(crop) { + pair userMin=pic.userMin(); + pair userMax=pic.userMax(); + pic.bounds.xclip(userMin.x,userMax.x); + pic.clip(userMin, userMax, + new void (frame f, transform t, transform T, pair, pair) { + frame Tinvf=T == identity() ? f : t*inverse(T)*inverse(t)*f; + clip(f,T*box(((t*userMin).x,(min(Tinvf)).y), + ((t*userMax).x,(max(Tinvf)).y))); + }); + } +} + +// Set the y limits of a picture. +void ylimits(picture pic=currentpicture, real min=-infinity, real max=infinity, + bool crop=NoCrop) +{ + if(min > max) return; + + pic.scale.y.automin=min <= -infinity; + pic.scale.y.automax=max >= infinity; + + bounds my; + if(pic.scale.y.automin() || pic.scale.y.automax()) + my=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale); + + if(pic.scale.y.automin) { + if(pic.scale.y.automin()) pic.userMiny(my.min); + } else pic.userMiny(min(pic.scale.y.T(min),pic.scale.y.T(max))); + + if(pic.scale.y.automax) { + if(pic.scale.y.automax()) pic.userMaxy(my.max); + } else pic.userMaxy(max(pic.scale.y.T(min),pic.scale.y.T(max))); + + if(crop) { + pair userMin=pic.userMin(); + pair userMax=pic.userMax(); + pic.bounds.yclip(userMin.y,userMax.y); + pic.clip(userMin, userMax, + new void (frame f, transform t, transform T, pair, pair) { + frame Tinvf=T == identity() ? f : t*inverse(T)*inverse(t)*f; + clip(f,T*box(((min(Tinvf)).x,(t*userMin).y), + ((max(Tinvf)).x,(t*userMax).y))); + }); + } +} + +// Crop a picture to the current user-space picture limits. +void crop(picture pic=currentpicture) +{ + xlimits(pic,false); + ylimits(pic,false); + if(pic.userSetx() && pic.userSety()) + clip(pic,box(pic.userMin(),pic.userMax())); +} + +// Restrict the x and y limits to box(min,max). +void limits(picture pic=currentpicture, pair min, pair max, bool crop=NoCrop) +{ + xlimits(pic,min.x,max.x); + ylimits(pic,min.y,max.y); + if(crop && pic.userSetx() && pic.userSety()) + clip(pic,box(pic.userMin(),pic.userMax())); +} + +// Internal routine to autoscale the user limits of a picture. +void autoscale(picture pic=currentpicture, axis axis) +{ + if(!pic.scale.set) { + bounds mx,my; + pic.scale.set=true; + + if(pic.userSetx()) { + mx=autoscale(pic.userMin().x,pic.userMax().x,pic.scale.x.scale); + if(pic.scale.x.scale.logarithmic && + floor(pic.userMin().x) == floor(pic.userMax().x)) { + if(pic.scale.x.automin()) + pic.userMinx2(floor(pic.userMin().x)); + if(pic.scale.x.automax()) + pic.userMaxx2(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.userMiny2(floor(pic.userMin().y)); + if(pic.scale.y.automax()) + pic.userMaxy2(ceil(pic.userMax().y)); + } + } else {my.min=my.max=0; pic.scale.set=false;} + + pic.scale.x.tickMin=mx.min; + pic.scale.x.tickMax=mx.max; + pic.scale.y.tickMin=my.min; + pic.scale.y.tickMax=my.max; + axis.xdivisor=mx.divisor; + axis.ydivisor=my.divisor; + } +} + +// Draw an x axis. +void xaxis(picture pic=currentpicture, Label L="", axis axis=YZero, + real xmin=-infinity, real xmax=infinity, pen p=currentpen, + ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin, + bool above=false) +{ + if(xmin > xmax) return; + + if(pic.scale.x.automin && xmin > -infinity) pic.scale.x.automin=false; + if(pic.scale.x.automax && xmax < infinity) pic.scale.x.automax=false; + + if(!pic.scale.set) { + axis(pic,axis); + autoscale(pic,axis); + } + + Label L=L.copy(); + bool newticks=false; + + if(xmin != -infinity) { + xmin=pic.scale.x.T(xmin); + newticks=true; + } + + if(xmax != infinity) { + xmax=pic.scale.x.T(xmax); + newticks=true; + } + + if(newticks && pic.userSetx() && ticks != NoTicks) { + if(xmin == -infinity) xmin=pic.userMin().x; + if(xmax == infinity) xmax=pic.userMax().x; + bounds mx=autoscale(xmin,xmax,pic.scale.x.scale); + pic.scale.x.tickMin=mx.min; + pic.scale.x.tickMax=mx.max; + axis.xdivisor=mx.divisor; + } + + axis(pic,axis); + + if(xmin == -infinity && !axis.extend) { + if(pic.scale.set) + xmin=pic.scale.x.automin() ? pic.scale.x.tickMin : + max(pic.scale.x.tickMin,pic.userMin().x); + else xmin=pic.userMin().x; + } + + if(xmax == infinity && !axis.extend) { + if(pic.scale.set) + xmax=pic.scale.x.automax() ? pic.scale.x.tickMax : + min(pic.scale.x.tickMax,pic.userMax().x); + else xmax=pic.userMax().x; + } + + if(L.defaultposition) L.position(axis.position); + L.align(L.align,axis.align); + + xaxisAt(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above); + if(axis.type == Both) + xaxisAt(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,true); +} + +// Draw a y axis. +void yaxis(picture pic=currentpicture, Label L="", axis axis=XZero, + real ymin=-infinity, real ymax=infinity, pen p=currentpen, + ticks ticks=NoTicks, arrowbar arrow=None, margin margin=NoMargin, + bool above=false, bool autorotate=true) +{ + if(ymin > ymax) return; + + if(pic.scale.y.automin && ymin > -infinity) pic.scale.y.automin=false; + if(pic.scale.y.automax && ymax < infinity) pic.scale.y.automax=false; + + if(!pic.scale.set) { + axis(pic,axis); + autoscale(pic,axis); + } + + Label L=L.copy(); + bool newticks=false; + + if(ymin != -infinity) { + ymin=pic.scale.y.T(ymin); + newticks=true; + } + + if(ymax != infinity) { + ymax=pic.scale.y.T(ymax); + newticks=true; + } + + if(newticks && pic.userSety() && ticks != NoTicks) { + if(ymin == -infinity) ymin=pic.userMin().y; + if(ymax == infinity) ymax=pic.userMax().y; + bounds my=autoscale(ymin,ymax,pic.scale.y.scale); + pic.scale.y.tickMin=my.min; + pic.scale.y.tickMax=my.max; + axis.ydivisor=my.divisor; + } + + axis(pic,axis); + + if(ymin == -infinity && !axis.extend) { + if(pic.scale.set) + ymin=pic.scale.y.automin() ? pic.scale.y.tickMin : + max(pic.scale.y.tickMin,pic.userMin().y); + else ymin=pic.userMin().y; + } + + + if(ymax == infinity && !axis.extend) { + if(pic.scale.set) + ymax=pic.scale.y.automax() ? pic.scale.y.tickMax : + min(pic.scale.y.tickMax,pic.userMax().y); + else ymax=pic.userMax().y; + } + + if(L.defaultposition) L.position(axis.position); + L.align(L.align,axis.align); + + if(autorotate && L.defaulttransform) { + frame f; + add(f,Label(L.s,(0,0),L.p)); + if(length(max(f)-min(f)) > ylabelwidth*fontsize(L.p)) + L.transform(rotate(90)); + } + + yaxisAt(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above); + if(axis.type == Both) + yaxisAt(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,true); +} + +// Draw x and y axes. +void axes(picture pic=currentpicture, Label xlabel="", Label ylabel="", + bool extend=true, + pair min=(-infinity,-infinity), pair max=(infinity,infinity), + pen p=currentpen, arrowbar arrow=None, margin margin=NoMargin, + bool above=false) +{ + xaxis(pic,xlabel,YZero(extend),min.x,max.x,p,arrow,margin,above); + yaxis(pic,ylabel,XZero(extend),min.y,max.y,p,arrow,margin,above); +} + +// Draw a yaxis at x. +void xequals(picture pic=currentpicture, Label L="", real x, + bool extend=false, real ymin=-infinity, real ymax=infinity, + pen p=currentpen, ticks ticks=NoTicks, + arrowbar arrow=None, margin margin=NoMargin, bool above=true) +{ + yaxis(pic,L,XEquals(x,extend),ymin,ymax,p,ticks,arrow,margin,above); +} + +// Draw an xaxis at y. +void yequals(picture pic=currentpicture, Label L="", real y, + bool extend=false, real xmin=-infinity, real xmax=infinity, + pen p=currentpen, ticks ticks=NoTicks, + arrowbar arrow=None, margin margin=NoMargin, bool above=true) +{ + xaxis(pic,L,YEquals(y,extend),xmin,xmax,p,ticks,arrow,margin,above); +} + +pair Scale(picture pic=currentpicture, pair z) +{ + return (pic.scale.x.T(z.x),pic.scale.y.T(z.y)); +} + +real ScaleX(picture pic=currentpicture, real x) +{ + return pic.scale.x.T(x); +} + +real ScaleY(picture pic=currentpicture, real y) +{ + return pic.scale.y.T(y); +} + +// Draw a tick of length size at pair z in direction dir using pen p. +void tick(picture pic=currentpicture, pair z, pair dir, real size=Ticksize, + pen p=currentpen) +{ + pair z=Scale(pic,z); + pic.add(new void (frame f, transform t) { + pair tz=t*z; + draw(f,tz--tz+unit(dir)*size,p); + }); + pic.addPoint(z,p); + pic.addPoint(z,unit(dir)*size,p); +} + +void xtick(picture pic=currentpicture, explicit pair z, pair dir=N, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,z,dir,size,p); +} + +void xtick(picture pic=currentpicture, real x, pair dir=N, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,(x,pic.scale.y.scale.logarithmic ? 1 : 0),dir,size,p); +} + +void ytick(picture pic=currentpicture, explicit pair z, pair dir=E, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,z,dir,size,p); +} + +void ytick(picture pic=currentpicture, real y, pair dir=E, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,(pic.scale.x.scale.logarithmic ? 1 : 0,y),dir,size,p); +} + +void tick(picture pic=currentpicture, Label L, real value, explicit pair z, + pair dir, string format="", real size=Ticksize, pen p=currentpen) +{ + Label L=L.copy(); + L.position(Scale(pic,z)); + L.align(L.align,-dir); + if(shift(L.T)*0 == 0) + L.T=shift(dot(dir,L.align.dir) > 0 ? dir*size : + ticklabelshift(L.align.dir,p))*L.T; + L.p(p); + if(L.s == "") L.s=format(format == "" ? defaultformat : format,value); + L.s=baseline(L.s,baselinetemplate); + add(pic,L); + tick(pic,z,dir,size,p); +} + +void xtick(picture pic=currentpicture, Label L, explicit pair z, pair dir=N, + string format="", real size=Ticksize, pen p=currentpen) +{ + tick(pic,L,z.x,z,dir,format,size,p); +} + +void xtick(picture pic=currentpicture, Label L, real x, pair dir=N, + string format="", real size=Ticksize, pen p=currentpen) +{ + xtick(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0),dir,size,p); +} + +void ytick(picture pic=currentpicture, Label L, explicit pair z, pair dir=E, + string format="", real size=Ticksize, pen p=currentpen) +{ + tick(pic,L,z.y,z,dir,format,size,p); +} + +void ytick(picture pic=currentpicture, Label L, real y, pair dir=E, + string format="", real size=Ticksize, pen p=currentpen) +{ + xtick(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y),dir,format,size,p); +} + +private void label(picture pic, Label L, pair z, real x, align align, + string format, pen p) +{ + Label L=L.copy(); + L.position(z); + L.align(align); + L.p(p); + if(shift(L.T)*0 == 0) + L.T=shift(ticklabelshift(L.align.dir,L.p))*L.T; + if(L.s == "") L.s=format(format == "" ? defaultformat : format,x); + L.s=baseline(L.s,baselinetemplate); + add(pic,L); +} + +// Put a label on the x axis. +void labelx(picture pic=currentpicture, Label L="", explicit pair z, + align align=S, string format="", pen p=currentpen) +{ + label(pic,L,Scale(pic,z),z.x,align,format,p); +} + +void labelx(picture pic=currentpicture, Label L="", real x, + align align=S, string format="", pen p=currentpen) +{ + labelx(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0),align,format,p); +} + +void labelx(picture pic=currentpicture, Label L, + string format="", explicit pen p=currentpen) +{ + labelx(pic,L,L.position.position,format,p); +} + +// Put a label on the y axis. +void labely(picture pic=currentpicture, Label L="", explicit pair z, + align align=W, string format="", pen p=currentpen) +{ + label(pic,L,Scale(pic,z),z.y,align,format,p); +} + +void labely(picture pic=currentpicture, Label L="", real y, + align align=W, string format="", pen p=currentpen) +{ + labely(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y),align,format,p); +} + +void labely(picture pic=currentpicture, Label L, + string format="", explicit pen p=currentpen) +{ + labely(pic,L,L.position.position,format,p); +} + +private string noprimary="Primary axis must be drawn before secondary axis"; + +// Construct a secondary X axis +picture secondaryX(picture primary=currentpicture, void f(picture)) +{ + if(!primary.scale.set) abort(noprimary); + picture pic; + size(pic,primary); + 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; + size(pic,primary); + if(primary.userMax().y == primary.userMin().y) return pic; + f(pic); + if(!pic.userSety()) return pic; + bounds a=autoscale(pic.userMin().y,pic.userMax().y,pic.scale.y.scale); + real bmin=pic.scale.y.automin() ? a.min : pic.userMin().y; + real bmax=pic.scale.y.automax() ? a.max : pic.userMax().y; + + real denom=bmax-bmin; + if(denom != 0) { + pic.erase(); + real m=(primary.userMax().y-primary.userMin().y)/denom; + pic.scale.y.postscale=Linear(m,bmin-primary.userMin().y/m); + pic.scale.set=true; + pic.scale.x.tickMin=primary.userMin().x; + pic.scale.x.tickMax=primary.userMax().x; + pic.scale.y.tickMin=pic.scale.y.postscale.T(a.min); + pic.scale.y.tickMax=pic.scale.y.postscale.T(a.max); + axis.ydivisor=a.divisor; + f(pic); + } + pic.userCopy(primary); + return pic; +} + +typedef guide graph(pair f(real), real, real, int); +typedef guide[] multigraph(pair f(real), real, real, int); + +graph graph(interpolate join) +{ + return new guide(pair f(real), real a, real b, int n) { + real width=b-a; + return n == 0 ? join(f(a)) : + join(...sequence(new guide(int i) {return f(a+(i/n)*width);},n+1)); + }; +} + +multigraph graph(interpolate join, bool3 cond(real)) +{ + return new guide[](pair f(real), real a, real b, int n) { + real width=b-a; + if(n == 0) return new guide[] {join(cond(a) ? f(a) : nullpath)}; + guide[] G; + guide[] g; + for(int i=0; i < n+1; ++i) { + real t=a+(i/n)*width; + bool3 b=cond(t); + if(b) + g.push(f(t)); + else { + if(g.length > 0) { + G.push(join(...g)); + g=new guide[] {}; + } + if(b == default) + g.push(f(t)); + } + } + if(g.length > 0) + G.push(join(...g)); + return G; + }; +} + +guide Straight(... guide[])=operator --; +guide Spline(... guide[])=operator ..; + +interpolate Hermite(splinetype splinetype) +{ + return new guide(... guide[] a) { + int n=a.length; + if(n == 0) return nullpath; + real[] x,y; + guide G; + for(int i=0; i < n; ++i) { + guide g=a[i]; + int m=size(g); + if(m == 0) continue; + pair z=point(g,0); + x.push(z.x); + y.push(z.y); + if(m > 1) { + G=G..hermite(x,y,splinetype) & g; + pair z=point(g,m); + x=new real[] {z.x}; + y=new real[] {z.y}; + } + } + return G & hermite(x,y,splinetype); + }; +} + +interpolate Hermite=Hermite(Spline); + +guide graph(picture pic=currentpicture, real f(real), real a, real b, + int n=ngraph, real T(real)=identity, interpolate join=operator --) +{ + if(T == identity) + return graph(join)(new pair(real x) { + return (x,pic.scale.y.T(f(pic.scale.x.Tinv(x))));}, + pic.scale.x.T(a),pic.scale.x.T(b),n); + else + return graph(join)(new pair(real x) { + return Scale(pic,(T(x),f(T(x))));}, + a,b,n); +} + +guide[] graph(picture pic=currentpicture, real f(real), real a, real b, + int n=ngraph, real T(real)=identity, + bool3 cond(real), interpolate join=operator --) +{ + if(T == identity) + return graph(join,cond)(new pair(real x) { + return (x,pic.scale.y.T(f(pic.scale.x.Tinv(x))));}, + pic.scale.x.T(a),pic.scale.x.T(b),n); + else + return graph(join,cond)(new pair(real x) { + return Scale(pic,(T(x),f(T(x))));}, + a,b,n); +} + +guide graph(picture pic=currentpicture, real x(real), real y(real), real a, + real b, int n=ngraph, real T(real)=identity, + interpolate join=operator --) +{ + if(T == identity) + return graph(join)(new pair(real t) {return Scale(pic,(x(t),y(t)));},a,b,n); + else + return graph(join)(new pair(real t) { + return Scale(pic,(x(T(t)),y(T(t)))); + },a,b,n); +} + +guide[] graph(picture pic=currentpicture, real x(real), real y(real), real a, + real b, int n=ngraph, real T(real)=identity, bool3 cond(real), + interpolate join=operator --) +{ + if(T == identity) + return graph(join,cond)(new pair(real t) {return Scale(pic,(x(t),y(t)));}, + a,b,n); + else + return graph(join,cond)(new pair(real t) { + return Scale(pic,(x(T(t)),y(T(t))));}, + a,b,n); +} + +guide graph(picture pic=currentpicture, pair z(real), real a, real b, + int n=ngraph, real T(real)=identity, interpolate join=operator --) +{ + if(T == identity) + return graph(join)(new pair(real t) {return Scale(pic,z(t));},a,b,n); + else + return graph(join)(new pair(real t) { + return Scale(pic,z(T(t))); + },a,b,n); +} + +guide[] graph(picture pic=currentpicture, pair z(real), real a, real b, + int n=ngraph, real T(real)=identity, bool3 cond(real), + interpolate join=operator --) +{ + if(T == identity) + return graph(join,cond)(new pair(real t) {return Scale(pic,z(t));},a,b,n); + else + return graph(join,cond)(new pair(real t) { + return Scale(pic,z(T(t))); + },a,b,n); +} + +string conditionlength="condition array has different length than data"; + +void checkconditionlength(int x, int y) +{ + checklengths(x,y,conditionlength); +} + +guide graph(picture pic=currentpicture, pair[] z, interpolate join=operator --) +{ + int i=0; + return graph(join)(new pair(real) { + pair w=Scale(pic,z[i]); + ++i; + return w; + },0,0,z.length-1); +} + +guide[] graph(picture pic=currentpicture, pair[] z, bool3[] cond, + interpolate join=operator --) +{ + int n=z.length; + int i=0; + pair w; + checkconditionlength(cond.length,n); + bool3 condition(real) { + bool3 b=cond[i]; + if(b != false) w=Scale(pic,z[i]); + ++i; + return b; + } + return graph(join,condition)(new pair(real) {return w;},0,0,n-1); +} + +guide graph(picture pic=currentpicture, real[] x, real[] y, + interpolate join=operator --) +{ + int n=x.length; + checklengths(n,y.length); + int i=0; + return graph(join)(new pair(real) { + pair w=Scale(pic,(x[i],y[i])); + ++i; + return w; + },0,0,n-1); +} + +guide[] graph(picture pic=currentpicture, real[] x, real[] y, bool3[] cond, + interpolate join=operator --) +{ + int n=x.length; + checklengths(n,y.length); + int i=0; + pair w; + checkconditionlength(cond.length,n); + bool3 condition(real) { + bool3 b=cond[i]; + if(b != false) w=Scale(pic,(x[i],y[i])); + ++i; + return b; + } + return graph(join,condition)(new pair(real) {return w;},0,0,n-1); +} + +// Connect points in z into segments corresponding to consecutive true elements +// of b using interpolation operator join. +path[] segment(pair[] z, bool[] cond, interpolate join=operator --) +{ + checkconditionlength(cond.length,z.length); + int[][] segment=segment(cond); + return sequence(new path(int i) {return join(...z[segment[i]]);}, + segment.length); +} + +pair polar(real r, real theta) +{ + return r*expi(theta); +} + +guide polargraph(picture pic=currentpicture, real r(real), real a, real b, + int n=ngraph, interpolate join=operator --) +{ + return graph(join)(new pair(real theta) { + return Scale(pic,polar(r(theta),theta)); + },a,b,n); +} + +guide polargraph(picture pic=currentpicture, real[] r, real[] theta, + interpolate join=operator--) +{ + int n=r.length; + checklengths(n,theta.length); + int i=0; + return graph(join)(new pair(real) { + pair w=Scale(pic,polar(r[i],theta[i])); + ++i; + return w; + },0,0,n-1); +} + +void errorbar(picture pic, pair z, pair dp, pair dm, pen p=currentpen, + real size=0) +{ + real dmx=-abs(dm.x); + real dmy=-abs(dm.y); + real dpx=abs(dp.x); + real dpy=abs(dp.y); + if(dmx != dpx) draw(pic,Scale(pic,z+(dmx,0))--Scale(pic,z+(dpx,0)),p, + Bars(size)); + if(dmy != dpy) draw(pic,Scale(pic,z+(0,dmy))--Scale(pic,z+(0,dpy)),p, + Bars(size)); +} + +void errorbars(picture pic=currentpicture, pair[] z, pair[] dp, pair[] dm={}, + bool[] cond={}, pen p=currentpen, real size=0) +{ + if(dm.length == 0) dm=dp; + int n=z.length; + checklengths(n,dm.length); + checklengths(n,dp.length); + bool all=cond.length == 0; + if(!all) + checkconditionlength(cond.length,n); + for(int i=0; i < n; ++i) { + if(all || cond[i]) + errorbar(pic,z[i],dp[i],dm[i],p,size); + } +} + +void errorbars(picture pic=currentpicture, real[] x, real[] y, + real[] dpx, real[] dpy, real[] dmx={}, real[] dmy={}, + bool[] cond={}, pen p=currentpen, real size=0) +{ + if(dmx.length == 0) dmx=dpx; + if(dmy.length == 0) dmy=dpy; + int n=x.length; + checklengths(n,y.length); + checklengths(n,dpx.length); + checklengths(n,dpy.length); + checklengths(n,dmx.length); + checklengths(n,dmy.length); + bool all=cond.length == 0; + if(!all) + checkconditionlength(cond.length,n); + for(int i=0; i < n; ++i) { + if(all || cond[i]) + errorbar(pic,(x[i],y[i]),(dpx[i],dpy[i]),(dmx[i],dmy[i]),p,size); + } +} + +void errorbars(picture pic=currentpicture, real[] x, real[] y, + real[] dpy, bool[] cond={}, pen p=currentpen, real size=0) +{ + errorbars(pic,x,y,0*x,dpy,cond,p,size); +} + +// Return a vector field on path g, specifying the vector as a function of the +// relative position along path g in [0,1]. +picture vectorfield(path vector(real), path g, int n, bool truesize=false, + pen p=currentpen, arrowbar arrow=Arrow, + margin margin=PenMargin) +{ + picture pic; + for(int i=0; i < n; ++i) { + real x=(n == 1) ? 0.5 : i/(n-1); + if(truesize) + draw(relpoint(g,x),pic,vector(x),p,arrow); + else + draw(pic,shift(relpoint(g,x))*vector(x),p,arrow,margin); + } + return pic; +} + +real maxlength(pair a, pair b, int nx, int ny) +{ + return min((b.x-a.x)/nx,(b.y-a.y)/ny); +} + +// return a vector field over box(a,b). +picture vectorfield(path vector(pair), pair a, pair b, + int nx=nmesh, int ny=nx, bool truesize=false, + real maxlength=truesize ? 0 : maxlength(a,b,nx,ny), + bool cond(pair z)=null, pen p=currentpen, + arrowbar arrow=Arrow, margin margin=PenMargin) +{ + picture pic; + real dx=1/nx; + real dy=1/ny; + bool all=cond == null; + real scale; + + if(maxlength > 0) { + real size(pair z) { + path g=vector(z); + return abs(point(g,size(g)-1)-point(g,0)); + } + real max=size(a); + for(int i=0; i <= nx; ++i) { + real x=interp(a.x,b.x,i*dx); + for(int j=0; j <= ny; ++j) + max=max(max,size((x,interp(a.y,b.y,j*dy)))); + } + scale=max > 0 ? maxlength/max : 1; + } else scale=1; + + for(int i=0; i <= nx; ++i) { + real x=interp(a.x,b.x,i*dx); + for(int j=0; j <= ny; ++j) { + real y=interp(a.y,b.y,j*dy); + pair z=(x,y); + if(all || cond(z)) { + path g=scale(scale)*vector(z); + if(truesize) + draw(z,pic,g,p,arrow); + else + draw(pic,shift(z)*g,p,arrow,margin); + } + } + } + return pic; +} + +// True arc +path Arc(pair c, real r, real angle1, real angle2, bool direction, + int n=nCircle) +{ + angle1=radians(angle1); + angle2=radians(angle2); + if(direction) { + if(angle1 >= angle2) angle1 -= 2pi; + } else if(angle2 >= angle1) angle2 -= 2pi; + return shift(c)*polargraph(new real(real t){return r;},angle1,angle2,n, + operator ..); +} + +path Arc(pair c, real r, real angle1, real angle2, int n=nCircle) +{ + return Arc(c,r,angle1,angle2,angle2 >= angle1 ? CCW : CW,n); +} + +path Arc(pair c, explicit pair z1, explicit pair z2, bool direction=CCW, + int n=nCircle) +{ + return Arc(c,abs(z1-c),degrees(z1-c),degrees(z2-c),direction,n); +} + +// True circle +path Circle(pair c, real r, int n=nCircle) +{ + return Arc(c,r,0,360,n)&cycle; +} diff --git a/Build/source/utils/asymptote/base/graph3.asy b/Build/source/utils/asymptote/base/graph3.asy new file mode 100644 index 00000000000..f690c6a3c10 --- /dev/null +++ b/Build/source/utils/asymptote/base/graph3.asy @@ -0,0 +1,2319 @@ +// Three-dimensional graphing routines + +private import math; +import graph; +import three; + +typedef triple direction3(real); +direction3 Dir(triple dir) {return new triple(real) {return dir;};} + +ticklocate ticklocate(real a, real b, autoscaleT S=defaultS, + real tickmin=-infinity, real tickmax=infinity, + real time(real)=null, direction3 dir) +{ + if((valuetime) time == null) time=linear(S.T(),a,b); + ticklocate locate; + locate.a=a; + locate.b=b; + locate.S=S.copy(); + if(finite(tickmin)) locate.S.tickMin=tickmin; + if(finite(tickmax)) locate.S.tickMax=tickmax; + locate.time=time; + locate.dir=zero; + locate.dir3=dir; + return locate; +} + +private struct locateT { + real t; // tick location time + triple V; // tick location in frame coordinates + triple pathdir; // path direction in frame coordinates + triple dir; // tick direction in frame coordinates + + void dir(transform3 T, path3 g, ticklocate locate, real t) { + pathdir=unit(shiftless(T)*dir(g,t)); + triple Dir=locate.dir3(t); + dir=unit(Dir); + } + // Locate the desired position of a tick along a path. + void calc(transform3 T, path3 g, ticklocate locate, real val) { + t=locate.time(val); + V=T*point(g,t); + dir(T,g,locate,t); + } +} + +void drawtick(picture pic, transform3 T, path3 g, path3 g2, + ticklocate locate, real val, real Size, int sign, pen p, + bool extend) +{ + locateT locate1,locate2; + locate1.calc(T,g,locate,val); + path3 G; + if(extend && size(g2) > 0) { + locate2.calc(T,g2,locate,val); + G=locate1.V--locate2.V; + } else + G=(sign == 0) ? + locate1.V-Size*locate1.dir--locate1.V+Size*locate1.dir : + locate1.V--locate1.V+Size*sign*locate1.dir; + draw(pic,G,p,name="tick"); +} + +triple ticklabelshift(triple align, pen p=currentpen) +{ + return 0.25*unit(align)*labelmargin(p); +} + +// Signature of routines that draw labelled paths with ticks and tick labels. +typedef void ticks3(picture, transform3, Label, path3, path3, pen, + arrowbar3, margin3, ticklocate, int[], bool opposite=false, + bool primary=true); + +// Label a tick on a frame. +void labeltick(picture pic, transform3 T, path3 g, + ticklocate locate, real val, int sign, real Size, + ticklabel ticklabel, Label F, real norm=0) +{ + locateT locate1; + locate1.calc(T,g,locate,val); + triple align=F.align.dir3; + if(align == O) align=sign*locate1.dir; + + triple shift=align*labelmargin(F.p); + if(dot(align,sign*locate1.dir) >= 0) + shift=sign*(Size)*locate1.dir; + + real label; + if(locate.S.scale.logarithmic) + label=locate.S.scale.Tinv(val); + else { + label=val; + if(abs(label) < zerotickfuzz*norm) label=0; + // Fix epsilon errors at +/-1e-4 + // default format changes to scientific notation here + if(abs(abs(label)-1e-4) < epsilon) label=sgn(label)*1e-4; + } + + string s=ticklabel(label); + triple v=locate1.V+shift; + if(s != "") + label(pic,F.defaulttransform3 ? baseline(s,baselinetemplate) : F.T3*s,v, + align,F.p); +} + +// Add axis label L to frame f. +void labelaxis(picture pic, transform3 T, Label L, path3 g, + ticklocate locate=null, int sign=1, bool ticklabels=false) +{ + triple m=pic.min(identity4); + triple M=pic.max(identity4); + triple align=L.align.dir3; + Label L=L.copy(); + + pic.add(new void(frame f, transform3 T, picture pic2, projection P) { + path3 g=T*g; + real t=relative(L,g); + triple v=point(g,t); + picture F; + if(L.align.dir3 == O) + align=unit(invert(L.align.dir,v,P))*abs(L.align.dir); + + if(ticklabels && locate != null && piecewisestraight(g)) { + locateT locate1; + locate1.dir(T,g,locate,t); + triple pathdir=locate1.pathdir; + + triple perp=cross(pathdir,P.normal); + if(align == O) + align=unit(sgn(dot(sign*locate1.dir,perp))*perp); + path[] g=project(box(T*m,T*M),P); + pair z=project(v,P); + pair Ppathdir=project(v+pathdir,P)-z; + pair Perp=unit(I*Ppathdir); + real angle=degrees(Ppathdir,warn=false); + transform S=rotate(-angle,z); + path[] G=S*g; + pair Palign=project(v+align,P)-z; + pair Align=rotate(-angle)*dot(Palign,Perp)*Perp; + pair offset=unit(Palign)* + abs((Align.y >= 0 ? max(G).y : (Align.y < 0 ? min(G).y : 0))-z.y); + triple normal=cross(pathdir,align); + if(normal != O) v=invert(z+offset,normal,v,P); + } + + label(F,L,v); + add(f,F.fit3(identity4,pic2,P)); + },exact=false); + + path3[] G=path3(texpath(L,bbox=true)); + if(G.length > 0) { + G=L.align.is3D ? align(G,O,align,L.p) : L.T3*G; + triple v=point(g,relative(L,g)); + pic.addBox(v,v,min(G),max(G)); + } +} + +// Tick construction routine for a user-specified array of tick values. +ticks3 Ticks3(int sign, Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks=new real[], real[] ticks=new real[], int N=1, + bool begin=true, bool end=true, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return new void(picture pic, transform3 t, Label L, path3 g, path3 g2, pen p, + arrowbar3 arrow, margin3 margin, ticklocate locate, + int[] divisor, bool opposite, bool primary) { + // Use local copy of context variables: + int Sign=opposite ? -1 : 1; + int sign=Sign*sign; + pen pTick=pTick; + pen ptick=ptick; + ticklabel ticklabel=ticklabel; + + real Size=Size; + real size=size; + if(Size == 0) Size=Ticksize; + if(size == 0) size=ticksize; + + Label L=L.copy(); + Label F=F.copy(); + L.p(p); + F.p(p); + if(pTick == nullpen) pTick=p; + if(ptick == nullpen) ptick=pTick; + + bool ticklabels=false; + path3 G=t*g; + path3 G2=t*g2; + + scalefcn T; + + real a,b; + if(locate.S.scale.logarithmic) { + a=locate.S.postscale.Tinv(locate.a); + b=locate.S.postscale.Tinv(locate.b); + T=locate.S.scale.T; + } else { + a=locate.S.Tinv(locate.a); + b=locate.S.Tinv(locate.b); + T=identity; + } + + if(a > b) {real temp=a; a=b; b=temp;} + + real norm=max(abs(a),abs(b)); + + string format=autoformat(F.s,norm...Ticks); + if(F.s == "%") F.s=""; + if(ticklabel == null) { + if(locate.S.scale.logarithmic) { + int base=round(locate.S.scale.Tinv(1)); + ticklabel=format == "%" ? Format("") : DefaultLogFormat(base); + } else ticklabel=Format(format); + } + + bool labelaxis=L.s != "" && primary; + + begingroup3(pic,"axis"); + + if(primary) draw(pic,margin(G,p).g,p,arrow); + else draw(pic,G,p); + + for(int i=(begin ? 0 : 1); i < (end ? Ticks.length : Ticks.length-1); ++i) { + real val=T(Ticks[i]); + if(val >= a && val <= b) + drawtick(pic,t,g,g2,locate,val,Size,sign,pTick,extend); + } + for(int i=0; i < ticks.length; ++i) { + real val=T(ticks[i]); + if(val >= a && val <= b) + drawtick(pic,t,g,g2,locate,val,size,sign,ptick,extend); + } + + if(N == 0) N=1; + if(Size > 0 && primary) { + for(int i=(beginlabel ? 0 : 1); + i < (endlabel ? Ticks.length : Ticks.length-1); i += N) { + real val=T(Ticks[i]); + if(val >= a && val <= b) { + ticklabels=true; + labeltick(pic,t,g,locate,val,Sign,Size,ticklabel,F,norm); + } + } + } + if(labelaxis) + labelaxis(pic,t,L,G,locate,Sign,ticklabels); + + endgroup3(pic); + }; +} + +// Automatic tick construction routine. +ticks3 Ticks3(int sign, Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return new void(picture pic, transform3 T, Label L, + path3 g, path3 g2, pen p, + arrowbar3 arrow, margin3 margin=NoMargin3, ticklocate locate, + int[] divisor, bool opposite, bool primary) { + path3 G=T*g; + real limit=Step == 0 ? axiscoverage*arclength(G) : 0; + tickvalues values=modify(generateticks(sign,F,ticklabel,N,n,Step,step, + Size,size,identity(),1, + project(G,currentprojection), + limit,p,locate,divisor, + opposite)); + Ticks3(sign,F,ticklabel,beginlabel,endlabel,values.major,values.minor, + values.N,begin,end,Size,size,extend,pTick,ptick) + (pic,T,L,g,g2,p,arrow,margin,locate,divisor,opposite,primary); + }; +} + +ticks3 NoTicks3() +{ + return new void(picture pic, transform3 T, Label L, path3 g, + path3, pen p, arrowbar3 arrow, margin3 margin, + ticklocate, int[], bool opposite, bool primary) { + path3 G=T*g; + if(primary) draw(pic,margin(G,p).g,p,arrow,margin); + else draw(pic,G,p); + if(L.s != "" && primary) { + Label L=L.copy(); + L.p(p); + labelaxis(pic,T,L,G,opposite ? -1 : 1); + } + }; +} + +ticks3 InTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks3(-1,format,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,modify,Size,size,extend,pTick,ptick); +} + +ticks3 OutTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks3(1,format,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,modify,Size,size,extend,pTick,ptick); +} + +ticks3 InOutTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, tickmodifier modify=None, + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks3(0,format,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,modify,Size,size,extend,pTick,ptick); +} + +ticks3 InTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks, real[] ticks=new real[], + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks3(-1,format,ticklabel,beginlabel,endlabel, + Ticks,ticks,Size,size,extend,pTick,ptick); +} + +ticks3 OutTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks, real[] ticks=new real[], + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks3(1,format,ticklabel,beginlabel,endlabel, + Ticks,ticks,Size,size,extend,pTick,ptick); +} + +ticks3 InOutTicks(Label format="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + real[] Ticks, real[] ticks=new real[], + real Size=0, real size=0, bool extend=false, + pen pTick=nullpen, pen ptick=nullpen) +{ + return Ticks3(0,format,ticklabel,beginlabel,endlabel, + Ticks,ticks,Size,size,extend,pTick,ptick); +} + +ticks3 NoTicks3=NoTicks3(), +InTicks=InTicks(), +OutTicks=OutTicks(), +InOutTicks=InOutTicks(); + +triple tickMin3(picture pic) +{ + return minbound(pic.userMin(),(pic.scale.x.tickMin,pic.scale.y.tickMin, + pic.scale.z.tickMin)); +} + +triple tickMax3(picture pic) +{ + return maxbound(pic.userMax(),(pic.scale.x.tickMax,pic.scale.y.tickMax, + pic.scale.z.tickMax)); +} + +axis Bounds(int type=Both, int type2=Both, triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=type; + axis.type2=type2; + axis.position=0.5; + axis.align=align; + axis.extend=extend; + }; +} + +axis YZEquals(real y, real z, triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.type2=Value; + axis.value=pic.scale.y.T(y); + axis.value2=pic.scale.z.T(z); + axis.position=1; + axis.align=align; + axis.extend=extend; + }; +} + +axis XZEquals(real x, real z, triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.type2=Value; + axis.value=pic.scale.x.T(x); + axis.value2=pic.scale.z.T(z); + axis.position=1; + axis.align=align; + axis.extend=extend; + }; +} + +axis XYEquals(real x, real y, triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.type2=Value; + axis.value=pic.scale.x.T(x); + axis.value2=pic.scale.y.T(y); + axis.position=1; + axis.align=align; + axis.extend=extend; + }; +} + +axis YZZero(triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.type2=Value; + axis.value=pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); + axis.value2=pic.scale.z.T(pic.scale.z.scale.logarithmic ? 1 : 0); + axis.position=1; + axis.align=align; + axis.extend=extend; + }; +} + +axis XZZero(triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.type2=Value; + axis.value=pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); + axis.value2=pic.scale.z.T(pic.scale.z.scale.logarithmic ? 1 : 0); + axis.position=1; + axis.align=align; + axis.extend=extend; + }; +} + +axis XYZero(triple align=O, bool extend=false) +{ + return new void(picture pic, axisT axis) { + axis.type=Value; + axis.type2=Value; + axis.value=pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); + axis.value2=pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); + axis.position=1; + axis.align=align; + axis.extend=extend; + }; +} + +axis +Bounds=Bounds(), +YZZero=YZZero(), +XZZero=XZZero(), +XYZero=XYZero(); + +// Draw a general three-dimensional axis. +void axis(picture pic=currentpicture, Label L="", path3 g, path3 g2=nullpath3, + pen p=currentpen, ticks3 ticks, ticklocate locate, + arrowbar3 arrow=None, margin3 margin=NoMargin3, + int[] divisor=new int[], bool above=false, bool opposite=false) +{ + Label L=L.copy(); + real t=reltime(g,0.5); + if(L.defaultposition) L.position(t); + divisor=copy(divisor); + locate=locate.copy(); + + pic.add(new void (picture f, transform3 t, transform3 T, triple, triple) { + picture d; + ticks(d,t,L,g,g2,p,arrow,margin,locate,divisor,opposite,true); + add(f,t*T*inverse(t)*d); + },above=above); + + addPath(pic,g,p); + + if(L.s != "") { + frame f; + Label L0=L.copy(); + L0.position(0); + add(f,L0); + triple pos=point(g,L.relative()*length(g)); + pic.addBox(pos,pos,min3(f),max3(f)); + } +} + +real xtrans(transform3 t, real x) +{ + return (t*(x,0,0)).x; +} + +real ytrans(transform3 t, real y) +{ + return (t*(0,y,0)).y; +} + +real ztrans(transform3 t, real z) +{ + return (t*(0,0,z)).z; +} + +private triple defaultdir(triple X, triple Y, triple Z, bool opposite=false, + projection P) { + triple u=cross(P.normal,Z); + return abs(dot(u,X)) > abs(dot(u,Y)) ? -X : (opposite ? Y : -Y); +} + +// An internal routine to draw an x axis at a particular y value. +void xaxis3At(picture pic=currentpicture, Label L="", axis axis, + real xmin=-infinity, real xmax=infinity, pen p=currentpen, + ticks3 ticks=NoTicks3, + arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=true, + bool opposite=false, bool opposite2=false, bool primary=true) +{ + int type=axis.type; + int type2=axis.type2; + triple dir=axis.align.dir3 == O ? + defaultdir(Y,Z,X,opposite^opposite2,currentprojection) : axis.align.dir3; + Label L=L.copy(); + if(L.align.dir3 == O && L.align.dir == 0) L.align(opposite ? -dir : dir); + + real y=axis.value; + real z=axis.value2; + real y2,z2; + int[] divisor=copy(axis.xdivisor); + + pic.add(new void(picture f, transform3 t, transform3 T, triple lb, + triple rt) { + transform3 tinv=inverse(t); + triple a=xmin == -infinity ? tinv*(lb.x-min3(p).x,ytrans(t,y), + ztrans(t,z)) : (xmin,y,z); + triple b=xmax == infinity ? tinv*(rt.x-max3(p).x,ytrans(t,y), + ztrans(t,z)) : (xmax,y,z); + real y0; + real z0; + if(abs(dir.y) < abs(dir.z)) { + y0=y; + z0=z2; + } else { + y0=y2; + z0=z; + } + + triple a2=xmin == -infinity ? tinv*(lb.x-min3(p).x,ytrans(t,y0), + ztrans(t,z0)) : (xmin,y0,z0); + triple b2=xmax == infinity ? tinv*(rt.x-max3(p).x,ytrans(t,y0), + ztrans(t,z0)) : (xmax,y0,z0); + + if(xmin == -infinity || xmax == infinity) { + bounds mx=autoscale(a.x,b.x,pic.scale.x.scale); + pic.scale.x.tickMin=mx.min; + pic.scale.x.tickMax=mx.max; + divisor=mx.divisor; + } + + triple fuzz=X*epsilon*max(abs(a.x),abs(b.x)); + a -= fuzz; + b += fuzz; + + picture d; + ticks(d,t,L,a--b,finite(y0) && finite(z0) ? a2--b2 : nullpath3, + p,arrow,margin, + ticklocate(a.x,b.x,pic.scale.x,Dir(dir)),divisor, + opposite,primary); + add(f,t*T*tinv*d); + },above=above); + + void bounds() { + if(type == Min) + y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y; + else if(type == Max) + y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; + else if(type == Both) { + y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; + y=opposite ? y2 : + (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y); + } + + if(type2 == Min) + z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z; + else if(type2 == Max) + z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; + else if(type2 == Both) { + z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; + z=opposite2 ? z2 : + (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z); + } + + real Xmin=finite(xmin) ? xmin : pic.userMin().x; + real Xmax=finite(xmax) ? xmax : pic.userMax().x; + + triple a=(Xmin,y,z); + triple b=(Xmax,y,z); + triple a2=(Xmin,y2,z2); + triple b2=(Xmax,y2,z2); + + if(finite(a)) { + pic.addPoint(a,min3(p)); + pic.addPoint(a,max3(p)); + } + + if(finite(b)) { + pic.addPoint(b,min3(p)); + pic.addPoint(b,max3(p)); + } + + if(finite(a) && finite(b)) { + picture d; + ticks(d,pic.scaling3(warn=false),L, + (a.x,0,0)--(b.x,0,0),(a2.x,0,0)--(b2.x,0,0),p,arrow,margin, + ticklocate(a.x,b.x,pic.scale.x,Dir(dir)),divisor, + opposite,primary); + frame f; + if(L.s != "") { + Label L0=L.copy(); + L0.position(0); + add(f,L0); + } + triple pos=a+L.relative()*(b-a); + triple m=min3(d); + triple M=max3(d); + pic.addBox(pos,pos,(min3(f).x,m.y,m.z),(max3(f).x,m.y,m.z)); + } + } + + // Process any queued y and z axes bound calculation requests. + for(int i=0; i < pic.scale.y.bound.length; ++i) + pic.scale.y.bound[i](); + for(int i=0; i < pic.scale.z.bound.length; ++i) + pic.scale.z.bound[i](); + + pic.scale.y.bound.delete(); + pic.scale.z.bound.delete(); + + bounds(); + + // Request another x bounds calculation before final picture scaling. + pic.scale.x.bound.push(bounds); +} + +// An internal routine to draw a y axis at a particular value. +void yaxis3At(picture pic=currentpicture, Label L="", axis axis, + real ymin=-infinity, real ymax=infinity, pen p=currentpen, + ticks3 ticks=NoTicks3, + arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=true, + bool opposite=false, bool opposite2=false, bool primary=true) +{ + int type=axis.type; + int type2=axis.type2; + triple dir=axis.align.dir3 == O ? + defaultdir(X,Z,Y,opposite^opposite2,currentprojection) : axis.align.dir3; + Label L=L.copy(); + if(L.align.dir3 == O && L.align.dir == 0) L.align(opposite ? -dir : dir); + + real x=axis.value; + real z=axis.value2; + real x2,z2; + int[] divisor=copy(axis.ydivisor); + + pic.add(new void(picture f, transform3 t, transform3 T, triple lb, + triple rt) { + transform3 tinv=inverse(t); + triple a=ymin == -infinity ? tinv*(xtrans(t,x),lb.y-min3(p).y, + ztrans(t,z)) : (x,ymin,z); + triple b=ymax == infinity ? tinv*(xtrans(t,x),rt.y-max3(p).y, + ztrans(t,z)) : (x,ymax,z); + real x0; + real z0; + if(abs(dir.x) < abs(dir.z)) { + x0=x; + z0=z2; + } else { + x0=x2; + z0=z; + } + + triple a2=ymin == -infinity ? tinv*(xtrans(t,x0),lb.y-min3(p).y, + ztrans(t,z0)) : (x0,ymin,z0); + triple b2=ymax == infinity ? tinv*(xtrans(t,x0),rt.y-max3(p).y, + ztrans(t,z0)) : (x0,ymax,z0); + + if(ymin == -infinity || ymax == infinity) { + bounds my=autoscale(a.y,b.y,pic.scale.y.scale); + pic.scale.y.tickMin=my.min; + pic.scale.y.tickMax=my.max; + divisor=my.divisor; + } + + triple fuzz=Y*epsilon*max(abs(a.y),abs(b.y)); + a -= fuzz; + b += fuzz; + + picture d; + ticks(d,t,L,a--b,finite(x0) && finite(z0) ? a2--b2 : nullpath3, + p,arrow,margin, + ticklocate(a.y,b.y,pic.scale.y,Dir(dir)),divisor, + opposite,primary); + add(f,t*T*tinv*d); + },above=above); + + void bounds() { + if(type == Min) + x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x; + else if(type == Max) + x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; + else if(type == Both) { + x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; + x=opposite ? x2 : + (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x); + } + + if(type2 == Min) + z=pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z; + else if(type2 == Max) + z=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; + else if(type2 == Both) { + z2=pic.scale.z.automax() ? tickMax3(pic).z : pic.userMax().z; + z=opposite2 ? z2 : + (pic.scale.z.automin() ? tickMin3(pic).z : pic.userMin().z); + } + + real Ymin=finite(ymin) ? ymin : pic.userMin().y; + real Ymax=finite(ymax) ? ymax : pic.userMax().y; + + triple a=(x,Ymin,z); + triple b=(x,Ymax,z); + triple a2=(x2,Ymin,z2); + triple b2=(x2,Ymax,z2); + + if(finite(a)) { + pic.addPoint(a,min3(p)); + pic.addPoint(a,max3(p)); + } + + if(finite(b)) { + pic.addPoint(b,min3(p)); + pic.addPoint(b,max3(p)); + } + + if(finite(a) && finite(b)) { + picture d; + ticks(d,pic.scaling3(warn=false),L, + (0,a.y,0)--(0,b.y,0),(0,a2.y,0)--(0,a2.y,0),p,arrow,margin, + ticklocate(a.y,b.y,pic.scale.y,Dir(dir)),divisor, + opposite,primary); + frame f; + if(L.s != "") { + Label L0=L.copy(); + L0.position(0); + add(f,L0); + } + triple pos=a+L.relative()*(b-a); + triple m=min3(d); + triple M=max3(d); + pic.addBox(pos,pos,(m.x,min3(f).y,m.z),(m.x,max3(f).y,m.z)); + } + } + + // Process any queued x and z axis bound calculation requests. + for(int i=0; i < pic.scale.x.bound.length; ++i) + pic.scale.x.bound[i](); + for(int i=0; i < pic.scale.z.bound.length; ++i) + pic.scale.z.bound[i](); + + pic.scale.x.bound.delete(); + pic.scale.z.bound.delete(); + + bounds(); + + // Request another y bounds calculation before final picture scaling. + pic.scale.y.bound.push(bounds); +} + +// An internal routine to draw a z axis at a particular value. +void zaxis3At(picture pic=currentpicture, Label L="", axis axis, + real zmin=-infinity, real zmax=infinity, pen p=currentpen, + ticks3 ticks=NoTicks3, + arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=true, + bool opposite=false, bool opposite2=false, bool primary=true) +{ + int type=axis.type; + int type2=axis.type2; + triple dir=axis.align.dir3 == O ? + defaultdir(X,Y,Z,opposite^opposite2,currentprojection) : axis.align.dir3; + Label L=L.copy(); + if(L.align.dir3 == O && L.align.dir == 0) L.align(opposite ? -dir : dir); + + real x=axis.value; + real y=axis.value2; + real x2,y2; + int[] divisor=copy(axis.zdivisor); + + pic.add(new void(picture f, transform3 t, transform3 T, triple lb, + triple rt) { + transform3 tinv=inverse(t); + triple a=zmin == -infinity ? tinv*(xtrans(t,x),ytrans(t,y), + lb.z-min3(p).z) : (x,y,zmin); + triple b=zmax == infinity ? tinv*(xtrans(t,x),ytrans(t,y), + rt.z-max3(p).z) : (x,y,zmax); + real x0; + real y0; + if(abs(dir.x) < abs(dir.y)) { + x0=x; + y0=y2; + } else { + x0=x2; + y0=y; + } + + triple a2=zmin == -infinity ? tinv*(xtrans(t,x0),ytrans(t,y0), + lb.z-min3(p).z) : (x0,y0,zmin); + triple b2=zmax == infinity ? tinv*(xtrans(t,x0),ytrans(t,y0), + rt.z-max3(p).z) : (x0,y0,zmax); + + if(zmin == -infinity || zmax == infinity) { + bounds mz=autoscale(a.z,b.z,pic.scale.z.scale); + pic.scale.z.tickMin=mz.min; + pic.scale.z.tickMax=mz.max; + divisor=mz.divisor; + } + + triple fuzz=Z*epsilon*max(abs(a.z),abs(b.z)); + a -= fuzz; + b += fuzz; + + picture d; + ticks(d,t,L,a--b,finite(x0) && finite(y0) ? a2--b2 : nullpath3, + p,arrow,margin, + ticklocate(a.z,b.z,pic.scale.z,Dir(dir)),divisor, + opposite,primary); + add(f,t*T*tinv*d); + },above=above); + + void bounds() { + if(type == Min) + x=pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x; + else if(type == Max) + x=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; + else if(type == Both) { + x2=pic.scale.x.automax() ? tickMax3(pic).x : pic.userMax().x; + x=opposite ? x2 : + (pic.scale.x.automin() ? tickMin3(pic).x : pic.userMin().x); + } + + if(type2 == Min) + y=pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y; + else if(type2 == Max) + y=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; + else if(type2 == Both) { + y2=pic.scale.y.automax() ? tickMax3(pic).y : pic.userMax().y; + y=opposite2 ? y2 : + (pic.scale.y.automin() ? tickMin3(pic).y : pic.userMin().y); + } + + real Zmin=finite(zmin) ? zmin : pic.userMin().z; + real Zmax=finite(zmax) ? zmax : pic.userMax().z; + + triple a=(x,y,Zmin); + triple b=(x,y,Zmax); + triple a2=(x2,y2,Zmin); + triple b2=(x2,y2,Zmax); + + if(finite(a)) { + pic.addPoint(a,min3(p)); + pic.addPoint(a,max3(p)); + } + + if(finite(b)) { + pic.addPoint(b,min3(p)); + pic.addPoint(b,max3(p)); + } + + if(finite(a) && finite(b)) { + picture d; + ticks(d,pic.scaling3(warn=false),L, + (0,0,a.z)--(0,0,b.z),(0,0,a2.z)--(0,0,a2.z),p,arrow,margin, + ticklocate(a.z,b.z,pic.scale.z,Dir(dir)),divisor, + opposite,primary); + frame f; + if(L.s != "") { + Label L0=L.copy(); + L0.position(0); + add(f,L0); + } + triple pos=a+L.relative()*(b-a); + triple m=min3(d); + triple M=max3(d); + pic.addBox(pos,pos,(m.x,m.y,min3(f).z),(m.x,m.y,max3(f).z)); + } + } + + // Process any queued x and y axes bound calculation requests. + for(int i=0; i < pic.scale.x.bound.length; ++i) + pic.scale.x.bound[i](); + for(int i=0; i < pic.scale.y.bound.length; ++i) + pic.scale.y.bound[i](); + + pic.scale.x.bound.delete(); + pic.scale.y.bound.delete(); + + bounds(); + + // Request another z bounds calculation before final picture scaling. + pic.scale.z.bound.push(bounds); +} + +// Internal routine to autoscale the user limits of a picture. +void autoscale3(picture pic=currentpicture, axis axis) +{ + bool set=pic.scale.set; + autoscale(pic,axis); + + if(!set) { + bounds mz; + if(pic.userSetz()) { + mz=autoscale(pic.userMin().z,pic.userMax().z,pic.scale.z.scale); + if(pic.scale.z.scale.logarithmic && + floor(pic.userMin().z) == floor(pic.userMax().z)) { + if(pic.scale.z.automin()) + pic.userMinz3(floor(pic.userMin().z)); + if(pic.scale.z.automax()) + pic.userMaxz3(ceil(pic.userMax().z)); + } + } else {mz.min=mz.max=0; pic.scale.set=false;} + + pic.scale.z.tickMin=mz.min; + pic.scale.z.tickMax=mz.max; + axis.zdivisor=mz.divisor; + } +} + +// Draw an x axis in three dimensions. +void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero, + real xmin=-infinity, real xmax=infinity, pen p=currentpen, + ticks3 ticks=NoTicks3, + arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=false) +{ + if(xmin > xmax) return; + + if(pic.scale.x.automin && xmin > -infinity) pic.scale.x.automin=false; + if(pic.scale.x.automax && xmax < infinity) pic.scale.x.automax=false; + + if(!pic.scale.set) { + axis(pic,axis); + autoscale3(pic,axis); + } + + bool newticks=false; + + if(xmin != -infinity) { + xmin=pic.scale.x.T(xmin); + newticks=true; + } + + if(xmax != infinity) { + xmax=pic.scale.x.T(xmax); + newticks=true; + } + + if(newticks && pic.userSetx() && ticks != NoTicks3) { + if(xmin == -infinity) xmin=pic.userMin().x; + if(xmax == infinity) xmax=pic.userMax().x; + bounds mx=autoscale(xmin,xmax,pic.scale.x.scale); + pic.scale.x.tickMin=mx.min; + pic.scale.x.tickMax=mx.max; + axis.xdivisor=mx.divisor; + } + + axis(pic,axis); + + if(xmin == -infinity && !axis.extend) { + if(pic.scale.set) + xmin=pic.scale.x.automin() ? pic.scale.x.tickMin : + max(pic.scale.x.tickMin,pic.userMin().x); + else xmin=pic.userMin().x; + } + + if(xmax == infinity && !axis.extend) { + if(pic.scale.set) + xmax=pic.scale.x.automax() ? pic.scale.x.tickMax : + min(pic.scale.x.tickMax,pic.userMax().x); + else xmax=pic.userMax().x; + } + + if(L.defaultposition) { + L=L.copy(); + L.position(axis.position); + } + + bool back=false; + if(axis.type == Both) { + triple v=currentprojection.normal; + back=dot((0,pic.userMax().y-pic.userMin().y,0),v)*sgn(v.z) > 0; + } + + xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,false,false,!back); + if(axis.type == Both) + xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,true,false,back); + if(axis.type2 == Both) { + xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,false,true,false); + if(axis.type == Both) + xaxis3At(pic,L,axis,xmin,xmax,p,ticks,arrow,margin,above,true,true,false); + } +} + +// Draw a y axis in three dimensions. +void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero, + real ymin=-infinity, real ymax=infinity, pen p=currentpen, + ticks3 ticks=NoTicks3, + arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=false) +{ + if(ymin > ymax) return; + + if(pic.scale.y.automin && ymin > -infinity) pic.scale.y.automin=false; + if(pic.scale.y.automax && ymax < infinity) pic.scale.y.automax=false; + + if(!pic.scale.set) { + axis(pic,axis); + autoscale3(pic,axis); + } + + bool newticks=false; + + if(ymin != -infinity) { + ymin=pic.scale.y.T(ymin); + newticks=true; + } + + if(ymax != infinity) { + ymax=pic.scale.y.T(ymax); + newticks=true; + } + + if(newticks && pic.userSety() && ticks != NoTicks3) { + if(ymin == -infinity) ymin=pic.userMin().y; + if(ymax == infinity) ymax=pic.userMax().y; + bounds my=autoscale(ymin,ymax,pic.scale.y.scale); + pic.scale.y.tickMin=my.min; + pic.scale.y.tickMax=my.max; + axis.ydivisor=my.divisor; + } + + axis(pic,axis); + + if(ymin == -infinity && !axis.extend) { + if(pic.scale.set) + ymin=pic.scale.y.automin() ? pic.scale.y.tickMin : + max(pic.scale.y.tickMin,pic.userMin().y); + else ymin=pic.userMin().y; + } + + + if(ymax == infinity && !axis.extend) { + if(pic.scale.set) + ymax=pic.scale.y.automax() ? pic.scale.y.tickMax : + min(pic.scale.y.tickMax,pic.userMax().y); + else ymax=pic.userMax().y; + } + + if(L.defaultposition) { + L=L.copy(); + L.position(axis.position); + } + + bool back=false; + if(axis.type == Both) { + triple v=currentprojection.normal; + back=dot((pic.userMax().x-pic.userMin().x,0,0),v)*sgn(v.z) > 0; + } + + yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,false,false,!back); + + if(axis.type == Both) + yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,true,false,back); + if(axis.type2 == Both) { + yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,false,true,false); + if(axis.type == Both) + yaxis3At(pic,L,axis,ymin,ymax,p,ticks,arrow,margin,above,true,true,false); + } +} +// Draw a z axis in three dimensions. +void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero, + real zmin=-infinity, real zmax=infinity, pen p=currentpen, + ticks3 ticks=NoTicks3, + arrowbar3 arrow=None, margin3 margin=NoMargin3, bool above=false) +{ + if(zmin > zmax) return; + + if(pic.scale.z.automin && zmin > -infinity) pic.scale.z.automin=false; + if(pic.scale.z.automax && zmax < infinity) pic.scale.z.automax=false; + + if(!pic.scale.set) { + axis(pic,axis); + autoscale3(pic,axis); + } + + bool newticks=false; + + if(zmin != -infinity) { + zmin=pic.scale.z.T(zmin); + newticks=true; + } + + if(zmax != infinity) { + zmax=pic.scale.z.T(zmax); + newticks=true; + } + + if(newticks && pic.userSetz() && ticks != NoTicks3) { + if(zmin == -infinity) zmin=pic.userMin().z; + if(zmax == infinity) zmax=pic.userMax().z; + bounds mz=autoscale(zmin,zmax,pic.scale.z.scale); + pic.scale.z.tickMin=mz.min; + pic.scale.z.tickMax=mz.max; + axis.zdivisor=mz.divisor; + } + + axis(pic,axis); + + if(zmin == -infinity && !axis.extend) { + if(pic.scale.set) + zmin=pic.scale.z.automin() ? pic.scale.z.tickMin : + max(pic.scale.z.tickMin,pic.userMin().z); + else zmin=pic.userMin().z; + } + + if(zmax == infinity && !axis.extend) { + if(pic.scale.set) + zmax=pic.scale.z.automax() ? pic.scale.z.tickMax : + min(pic.scale.z.tickMax,pic.userMax().z); + else zmax=pic.userMax().z; + } + + if(L.defaultposition) { + L=L.copy(); + L.position(axis.position); + } + + bool back=false; + if(axis.type == Both) { + triple v=currentprojection.vector(); + back=dot((pic.userMax().x-pic.userMin().x,0,0),v)*sgn(v.y) > 0; + } + + zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,false,false,!back); + if(axis.type == Both) + zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,true,false,back); + if(axis.type2 == Both) { + zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,false,true,false); + if(axis.type == Both) + zaxis3At(pic,L,axis,zmin,zmax,p,ticks,arrow,margin,above,true,true,false); + } +} + +// Set the z limits of a picture. +void zlimits(picture pic=currentpicture, real min=-infinity, real max=infinity, + bool crop=NoCrop) +{ + if(min > max) return; + + pic.scale.z.automin=min <= -infinity; + pic.scale.z.automax=max >= infinity; + + bounds mz; + if(pic.scale.z.automin() || pic.scale.z.automax()) + mz=autoscale(pic.userMin().z,pic.userMax().z,pic.scale.z.scale); + + if(pic.scale.z.automin) { + if(pic.scale.z.automin()) pic.userMinz(mz.min); + } else pic.userMinz(min(pic.scale.z.T(min),pic.scale.z.T(max))); + + if(pic.scale.z.automax) { + if(pic.scale.z.automax()) pic.userMaxz(mz.max); + } else pic.userMaxz(max(pic.scale.z.T(min),pic.scale.z.T(max))); +} + +// Restrict the x, y, and z limits to box(min,max). +void limits(picture pic=currentpicture, triple min, triple max) +{ + xlimits(pic,min.x,max.x); + ylimits(pic,min.y,max.y); + zlimits(pic,min.z,max.z); +} + +// Draw x, y and z axes. +void axes3(picture pic=currentpicture, + Label xlabel="", Label ylabel="", Label zlabel="", + bool extend=false, + triple min=(-infinity,-infinity,-infinity), + triple max=(infinity,infinity,infinity), + pen p=currentpen, arrowbar3 arrow=None, margin3 margin=NoMargin3) +{ + xaxis3(pic,xlabel,YZZero(extend),min.x,max.x,p,arrow,margin); + yaxis3(pic,ylabel,XZZero(extend),min.y,max.y,p,arrow,margin); + zaxis3(pic,zlabel,XYZero(extend),min.z,max.z,p,arrow,margin); +} + +triple Scale(picture pic=currentpicture, triple v) +{ + return (pic.scale.x.T(v.x),pic.scale.y.T(v.y),pic.scale.z.T(v.z)); +} + +triple[][] Scale(picture pic=currentpicture, triple[][] P) +{ + triple[][] Q=new triple[P.length][]; + for(int i=0; i < P.length; ++i) { + triple[] Pi=P[i]; + Q[i]=new triple[Pi.length]; + for(int j=0; j < Pi.length; ++j) + Q[i][j]=Scale(pic,Pi[j]); + } + return Q; +} + +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); +} + +real ScaleZ(picture pic=currentpicture, real z) +{ + return pic.scale.z.T(z); +} + +real[][] ScaleZ(picture pic=currentpicture, real[][] P) +{ + real[][] Q=new real[P.length][]; + for(int i=0; i < P.length; ++i) { + real[] Pi=P[i]; + Q[i]=new real[Pi.length]; + for(int j=0; j < Pi.length; ++j) + Q[i][j]=ScaleZ(pic,Pi[j]); + } + return Q; +} + +real[] uniform(real T(real x), real Tinv(real x), real a, real b, int n) +{ + return map(Tinv,uniform(T(a),T(b),n)); +} + +// Draw a tick of length size at triple v in direction dir using pen p. +void tick(picture pic=currentpicture, triple v, triple dir, real size=Ticksize, + pen p=currentpen) +{ + triple v=Scale(pic,v); + pic.add(new void (picture f, transform3 t) { + triple tv=t*v; + draw(f,tv--tv+unit(dir)*size,p); + }); + pic.addPoint(v,p); + pic.addPoint(v,unit(dir)*size,p); +} + +void xtick(picture pic=currentpicture, triple v, triple dir=Y, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,v,dir,size,p); +} + +void xtick3(picture pic=currentpicture, real x, triple dir=Y, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,(x,pic.scale.y.scale.logarithmic ? 1 : 0, + pic.scale.z.scale.logarithmic ? 1 : 0),dir,size,p); +} + +void ytick(picture pic=currentpicture, triple v, triple dir=X, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,v,dir,size,p); +} + +void ytick3(picture pic=currentpicture, real y, triple dir=X, + real size=Ticksize, pen p=currentpen) +{ + tick(pic,(pic.scale.x.scale.logarithmic ? 1 : 0,y, + pic.scale.z.scale.logarithmic ? 1 : 0),dir,size,p); +} + +void ztick(picture pic=currentpicture, triple v, triple dir=X, + real size=Ticksize, pen p=currentpen) +{ + xtick(pic,v,dir,size,p); +} + +void ztick3(picture pic=currentpicture, real z, triple dir=X, + real size=Ticksize, pen p=currentpen) +{ + xtick(pic,(pic.scale.x.scale.logarithmic ? 1 : 0, + pic.scale.y.scale.logarithmic ? 1 : 0,z),dir,size,p); +} + +void tick(picture pic=currentpicture, Label L, real value, triple v, + triple dir, string format="", real size=Ticksize, pen p=currentpen) +{ + Label L=L.copy(); + L.align(L.align,-dir); + if(shift(L.T3)*O == O) + L.T3=shift(dot(dir,L.align.dir3) > 0 ? dir*size : + ticklabelshift(L.align.dir3,p))*L.T3; + L.p(p); + if(L.s == "") L.s=format(format == "" ? defaultformat : format,value); + L.s=baseline(L.s,baselinetemplate); + label(pic,L,Scale(pic,v)); + tick(pic,v,dir,size,p); +} + +void xtick(picture pic=currentpicture, Label L, triple v, triple dir=Y, + string format="", real size=Ticksize, pen p=currentpen) +{ + tick(pic,L,v.x,v,dir,format,size,p); +} + +void xtick3(picture pic=currentpicture, Label L, real x, triple dir=Y, + string format="", real size=Ticksize, pen p=currentpen) +{ + xtick(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0, + pic.scale.z.scale.logarithmic ? 1 : 0),dir,size,p); +} + +void ytick(picture pic=currentpicture, Label L, triple v, triple dir=X, + string format="", real size=Ticksize, pen p=currentpen) +{ + tick(pic,L,v.y,v,dir,format,size,p); +} + +void ytick3(picture pic=currentpicture, Label L, real y, triple dir=X, + string format="", real size=Ticksize, pen p=currentpen) +{ + xtick(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y, + pic.scale.z.scale.logarithmic ? 1 : 0),dir,format,size,p); +} + +void ztick(picture pic=currentpicture, Label L, triple v, triple dir=X, + string format="", real size=Ticksize, pen p=currentpen) +{ + tick(pic,L,v.z,v,dir,format,size,p); +} + +void ztick3(picture pic=currentpicture, Label L, real z, triple dir=X, + string format="", real size=Ticksize, pen p=currentpen) +{ + xtick(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0, + pic.scale.z.scale.logarithmic ? 1 : 0,z),dir,format,size,p); +} + +private void label(picture pic, Label L, triple v, real x, align align, + string format, pen p) +{ + Label L=L.copy(); + L.align(align); + L.p(p); + if(shift(L.T3)*O == O) + L.T3=shift(ticklabelshift(L.align.dir3,L.p))*L.T3; + if(L.s == "") L.s=format(format == "" ? defaultformat : format,x); + L.s=baseline(L.s,baselinetemplate); + label(pic,L,v); +} + +void labelx(picture pic=currentpicture, Label L="", triple v, + align align=-Y, string format="", pen p=currentpen) +{ + label(pic,L,Scale(pic,v),v.x,align,format,p); +} + +void labelx3(picture pic=currentpicture, Label L="", real x, + align align=-Y, string format="", pen p=currentpen) +{ + labelx(pic,L,(x,pic.scale.y.scale.logarithmic ? 1 : 0, + pic.scale.z.scale.logarithmic ? 1 : 0),align,format,p); +} + +void labely(picture pic=currentpicture, Label L="", triple v, + align align=-X, string format="", pen p=currentpen) +{ + label(pic,L,Scale(pic,v),v.y,align,format,p); +} + +void labely3(picture pic=currentpicture, Label L="", real y, + align align=-X, string format="", pen p=currentpen) +{ + labely(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0,y, + pic.scale.z.scale.logarithmic ? 1 : 0),align,format,p); +} + +void labelz(picture pic=currentpicture, Label L="", triple v, + align align=-X, string format="", pen p=currentpen) +{ + label(pic,L,Scale(pic,v),v.z,align,format,p); +} + +void labelz3(picture pic=currentpicture, Label L="", real z, + align align=-X, string format="", pen p=currentpen) +{ + labelz(pic,L,(pic.scale.x.scale.logarithmic ? 1 : 0, + pic.scale.y.scale.logarithmic ? 1 : 0,z),align,format,p); +} + +typedef guide3 graph(triple F(real), real, real, int); +typedef guide3[] multigraph(triple F(real), real, real, int); + +graph graph(interpolate3 join) +{ + return new guide3(triple f(real), real a, real b, int n) { + real width=b-a; + return n == 0 ? join(f(a)) : + join(...sequence(new guide3(int i) {return f(a+(i/n)*width);},n+1)); + }; +} + +multigraph graph(interpolate3 join, bool3 cond(real)) +{ + return new guide3[](triple f(real), real a, real b, int n) { + real width=b-a; + if(n == 0) return new guide3[] {join(cond(a) ? f(a) : nullpath3)}; + guide3[] G; + guide3[] g; + for(int i=0; i < n+1; ++i) { + real t=a+(i/n)*width; + bool3 b=cond(t); + if(b) + g.push(f(t)); + else { + if(g.length > 0) { + G.push(join(...g)); + g=new guide3[] {}; + } + if(b == default) + g.push(f(t)); + } + } + if(g.length > 0) + G.push(join(...g)); + return G; + }; +} + +guide3 Straight(... guide3[])=operator --; +guide3 Spline(... guide3[])=operator ..; + +guide3 graph(picture pic=currentpicture, real x(real), real y(real), + real z(real), real a, real b, int n=ngraph, + interpolate3 join=operator --) +{ + return graph(join)(new triple(real t) {return Scale(pic,(x(t),y(t),z(t)));}, + a,b,n); +} + +guide3[] graph(picture pic=currentpicture, real x(real), real y(real), + real z(real), real a, real b, int n=ngraph, + bool3 cond(real), interpolate3 join=operator --) +{ + return graph(join,cond)(new triple(real t) { + return Scale(pic,(x(t),y(t),z(t))); + },a,b,n); +} + +guide3 graph(picture pic=currentpicture, triple v(real), real a, real b, + int n=ngraph, interpolate3 join=operator --) +{ + return graph(join)(new triple(real t) {return Scale(pic,v(t));},a,b,n); +} + +guide3[] graph(picture pic=currentpicture, triple v(real), real a, real b, + int n=ngraph, bool3 cond(real), interpolate3 join=operator --) +{ + return graph(join,cond)(new triple(real t) { + return Scale(pic,v(t)); + },a,b,n); +} + +guide3 graph(picture pic=currentpicture, triple[] v, + interpolate3 join=operator --) +{ + int i=0; + return graph(join)(new triple(real) { + triple w=Scale(pic,v[i]); + ++i; + return w; + },0,0,v.length-1); +} + +guide3[] graph(picture pic=currentpicture, triple[] v, bool3[] cond, + interpolate3 join=operator --) +{ + int n=v.length; + int i=0; + triple w; + checkconditionlength(cond.length,n); + bool3 condition(real) { + bool b=cond[i]; + if(b) w=Scale(pic,v[i]); + ++i; + return b; + } + return graph(join,condition)(new triple(real) {return w;},0,0,n-1); +} + +guide3 graph(picture pic=currentpicture, real[] x, real[] y, real[] z, + interpolate3 join=operator --) +{ + int n=x.length; + checklengths(n,y.length); + checklengths(n,z.length); + int i=0; + return graph(join)(new triple(real) { + triple w=Scale(pic,(x[i],y[i],z[i])); + ++i; + return w; + },0,0,n-1); +} + +guide3[] graph(picture pic=currentpicture, real[] x, real[] y, real[] z, + bool3[] cond, interpolate3 join=operator --) +{ + int n=x.length; + checklengths(n,y.length); + checklengths(n,z.length); + int i=0; + triple w; + checkconditionlength(cond.length,n); + bool3 condition(real) { + bool3 b=cond[i]; + if(b != false) w=Scale(pic,(x[i],y[i],z[i])); + ++i; + return b; + } + return graph(join,condition)(new triple(real) {return w;},0,0,n-1); +} + +// The graph of a function along a path. +guide3 graph(triple F(path, real), path p, int n=1, + interpolate3 join=operator --) +{ + guide3 g=join(...sequence(new guide3(int i) { + return F(p,i/n); + },n*length(p))); + return cyclic(p) ? join(g,cycle) : join(g,F(p,length(p))); +} + +guide3 graph(triple F(pair), path p, int n=1, interpolate3 join=operator --) +{ + return graph(new triple(path p, real position) + {return F(point(p,position));},p,n,join); +} + +guide3 graph(picture pic=currentpicture, real f(pair), path p, int n=1, + interpolate3 join=operator --) +{ + return graph(new triple(pair z) {return Scale(pic,(z.x,z.y,f(z)));},p,n, + join); +} + +guide3 graph(real f(pair), path p, int n=1, real T(pair), + interpolate3 join=operator --) +{ + return graph(new triple(pair z) {pair w=T(z); return (w.x,w.y,f(w));},p,n, + join); +} + +// Connect points in v into segments corresponding to consecutive true elements +// of b using interpolation operator join. +path3[] segment(triple[] v, bool[] cond, interpolate3 join=operator --) +{ + checkconditionlength(cond.length,v.length); + int[][] segment=segment(cond); + return sequence(new path3(int i) {return join(...v[segment[i]]);}, + segment.length); +} + +bool uperiodic(real[][] a) { + int n=a.length; + if(n == 0) return false; + int m=a[0].length; + real[] a0=a[0]; + real[] a1=a[n-1]; + for(int j=0; j < m; ++j) { + real norm=0; + for(int i=0; i < n; ++i) + norm=max(norm,abs(a[i][j])); + real epsilon=sqrtEpsilon*norm; + if(abs(a0[j]-a1[j]) > epsilon) return false; + } + return true; +} +bool vperiodic(real[][] a) { + int n=a.length; + if(n == 0) return false; + int m=a[0].length-1; + for(int i=0; i < n; ++i) { + real[] ai=a[i]; + real epsilon=sqrtEpsilon*norm(ai); + if(abs(ai[0]-ai[m]) > epsilon) return false; + } + return true; +} + +bool uperiodic(triple[][] a) { + int n=a.length; + if(n == 0) return false; + int m=a[0].length; + triple[] a0=a[0]; + triple[] a1=a[n-1]; + real epsilon=sqrtEpsilon*norm(a); + for(int j=0; j < m; ++j) + if(abs(a0[j]-a1[j]) > epsilon) return false; + return true; +} +bool vperiodic(triple[][] a) { + int n=a.length; + if(n == 0) return false; + int m=a[0].length-1; + real epsilon=sqrtEpsilon*norm(a); + for(int i=0; i < n; ++i) + if(abs(a[i][0]-a[i][m]) > epsilon) return false; + return true; +} + +// return the surface described by a matrix f +surface surface(picture pic=currentpicture, triple[][] f, bool[][] cond={}) +{ + if(!rectangular(f)) abort("matrix is not rectangular"); + + int nx=f.length-1; + int ny=nx > 0 ? f[0].length-1 : 0; + + bool all=cond.length == 0; + + int count; + if(all) + count=nx*ny; + else { + count=0; + for(int i=0; i < nx; ++i) { + bool[] condi=cond[i]; + bool[] condp=cond[i+1]; + for(int j=0; j < ny; ++j) + if(condi[j] && condi[j+1] && condp[j] && condp[j+1]) ++count; + } + } + + surface s=surface(count); + s.index=new int[nx][ny]; + int k=-1; + for(int i=0; i < nx; ++i) { + bool[] condi,condp; + if(!all) { + condi=cond[i]; + condp=cond[i+1]; + } + triple[] fi=f[i]; + triple[] fp=f[i+1]; + int[] indexi=s.index[i]; + for(int j=0; j < ny; ++j) { + if(all || (condi[j] && condi[j+1] && condp[j] && condp[j+1])) + s.s[++k]=patch(new triple[] { + Scale(pic,fi[j]), + Scale(pic,fp[j]), + Scale(pic,fp[j+1]), + Scale(pic,fi[j+1])}); + indexi[j]=k; + } + } + + if(count == nx*ny) { + if(uperiodic(f)) s.ucyclic(true); + if(vperiodic(f)) s.vcyclic(true); + } + + return s; +} + +surface bispline(real[][] z, real[][] p, real[][] q, real[][] r, + real[] x, real[] y, bool[][] cond={}) +{ // z[i][j] is the value at (x[i],y[j]) + // p and q are the first derivatives with respect to x and y, respectively + // r is the second derivative ddu/dxdy + int n=x.length-1; + int m=y.length-1; + + bool all=cond.length == 0; + + int count; + if(all) + count=n*m; + else { + count=0; + for(int i=0; i < n; ++i) { + bool[] condi=cond[i]; + for(int j=0; j < m; ++j) + if(condi[j]) ++count; + } + } + + surface s=surface(count); + s.index=new int[n][m]; + int k=0; + for(int i=0; i < n; ++i) { + int ip=i+1; + real xi=x[i]; + real xp=x[ip]; + real x1=interp(xi,xp,1/3); + real x2=interp(xi,xp,2/3); + real hx=x1-xi; + real[] zi=z[i]; + real[] zp=z[ip]; + real[] ri=r[i]; + real[] rp=r[ip]; + real[] pi=p[i]; + real[] pp=p[ip]; + real[] qi=q[i]; + real[] qp=q[ip]; + int[] indexi=s.index[i]; + bool[] condi=all ? null : cond[i]; + for(int j=0; j < m; ++j) { + if(all || condi[j]) { + real yj=y[j]; + int jp=j+1; + real yp=y[jp]; + real y1=interp(yj,yp,1/3); + real y2=interp(yj,yp,2/3); + real hy=y1-yj; + real hxy=hx*hy; + real zij=zi[j]; + real zip=zi[jp]; + real zpj=zp[j]; + real zpp=zp[jp]; + real pij=hx*pi[j]; + real ppj=hx*pp[j]; + real qip=hy*qi[jp]; + real qpp=hy*qp[jp]; + real zippip=zip+hx*pi[jp]; + real zppmppp=zpp-hx*pp[jp]; + real zijqij=zij+hy*qi[j]; + real zpjqpj=zpj+hy*qp[j]; + + s.s[k]=patch(new triple[][] { + {(xi,yj,zij),(xi,y1,zijqij),(xi,y2,zip-qip),(xi,yp,zip)}, + {(x1,yj,zij+pij),(x1,y1,zijqij+pij+hxy*ri[j]), + (x1,y2,zippip-qip-hxy*ri[jp]),(x1,yp,zippip)}, + {(x2,yj,zpj-ppj),(x2,y1,zpjqpj-ppj-hxy*rp[j]), + (x2,y2,zppmppp-qpp+hxy*rp[jp]),(x2,yp,zppmppp)}, + {(xp,yj,zpj),(xp,y1,zpjqpj),(xp,y2,zpp-qpp),(xp,yp,zpp)}}, + copy=false); + indexi[j]=k; + ++k; + } + } + } + + return s; +} + +private real[][][] bispline0(real[][] z, real[][] p, real[][] q, real[][] r, + real[] x, real[] y, bool[][] cond={}) +{ // z[i][j] is the value at (x[i],y[j]) + // p and q are the first derivatives with respect to x and y, respectively + // r is the second derivative ddu/dxdy + int n=x.length-1; + int m=y.length-1; + + bool all=cond.length == 0; + + int count; + if(all) + count=n*m; + else { + count=0; + for(int i=0; i < n; ++i) { + bool[] condi=cond[i]; + bool[] condp=cond[i+1]; + for(int j=0; j < m; ++j) + if(all || (condi[j] && condi[j+1] && condp[j] && condp[j+1])) + ++count; + } + } + + real[][][] s=new real[count][][]; + int k=0; + for(int i=0; i < n; ++i) { + int ip=i+1; + real xi=x[i]; + real xp=x[ip]; + real hx=(xp-xi)/3; + real[] zi=z[i]; + real[] zp=z[ip]; + real[] ri=r[i]; + real[] rp=r[ip]; + real[] pi=p[i]; + real[] pp=p[ip]; + real[] qi=q[i]; + real[] qp=q[ip]; + bool[] condi=all ? null : cond[i]; + bool[] condp=all ? null : cond[i+1]; + for(int j=0; j < m; ++j) { + if(all || (condi[j] && condi[j+1] && condp[j] && condp[j+1])) { + real yj=y[j]; + int jp=j+1; + real yp=y[jp]; + real hy=(yp-yj)/3; + real hxy=hx*hy; + real zij=zi[j]; + real zip=zi[jp]; + real zpj=zp[j]; + real zpp=zp[jp]; + real pij=hx*pi[j]; + real ppj=hx*pp[j]; + real qip=hy*qi[jp]; + real qpp=hy*qp[jp]; + real zippip=zip+hx*pi[jp]; + real zppmppp=zpp-hx*pp[jp]; + real zijqij=zij+hy*qi[j]; + real zpjqpj=zpj+hy*qp[j]; + + s[k]=new real[][] {{zij,zijqij,zip-qip,zip}, + {zij+pij,zijqij+pij+hxy*ri[j], + zippip-qip-hxy*ri[jp],zippip}, + {zpj-ppj,zpjqpj-ppj-hxy*rp[j], + zppmppp-qpp+hxy*rp[jp],zppmppp}, + {zpj,zpjqpj,zpp-qpp,zpp}}; + ++k; + } + } + } + + return s; +} + +// return the surface values described by a real matrix f, interpolated with +// xsplinetype and ysplinetype. +real[][][] bispline(real[][] f, real[] x, real[] y, + splinetype xsplinetype=null, + splinetype ysplinetype=xsplinetype, bool[][] cond={}) +{ + real epsilon=sqrtEpsilon*norm(y); + if(xsplinetype == null) + xsplinetype=(abs(x[0]-x[x.length-1]) <= epsilon) ? periodic : notaknot; + if(ysplinetype == null) + ysplinetype=(abs(y[0]-y[y.length-1]) <= epsilon) ? periodic : notaknot; + int n=x.length; int m=y.length; + real[][] ft=transpose(f); + real[][] tp=new real[m][]; + for(int j=0; j < m; ++j) + tp[j]=xsplinetype(x,ft[j]); + real[][] q=new real[n][]; + for(int i=0; i < n; ++i) + q[i]=ysplinetype(y,f[i]); + real[][] qt=transpose(q); + real[] d1=xsplinetype(x,qt[0]); + real[] d2=xsplinetype(x,qt[m-1]); + real[][] r=new real[n][]; + real[][] p=transpose(tp); + for(int i=0; i < n; ++i) + r[i]=clamped(d1[i],d2[i])(y,p[i]); + return bispline0(f,p,q,r,x,y,cond); +} + +// return the surface described by a real matrix f, interpolated with +// xsplinetype and ysplinetype. +surface surface(picture pic=currentpicture, real[][] f, real[] x, real[] y, + splinetype xsplinetype=null, + splinetype ysplinetype=xsplinetype, + bool[][] cond={}) +{ + real[][] f=ScaleZ(pic,f); + real[] x=map(pic.scale.x.T,x); + real[] y=map(pic.scale.y.T,y); + + real epsilon=sqrtEpsilon*norm(y); + if(xsplinetype == null) + xsplinetype=(abs(x[0]-x[x.length-1]) <= epsilon) ? periodic : notaknot; + if(ysplinetype == null) + ysplinetype=(abs(y[0]-y[y.length-1]) <= epsilon) ? periodic : notaknot; + int n=x.length; int m=y.length; + real[][] ft=transpose(f); + real[][] tp=new real[m][]; + for(int j=0; j < m; ++j) + tp[j]=xsplinetype(x,ft[j]); + real[][] q=new real[n][]; + for(int i=0; i < n; ++i) + q[i]=ysplinetype(y,f[i]); + real[][] qt=transpose(q); + real[] d1=xsplinetype(x,qt[0]); + real[] d2=xsplinetype(x,qt[m-1]); + real[][] r=new real[n][]; + real[][] p=transpose(tp); + for(int i=0; i < n; ++i) + r[i]=clamped(d1[i],d2[i])(y,p[i]); + surface s=bispline(f,p,q,r,x,y,cond); + if(xsplinetype == periodic) s.ucyclic(true); + if(ysplinetype == periodic) s.vcyclic(true); + return s; +} + +// return the surface described by a real matrix f, interpolated with +// xsplinetype and ysplinetype. +surface surface(picture pic=currentpicture, real[][] f, pair a, pair b, + splinetype xsplinetype, splinetype ysplinetype=xsplinetype, + bool[][] cond={}) +{ + if(!rectangular(f)) abort("matrix is not rectangular"); + + int nx=f.length-1; + int ny=nx > 0 ? f[0].length-1 : 0; + + if(nx == 0 || ny == 0) return nullsurface; + + real[] x=uniform(pic.scale.x.T,pic.scale.x.Tinv,a.x,b.x,nx); + real[] y=uniform(pic.scale.y.T,pic.scale.y.Tinv,a.y,b.y,ny); + return surface(pic,f,x,y,xsplinetype,ysplinetype,cond); +} + +// return the surface described by a real matrix f, interpolated linearly. +surface surface(picture pic=currentpicture, 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]; + + pair a=Scale(pic,a); + pair b=Scale(pic,b); + for(int i=0; i <= nx; ++i) { + real x=pic.scale.x.Tinv(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,pic.scale.y.Tinv(interp(a.y,b.y,j/ny)),fi[j]); + } + return surface(pic,v,cond); +} + +// return the surface described by a parametric function f over box(a,b), +// interpolated linearly. +surface surface(picture pic=currentpicture, 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]; + + pair a=Scale(pic,a); + pair b=Scale(pic,b); + for(int i=0; i <= nu; ++i) { + real x=pic.scale.x.Tinv(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,pic.scale.y.Tinv(interp(a.y,b.y,j*dv))); + if(all || (activei[j]=cond(z))) vi[j]=f(z); + } + } + return surface(pic,v,active); +} + +// return the surface described by a parametric function f evaluated at u and v +// and interpolated with usplinetype and vsplinetype. +surface surface(picture pic=currentpicture, triple f(pair z), + real[] u, real[] v, splinetype[] usplinetype, + splinetype[] vsplinetype=Spline, bool cond(pair z)=null) +{ + int nu=u.length-1; + int nv=v.length-1; + real[] ipt=sequence(u.length); + real[] jpt=sequence(v.length); + real[][] fx=new real[u.length][v.length]; + real[][] fy=new real[u.length][v.length]; + real[][] fz=new real[u.length][v.length]; + + bool[][] active; + bool all=cond == null; + if(!all) active=new bool[u.length][v.length]; + + for(int i=0; i <= nu; ++i) { + real ui=u[i]; + real[] fxi=fx[i]; + real[] fyi=fy[i]; + real[] fzi=fz[i]; + bool[] activei=all ? null : active[i]; + for(int j=0; j <= nv; ++j) { + pair z=(ui,v[j]); + if(!all) activei[j]=cond(z); + triple f=Scale(pic,f(z)); + fxi[j]=f.x; + fyi[j]=f.y; + fzi[j]=f.z; + } + } + + if(usplinetype.length == 0) { + usplinetype=new splinetype[] {uperiodic(fx) ? periodic : notaknot, + uperiodic(fy) ? periodic : notaknot, + uperiodic(fz) ? periodic : notaknot}; + } else if(usplinetype.length != 3) abort("usplinetype must have length 3"); + + if(vsplinetype.length == 0) { + vsplinetype=new splinetype[] {vperiodic(fx) ? periodic : notaknot, + vperiodic(fy) ? periodic : notaknot, + vperiodic(fz) ? periodic : notaknot}; + } else if(vsplinetype.length != 3) abort("vsplinetype must have length 3"); + + real[][][] sx=bispline(fx,ipt,jpt,usplinetype[0],vsplinetype[0],active); + real[][][] sy=bispline(fy,ipt,jpt,usplinetype[1],vsplinetype[1],active); + real[][][] sz=bispline(fz,ipt,jpt,usplinetype[2],vsplinetype[2],active); + + surface s=surface(sx.length); + s.index=new int[nu][nv]; + int k=-1; + for(int i=0; i < nu; ++i) { + int[] indexi=s.index[i]; + for(int j=0; j < nv; ++j) + indexi[j]=++k; + } + + for(int k=0; k < sx.length; ++k) { + triple[][] Q=new triple[4][]; + real[][] Px=sx[k]; + real[][] Py=sy[k]; + real[][] Pz=sz[k]; + for(int i=0; i < 4 ; ++i) { + real[] Pxi=Px[i]; + real[] Pyi=Py[i]; + real[] Pzi=Pz[i]; + Q[i]=new triple[] {(Pxi[0],Pyi[0],Pzi[0]), + (Pxi[1],Pyi[1],Pzi[1]), + (Pxi[2],Pyi[2],Pzi[2]), + (Pxi[3],Pyi[3],Pzi[3])}; + } + s.s[k]=patch(Q); + } + + if(usplinetype[0] == periodic && usplinetype[1] == periodic && + usplinetype[1] == periodic) s.ucyclic(true); + + if(vsplinetype[0] == periodic && vsplinetype[1] == periodic && + vsplinetype[1] == periodic) s.vcyclic(true); + + return s; +} + +// return the surface described by a parametric function f over box(a,b), +// interpolated with usplinetype and vsplinetype. +surface surface(picture pic=currentpicture, triple f(pair z), pair a, pair b, + int nu=nmesh, int nv=nu, + splinetype[] usplinetype, splinetype[] vsplinetype=Spline, + bool cond(pair z)=null) +{ + real[] x=uniform(pic.scale.x.T,pic.scale.x.Tinv,a.x,b.x,nu); + real[] y=uniform(pic.scale.y.T,pic.scale.y.Tinv,a.y,b.y,nv); + return surface(pic,f,x,y,usplinetype,vsplinetype,cond); +} + +// return the surface described by a real function f over box(a,b), +// interpolated linearly. +surface surface(picture pic=currentpicture, real f(pair z), pair a, pair b, + int nx=nmesh, int ny=nx, bool cond(pair z)=null) +{ + return surface(pic,new triple(pair z) {return (z.x,z.y,f(z));},a,b,nx,ny, + cond); +} + +// return the surface described by a real function f over box(a,b), +// interpolated with xsplinetype and ysplinetype. +surface surface(picture pic=currentpicture, real f(pair z), pair a, pair b, + int nx=nmesh, int ny=nx, splinetype xsplinetype, + splinetype ysplinetype=xsplinetype, bool cond(pair z)=null) +{ + bool[][] active; + bool all=cond == null; + if(!all) active=new bool[nx+1][ny+1]; + + real dx=1/nx; + real dy=1/ny; + pair Idy=(0,dy); + pair dz=(dx,dy); + + real[][] F=new real[nx+1][ny+1]; + real[] x=uniform(pic.scale.x.T,pic.scale.x.Tinv,a.x,b.x,nx); + real[] y=uniform(pic.scale.y.T,pic.scale.y.Tinv,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(pic,F,x,y,xsplinetype,ysplinetype,active); +} + +guide3[][] lift(real f(real x, real y), guide[][] g, + interpolate3 join=operator --) +{ + guide3[][] G=new guide3[g.length][]; + for(int cnt=0; cnt < g.length; ++cnt) { + guide[] gcnt=g[cnt]; + guide3[] Gcnt=new guide3[gcnt.length]; + for(int i=0; i < gcnt.length; ++i) { + guide gcnti=gcnt[i]; + guide3 Gcnti=join(...sequence(new guide3(int j) { + pair z=point(gcnti,j); + return (z.x,z.y,f(z.x,z.y)); + },size(gcnti))); + if(cyclic(gcnti)) Gcnti=Gcnti..cycle; + Gcnt[i]=Gcnti; + } + G[cnt]=Gcnt; + } + return G; +} + +guide3[][] lift(real f(pair z), guide[][] g, interpolate3 join=operator --) +{ + return lift(new real(real x, real y) {return f((x,y));},g,join); +} + +void draw(picture pic=currentpicture, Label[] L=new Label[], + guide3[][] g, pen[] p, light light=currentlight, string name="", + render render=defaultrender, + interaction interaction=LabelInteraction()) +{ + pen thin=is3D() ? thin() : defaultpen; + bool group=g.length > 1 && (name != "" || render.defaultnames); + if(group) + begingroup3(pic,name == "" ? "contours" : name,render); + for(int cnt=0; cnt < g.length; ++cnt) { + guide3[] gcnt=g[cnt]; + pen pcnt=thin+p[cnt]; + for(int i=0; i < gcnt.length; ++i) + draw(pic,gcnt[i],pcnt,light,name); + if(L.length > 0) { + Label Lcnt=L[cnt]; + for(int i=0; i < gcnt.length; ++i) { + if(Lcnt.s != "" && size(gcnt[i]) > 1) + label(pic,Lcnt,gcnt[i],pcnt,name,interaction); + } + } + } + if(group) + endgroup3(pic); +} + +void draw(picture pic=currentpicture, Label[] L=new Label[], + guide3[][] g, pen p=currentpen, light light=currentlight, + string name="", render render=defaultrender, + interaction interaction=LabelInteraction()) +{ + draw(pic,L,g,sequence(new pen(int) {return p;},g.length),light,name, + render,interaction); +} + +real maxlength(triple f(pair z), pair a, pair b, int nu, int nv) +{ + return min(abs(f((b.x,a.y))-f(a))/nu,abs(f((a.x,b.y))-f(a))/nv); +} + +// return a vector field on a parametric surface f over box(a,b). +picture vectorfield(path3 vector(pair v), triple f(pair z), pair a, pair b, + int nu=nmesh, int nv=nu, bool truesize=false, + real maxlength=truesize ? 0 : maxlength(f,a,b,nu,nv), + bool cond(pair z)=null, pen p=currentpen, + arrowbar3 arrow=Arrow3, margin3 margin=PenMargin3, + string name="", render render=defaultrender) +{ + picture pic; + real du=1/nu; + real dv=1/nv; + bool all=cond == null; + real scale; + + if(maxlength > 0) { + real size(pair z) { + path3 g=vector(z); + return abs(point(g,size(g)-1)-point(g,0)); + } + real max=size((0,0)); + for(int i=0; i <= nu; ++i) { + real x=interp(a.x,b.x,i*du); + for(int j=0; j <= nv; ++j) + max=max(max,size((x,interp(a.y,b.y,j*dv)))); + } + scale=max > 0 ? maxlength/max : 1; + } else scale=1; + + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(pic,name == "" ? "vectorfield" : name,render); + for(int i=0; i <= nu; ++i) { + real x=interp(a.x,b.x,i*du); + for(int j=0; j <= nv; ++j) { + pair z=(x,interp(a.y,b.y,j*dv)); + if(all || cond(z)) { + path3 g=scale3(scale)*vector(z); + string name="vector"; + if(truesize) { + picture opic; + draw(opic,g,p,arrow,margin,name,render); + add(pic,opic,f(z)); + } else + draw(pic,shift(f(z))*g,p,arrow,margin,name,render); + } + } + } + if(group) + endgroup3(pic); + return pic; +} + +triple polar(real r, real theta, real phi) +{ + return r*expi(theta,phi); +} + +guide3 polargraph(real r(real,real), real theta(real), real phi(real), + int n=ngraph, interpolate3 join=operator --) +{ + return graph(join)(new triple(real t) { + return polar(r(theta(t),phi(t)),theta(t),phi(t)); + },0,1,n); +} + +// True arc +path3 Arc(triple c, triple v1, triple v2, triple normal=O, bool direction=CCW, + int n=nCircle) +{ + v1 -= c; + real r=abs(v1); + v1=unit(v1); + v2=unit(v2-c); + + if(normal == O) { + normal=cross(v1,v2); + if(normal == O) abort("explicit normal required for these endpoints"); + } + + transform3 T=align(unit(normal)); + transform3 Tinv=transpose(T); + v1=Tinv*v1; + v2=Tinv*v2; + + real fuzz=sqrtEpsilon*max(abs(v1),abs(v2)); + if(abs(v1.z) > fuzz || abs(v2.z) > fuzz) + abort("invalid normal vector"); + + real phi1=radians(longitude(v1,warn=false)); + real phi2=radians(longitude(v2,warn=false)); + if(direction) { + if(phi1 >= phi2) phi1 -= 2pi; + } else if(phi2 >= phi1) phi2 -= 2pi; + + static real piby2=pi/2; + return shift(c)*T*polargraph(new real(real theta, real phi) {return r;}, + new real(real t) {return piby2;}, + new real(real t) {return interp(phi1,phi2,t);}, + n,operator ..); +} + +path3 Arc(triple c, real r, real theta1, real phi1, real theta2, real phi2, + triple normal=O, bool direction, int n=nCircle) +{ + return Arc(c,c+r*dir(theta1,phi1),c+r*dir(theta2,phi2),normal,direction,n); +} + +path3 Arc(triple c, real r, real theta1, real phi1, real theta2, real phi2, + triple normal=O, int n=nCircle) +{ + return Arc(c,r,theta1,phi1,theta2,phi2,normal, + theta2 > theta1 || (theta2 == theta1 && phi2 >= phi1) ? CCW : CW, + n); +} + +// True circle +path3 Circle(triple c, real r, triple normal=Z, int n=nCircle) +{ + static real piby2=pi/2; + return shift(c)*align(unit(normal))* + polargraph(new real(real theta, real phi) {return r;}, + new real(real t) {return piby2;}, + new real(real t) {return interp(0,2pi,t);},n,operator ..); + +} diff --git a/Build/source/utils/asymptote/base/graph_settings.asy b/Build/source/utils/asymptote/base/graph_settings.asy new file mode 100644 index 00000000000..f5d04d0797b --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/graph_splinetype.asy b/Build/source/utils/asymptote/base/graph_splinetype.asy new file mode 100644 index 00000000000..77e459d47e2 --- /dev/null +++ b/Build/source/utils/asymptote/base/graph_splinetype.asy @@ -0,0 +1,264 @@ +private import math; + +typedef real[] splinetype(real[], real[]); + +restricted real[] Spline(real[] x, real[] y); +restricted splinetype[] Spline; + +string morepoints="interpolation requires at least 2 points"; +string differentlengths="arrays have different lengths"; +void checklengths(int x, int y, string text=differentlengths) +{ + if(x != y) + abort(text+": "+string(x)+" != "+string(y)); +} + +void checkincreasing(real[] x) +{ + if(!increasing(x,true)) + abort("strictly increasing array expected"); +} + +// Linear interpolation +real[] linear(real[] x, real[] y) +{ + int n=x.length; + checklengths(n,y.length); + real[] d=new real[n]; + for(int i=0; i < n-1; ++i) + d[i]=(y[i+1]-y[i])/(x[i+1]-x[i]); + d[n-1]=d[n-2]; + return d; +} + +// Standard cubic spline interpolation with not-a-knot condition: +// s'''(x_2^-)=s'''(x_2^+) et s'''(x_(n_2)^-)=s'''(x_(n-2)^+) +// if n=2, linear interpolation is returned +// if n=3, an interpolation polynomial of degree <= 2 is returned: +// p(x_1)=y_1, p(x_2)=y_2, p(x_3)=y_3 +real[] notaknot(real[] x, real[] y) +{ + int n=x.length; + checklengths(n,y.length); + checkincreasing(x); + real[] d; + if(n > 3) { + real[] a=new real[n]; + real[] b=new real[n]; + real[] c=new real[n]; + real[] g=new real[n]; + b[0]=x[2]-x[1]; + c[0]=x[2]-x[0]; + a[0]=0; + g[0]=((x[1]-x[0])^2*(y[2]-y[1])/b[0]+b[0]*(2*b[0]+3*(x[1]-x[0]))* + (y[1]-y[0])/(x[1]-x[0]))/c[0]; + for(int i=1; i < n-1; ++i) { + a[i]=x[i+1]-x[i]; + c[i]=x[i]-x[i-1]; + b[i]=2*(a[i]+c[i]); + g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]); + } + c[n-1]=0; + b[n-1]=x[n-2]-x[n-3]; + a[n-1]=x[n-1]-x[n-3]; + g[n-1]=((x[n-1]-x[n-2])^2*(y[n-2]-y[n-3])/b[n-1]+ + b[n-1]*(2*b[n-1]+3(x[n-1]-x[n-2]))* + (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))/a[n-1]; + d=tridiagonal(a,b,c,g); + } else if(n == 2) { + real val=(y[1]-y[0])/(x[1]-x[0]); + d=new real[] {val,val}; + } else if(n == 3) { + real a=(y[1]-y[0])/(x[1]-x[0]); + real b=(y[2]-y[1])/(x[2]-x[1]); + real c=(b-a)/(x[2]-x[0]); + d=new real[] {a+c*(x[0]-x[1]),a+c*(x[1]-x[0]),a+c*(2*x[2]-x[0]-x[1])}; + } else abort(morepoints); + return d; +} + +// Standard cubic spline interpolation with periodic condition +// s'(a)=s'(b), s''(a)=s''(b), assuming that f(a)=f(b) +// if n=2, linear interpolation is returned +real[] periodic(real[] x, real[] y) +{ + int n=x.length; + checklengths(n,y.length); + checkincreasing(x); + if(abs(y[n-1]-y[0]) > sqrtEpsilon*norm(y)) + abort("function values are not periodic"); + real[] d; + if(n > 2) { + real[] a=new real[n-1]; + real[] b=new real[n-1]; + real[] c=new real[n-1]; + real[] g=new real[n-1]; + c[0]=x[n-1]-x[n-2]; + a[0]=x[1]-x[0]; + b[0]=2*(a[0]+c[0]); + g[0]=3*c[0]*(y[1]-y[0])/a[0]+3*a[0]*(y[n-1]-y[n-2])/c[0]; + for(int i=1; i < n-1; ++i) { + a[i]=x[i+1]-x[i]; + c[i]=x[i]-x[i-1]; + b[i]=2*(a[i]+c[i]); + g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]); + } + d=tridiagonal(a,b,c,g); + d.push(d[0]); + } else if(n == 2) { + d=new real[] {0,0}; + } else abort(morepoints); + return d; +} + +// Standard cubic spline interpolation with the natural condition +// s''(a)=s''(b)=0. +// if n=2, linear interpolation is returned +// Don't use the natural type unless the underlying function +// has zero second end points derivatives. +real[] natural(real[] x, real[] y) +{ + int n=x.length; + checklengths(n,y.length); + checkincreasing(x); + real[] d; + if(n > 2) { + real[] a=new real[n]; + real[] b=new real[n]; + real[] c=new real[n]; + real[] g=new real[n]; + b[0]=2*(x[1]-x[0]); + c[0]=x[1]-x[0]; + a[0]=0; + g[0]=3*(y[1]-y[0]); + for(int i=1; i < n-1; ++i) { + a[i]=x[i+1]-x[i]; + c[i]=x[i]-x[i-1]; + b[i]=2*(a[i]+c[i]); + g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]); + } + c[n-1]=0; + a[n-1]=x[n-1]-x[n-2]; + b[n-1]=2*a[n-1]; + g[n-1]=3*(y[n-1]-y[n-2]); + d=tridiagonal(a,b,c,g); + } else if(n == 2) { + real val=(y[1]-y[0])/(x[1]-x[0]); + d=new real[] {val,val}; + } else abort(morepoints); + return d; +} + +// Standard cubic spline interpolation with clamped conditions f'(a), f'(b) +splinetype clamped(real slopea, real slopeb) +{ + return new real[] (real[] x, real[] y) { + int n=x.length; + checklengths(n,y.length); + checkincreasing(x); + real[] d; + if(n > 2) { + real[] a=new real[n]; + real[] b=new real[n]; + real[] c=new real[n]; + real[] g=new real[n]; + b[0]=x[1]-x[0]; + g[0]=b[0]*slopea; + c[0]=0; + a[0]=0; + for(int i=1; i < n-1; ++i) { + a[i]=x[i+1]-x[i]; + c[i]=x[i]-x[i-1]; + b[i]=2*(a[i]+c[i]); + g[i]=3*(c[i]*(y[i+1]-y[i])/a[i]+a[i]*(y[i]-y[i-1])/c[i]); + } + c[n-1]=0; + a[n-1]=0; + b[n-1]=x[n-1]-x[n-2]; + g[n-1]=b[n-1]*slopeb; + d=tridiagonal(a,b,c,g); + } else if(n == 2) { + d=new real[] {slopea,slopeb}; + } else abort(morepoints); + return d; + }; +} + +// Piecewise Cubic Hermite Interpolating Polynomial (PCHIP) +// Modified MATLAB code +// [1] Fritsch, F. N. and R. E. Carlson, +// "Monotone Piecewise Cubic Interpolation," +// SIAM J. Numerical Analysis, Vol. 17, 1980, pp.238-246. +// [2] Kahaner, David, Cleve Moler, Stephen Nash, +// Numerical Methods and Software, Prentice Hall, 1988. +real[] monotonic(real[] x, real[] y) +{ + int n=x.length; + checklengths(n,y.length); + checkincreasing(x); + real[] d=new real[n]; + if(n > 2) { + real[] h=new real[n-1]; + real[] del=new real[n-1]; + for(int i=0; i < n-1; ++i) { + h[i]=x[i+1]-x[i]; + del[i]=(y[i+1]-y[i])/h[i]; + } + int j=0; + int k[]=new int[]; + for(int i=0; i < n-2; ++i) + if((sgn(del[i])*sgn(del[i+1])) > 0) {k[j]=i; j=j+1;} + + real[] hs=new real[j]; + for(int i=0; i < j; ++i) hs[i]=h[k[i]]+h[k[i]+1]; + real w1[]=new real[j]; + real w2[]=new real[j]; + real dmax[]=new real[j]; + real dmin[]=new real[j]; + for(int i=0; i < j; ++i) { + w1[i]=(h[k[i]]+hs[i])/(3*hs[i]); + w2[i]=(h[k[i]+1]+hs[i])/(3*hs[i]); + dmax[i]=max(abs(del[k[i]]),abs(del[k[i]+1])); + dmin[i]=min(abs(del[k[i]]),abs(del[k[i]+1])); + } + for(int i=0; i < n; ++i) d[i]=0; + for(int i=0; i < j; ++i) + d[k[i]+1]=dmin[i]/(w1[i]*(del[k[i]]/dmax[i])+w2[i]*(del[k[i]+1]/dmax[i])); + d[0]=((2*h[0]+h[1])*del[0]-h[0]*del[1])/(h[0]+h[1]); + if(sgn(d[0]) != sgn(del[0])) {d[0]=0;} + else if((sgn(del[0]) != sgn(del[1])) && (abs(d[0]) > abs(3*del[0]))) + d[0]=3*del[0]; + + d[n-1]=((2*h[n-2]+h[n-3])*del[n-2]-h[n-2]*del[n-2])/(h[n-2]+h[n-3]); + if(sgn(d[n-1]) != sgn(del[n-2])) {d[n-1]=0;} + else if((sgn(del[n-2]) != sgn(del[n-3])) && + (abs(d[n-1]) > abs(3*del[n-2]))) + d[n-1]=3*del[n-2]; + } else if(n == 2) { + d[0]=d[1]=(y[1]-y[0])/(x[1]-x[0]); + } else abort(morepoints); + return d; +} + +// Return standard cubic spline interpolation as a guide +guide hermite(real[] x, real[] y, splinetype splinetype=null) +{ + int n=x.length; + if(n == 0) return nullpath; + + guide g=(x[0],y[0]); + if(n == 1) return g; + if(n == 2) return g--(x[1],y[1]); + + if(splinetype == null) + splinetype=(x[0] == x[x.length-1] && y[0] == y[y.length-1]) ? + periodic : notaknot; + + real[] dy=splinetype(x,y); + for(int i=1; i < n; ++i) { + pair z=(x[i],y[i]); + real dx=x[i]-x[i-1]; + g=g..controls((x[i-1],y[i-1])+dx*(1,dy[i-1])/3) and (z-dx*(1,dy[i])/3)..z; + } + return g; +} diff --git a/Build/source/utils/asymptote/base/grid3.asy b/Build/source/utils/asymptote/base/grid3.asy new file mode 100644 index 00000000000..62807847ae6 --- /dev/null +++ b/Build/source/utils/asymptote/base/grid3.asy @@ -0,0 +1,412 @@ +// grid3.asy +// Author: Philippe Ivaldi (Grids in 3D) +// http://www.piprime.fr/ +// Created: 10 janvier 2007 + +import graph3; + +struct grid3 { + path3 axea,axeb; + bounds bds; + triple dir; + valuetime vt; + ticklocate locate; + void create(picture pic, path3 axea, path3 axeb, path3 axelevel, + real min, real max, position pos, autoscaleT t) { + real position=pos.position.x; + triple level; + if(pos.relative) { + position=reltime(axelevel,position); + level=point(axelevel,position)-point(axelevel,0); + } else { + triple v=unit(point(axelevel,1)-point(axelevel,0)); + triple zerolevel=dot(-point(axelevel,0),v)*v; + level=zerolevel+position*v; + } + this.axea=shift(level)*axea; + this.axeb=shift(level)*axeb; + bds=autoscale(min,max,t.scale); + locate=ticklocate(min,max,t,bds.min,bds.max, + Dir(point(axeb,0)-point(axea,0))); + } +}; + +typedef grid3 grid3routine(picture pic); + +triple X(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMin().z);} +triple XY(picture pic) {return (pic.userMax().x,pic.userMax().y,pic.userMin().z);} +triple Y(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMin().z);} +triple YZ(picture pic) {return (pic.userMin().x,pic.userMax().y,pic.userMax().z);} +triple Z(picture pic) {return (pic.userMin().x,pic.userMin().y,pic.userMax().z);} +triple ZX(picture pic) {return (pic.userMax().x,pic.userMin().y,pic.userMax().z);} + +grid3routine XYgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--X(pic),Y(pic)--XY(pic),m--Z(pic), + m.x,M.x,pos,pic.scale.x); + return og; + }; +}; +grid3routine XYgrid=XYgrid(); + +grid3routine YXgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Y(pic),X(pic)--XY(pic),m--Z(pic), + m.y,M.y,pos,pic.scale.y); + return og; + }; +}; +grid3routine YXgrid=YXgrid(); + + +grid3routine XZgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--X(pic),Z(pic)--ZX(pic),m--Y(pic), + m.x,M.x,pos,pic.scale.x); + return og; + }; +}; +grid3routine XZgrid=XZgrid(); + +grid3routine ZXgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Z(pic),X(pic)--ZX(pic),m--Y(pic), + m.z,M.z,pos,pic.scale.z); + return og; + }; +}; +grid3routine ZXgrid=ZXgrid(); + +grid3routine YZgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Y(pic),Z(pic)--YZ(pic),m--X(pic), + m.y,M.y,pos,pic.scale.y); + return og; + }; +}; +grid3routine YZgrid=YZgrid(); + +grid3routine ZYgrid(position pos=Relative(0)) { + return new grid3(picture pic) { + grid3 og; + triple m=pic.userMin(); + triple M=pic.userMax(); + og.create(pic,m--Z(pic),Y(pic)--YZ(pic),m--X(pic), + m.z,M.z,pos,pic.scale.z); + return og; + }; +}; +grid3routine ZYgrid=ZYgrid(); + +typedef grid3routine grid3routines[] ; + +grid3routines XYXgrid(position pos=Relative(0)) { + grid3routines ogs=new grid3routine[] {XYgrid(pos),YXgrid(pos)}; + return ogs; +}; +grid3routines XYXgrid=XYXgrid(); +grid3routines YXYgrid(position pos=Relative(0)) {return XYXgrid(pos);}; +grid3routines YXYgrid=XYXgrid(); + +grid3routines ZXZgrid(position pos=Relative(0)) { + grid3routines ogs=new grid3routine[] {ZXgrid(pos),XZgrid(pos)}; + return ogs; +}; +grid3routines ZXZgrid=ZXZgrid(); +grid3routines XZXgrid(position pos=Relative(0)) {return ZXZgrid(pos);}; +grid3routines XZXgrid=XZXgrid(); + +grid3routines ZYZgrid(position pos=Relative(0)) { + grid3routines ogs=new grid3routine[] {ZYgrid(pos),YZgrid(pos)}; + return ogs; +}; +grid3routines ZYZgrid=ZYZgrid(); +grid3routines YZYgrid(position pos=Relative(0)) {return ZYZgrid(pos);}; +grid3routines YZYgrid=YZYgrid(); + +grid3routines XY_XZgrid(position posa=Relative(0), position posb=Relative(0)) { + grid3routines ogs=new grid3routine[] {XYgrid(posa),XZgrid(posb)}; + return ogs; +}; +grid3routines XY_XZgrid=XY_XZgrid(); + +grid3routines YX_YZgrid(position posa=Relative(0), position posb=Relative(0)) { + grid3routines ogs=new grid3routine[] {YXgrid(posa),YZgrid(posb)}; + return ogs; +}; +grid3routines YX_YZgrid=YX_YZgrid(); + +grid3routines ZX_ZYgrid(position posa=Relative(0), position posb=Relative(0)) { + grid3routines ogs=new grid3routine[] {ZXgrid(posa),ZYgrid(posb)}; + return ogs; +}; +grid3routines ZX_ZYgrid=ZX_ZYgrid(); + +typedef grid3routines[] grid3routinetype; + +grid3routinetype XYZgrid(position pos=Relative(0)) +{ + grid3routinetype ogs=new grid3routines[] {YZYgrid(pos),XYXgrid(pos), + XZXgrid(pos)}; + return ogs; +} +grid3routinetype XYZgrid=XYZgrid(); + +grid3routines operator cast(grid3routine gridroutine) { + grid3routines og=new grid3routine[] {gridroutine}; + return og; +} + +grid3routinetype operator cast(grid3routines gridroutine) { + grid3routinetype og=new grid3routines[] {gridroutine}; + return og; +} + +grid3routinetype operator cast(grid3routine gridroutine) { + grid3routinetype og=(grid3routinetype)(grid3routines) gridroutine; + return og; +} + +void grid3(picture pic=currentpicture, + grid3routinetype gridroutine=XYZgrid, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, + pen pGrid=grey, pen pgrid=lightgrey, + bool above=false) +{ + for(int j=0; j < gridroutine.length; ++j) { + grid3routines gridroutinej=gridroutine[j]; + for(int i=0; i < gridroutinej.length; ++i) { + grid3 gt=gridroutinej[i](pic); + pic.add(new void(picture f, transform3 t, transform3 T, triple, triple) { + picture d; + ticks3 ticks=Ticks3(1,F="%",ticklabel=null, + beginlabel=false,endlabel=false, + N=N,n=n,Step=Step,step=step, + begin=begin,end=end, + Size=0,size=0,extend=true, + pTick=pGrid,ptick=pgrid); + ticks(d,t,"",gt.axea,gt.axeb,nullpen,None,NoMargin3,gt.locate, + gt.bds.divisor,opposite=true,primary=false); + add(f,t*T*inverse(t)*d); + },above=above); + addPath(pic,gt.axea,pGrid); + addPath(pic,gt.axeb,pGrid); + } + } +} + +void grid3(picture pic=currentpicture, + grid3routinetype gridroutine, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, + pen[] pGrid, pen[] pgrid, + bool above=false) +{ + if(pGrid.length != gridroutine.length || pgrid.length != gridroutine.length) + abort("pen array has different length than grid"); + for(int i=0; i < gridroutine.length; ++i) { + grid3(pic=pic,gridroutine=gridroutine[i], + N=N,n=n,Step=Step,step=step, + begin=begin,end=end, + pGrid=pGrid[i],pgrid=pgrid[i], + above=above); + } +} + +position top=Relative(1); +position bottom=Relative(0); +position middle=Relative(0.5); + +// Structure used to communicate ticks and axis settings to grid3 routines. +struct ticksgridT { + ticks3 ticks; + // Other arguments of grid3 are define by ticks and axis settings + void grid3(picture, bool); +}; + +typedef ticksgridT ticksgrid(); + + +ticksgrid InOutTicks(Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, + real Size=0, real size=0, + pen pTick=nullpen, pen ptick=nullpen, + grid3routinetype gridroutine, + pen pGrid=grey, pen pgrid=lightgrey) +{ + return new ticksgridT() + { + ticksgridT otg; + otg.ticks=Ticks3(0,F,ticklabel,beginlabel,endlabel, + N,n,Step,step,begin,end, + Size,size,false,pTick,ptick); + otg.grid3=new void(picture pic, bool above) { + grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above); + }; + return otg; + }; +} + +ticksgrid InTicks(Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, + real Size=0, real size=0, + pen pTick=nullpen, pen ptick=nullpen, + grid3routinetype gridroutine, + pen pGrid=grey, pen pgrid=lightgrey) +{ + return new ticksgridT() + { + ticksgridT otg; + otg.ticks=Ticks3(-1,F,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,Size,size,false,pTick,ptick); + otg.grid3=new void(picture pic, bool above) { + grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above); + }; + return otg; + }; +} + +ticksgrid OutTicks(Label F="", ticklabel ticklabel=null, + bool beginlabel=true, bool endlabel=true, + int N=0, int n=0, real Step=0, real step=0, + bool begin=true, bool end=true, + real Size=0, real size=0, + pen pTick=nullpen, pen ptick=nullpen, + grid3routinetype gridroutine, + pen pGrid=grey, pen pgrid=lightgrey) +{ + return new ticksgridT() + { + ticksgridT otg; + otg.ticks=Ticks3(1,F,ticklabel,beginlabel,endlabel,N,n,Step,step, + begin,end,Size,size,false,pTick,ptick); + otg.grid3=new void(picture pic, bool above) { + grid3(pic,gridroutine,N,n,Step,step,begin,end,pGrid,pgrid,above); + }; + return otg; + }; +} + +void xaxis3(picture pic=currentpicture, Label L="", axis axis=YZZero, + pen p=currentpen, ticksgrid ticks, + arrowbar3 arrow=None, bool above=false) +{ + xaxis3(pic,L,axis,p,ticks().ticks,arrow,above); + ticks().grid3(pic,above); +} + +void yaxis3(picture pic=currentpicture, Label L="", axis axis=XZZero, + pen p=currentpen, ticksgrid ticks, + arrowbar3 arrow=None, bool above=false) +{ + yaxis3(pic,L,axis,p,ticks().ticks,arrow,above); + ticks().grid3(pic,above); +} + +void zaxis3(picture pic=currentpicture, Label L="", axis axis=XYZero, + pen p=currentpen, ticksgrid ticks, + arrowbar3 arrow=None, bool above=false) +{ + zaxis3(pic,L,axis,p,ticks().ticks,arrow,above); + ticks().grid3(pic,above); +} + +/* Example: + + import grid3; + + size(8cm,0); + currentprojection=orthographic(0.5,1,0.5); + + defaultpen(overwrite(SuppressQuiet)); + + scale(Linear, Linear, Log); + + grid3(pic=currentpicture, // picture + gridroutine=XYZgrid(// grid3routine + // or grid3routine[] (alias grid3routines) + // or grid3routines[]: + // The routine(s) to draw the grid(s): + // *XYgrid: draw grid from X in direction of Y + // *YXgrid: draw grid from Y in direction of X, ... + // *An array of previous values XYgrid, YXgrid, ... + // *XYXgrid: draw XYgrid and YXgrid grids + // *YXYgrid: draw XYgrid and YXgrid grids + // *ZXZgrid: draw ZXgrid and XZgrid grids + // *YX_YZgrid: draw YXgrid and YZgrid grids + // *XY_XZgrid: draw XYgrid and XZgrid grids + // *YX_YZgrid: draw YXgrid and YZgrid grids + // *An array of previous values XYXgrid,... + // *XYZgrid: draw XYXgrid, ZYZgrid, XZXgrid grids. + pos=Relative(0)), + // the position of the grid relative to the axis + // perpendicular to the grid; a real number + // specifies a coordinate relative to this axis. + // Aliases: top=Relative(1), middle=Relative(0.5) + // and bottom=Relative(0). + + // These arguments are similar to those of InOutTicks(): + N=0, // int + n=0, // int + Step=0, // real + step=0, // real + begin=true, // bool + end=true, // bool + pGrid=grey, // pen + pgrid=lightgrey, // pen + above=false // bool + ); + + xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks()); + yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks()); + zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks()); +*/ + +/* Other examples: + + int N=10, n=2; + xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks()); + yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks(N=N,n=n)); + zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks()); + grid3(N=N,n=n); + + xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks()); + yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks()); + zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks()); + grid3(new grid3routines[] {XYXgrid(top),XZXgrid(0)}); + + xaxis3(Label("$x$",position=EndPoint,align=S),OutTicks()); + yaxis3(Label("$y$",position=EndPoint,align=S),OutTicks()); + zaxis3(Label("$z$",position=EndPoint,align=(0,0.5)+W),OutTicks()); + grid3(new grid3routines[] {XYXgrid(-0.5),XYXgrid(1.5)}, + pGrid=new pen[] {red,blue}, + pgrid=new pen[] {0.5red,0.5blue}); + + // Axes with grids: + + xaxis3(Label("$x$",position=EndPoint,align=S), + OutTicks(Step=0.5,gridroutine=XYgrid)); + yaxis3(Label("$y$",position=EndPoint,align=S), + InOutTicks(Label("",align=0.5X),N=8,n=2,gridroutine=YX_YZgrid)); + zaxis3("$z$",OutTicks(ZYgrid)); +*/ diff --git a/Build/source/utils/asymptote/base/interpolate.asy b/Build/source/utils/asymptote/base/interpolate.asy new file mode 100644 index 00000000000..d66f227fcc3 --- /dev/null +++ b/Build/source/utils/asymptote/base/interpolate.asy @@ -0,0 +1,140 @@ +// Lagrange and Hermite interpolation in Asymptote +// Author: Olivier Guibé +// Acknowledgements: Philippe Ivaldi + +// diffdiv(x,y) computes Newton's Divided Difference for +// Lagrange interpolation with distinct values {x_0,..,x_n} in the array x +// and values y_0,...,y_n in the array y, + +// hdiffdiv(x,y,dyp) computes Newton's Divided Difference for +// Hermite interpolation where dyp={dy_0,...,dy_n}. +// +// fhorner(x,coeff) uses Horner's rule to compute the polynomial +// a_0+a_1(x-x_0)+a_2(x-x_0)(x-x_1)+...+a_n(x-x_0)..(x-x_{n-1}), +// where coeff={a_0,a_1,...,a_n}. + +// fspline does standard cubic spline interpolation of a function f +// on the interval [a,b]. +// The points a=x_1 < x_2 < .. < x_n=b form the array x; +// the points y_1=f(x_1),....,y_n=f(x_n) form the array y +// We use the Hermite form for the spline. + +// The syntax is: +// s=fspline(x,y); default not_a_knot condition +// s=fspline(x,y,natural); natural spline +// s=fspline(x,y,periodic); periodic spline +// s=fspline(x,y,clamped(1,1)); clamped spline +// s=fspline(x,y,monotonic); piecewise monotonic spline + +// Here s is a real function that is constant on (-infinity,a] and [b,infinity). + +private import math; +import graph_splinetype; + +typedef real fhorner(real); + +struct horner { + // x={x0,..,xn}(not necessarily distinct) + // a={a0,..,an} corresponds to the polyonmial + // a_0+a_1(x-x_0)+a_2(x-x_0)(x-x_1)+...+a_n(x-x_0)..(x-x_{n-1}), + real[] x; + real[] a; +} + +// Evaluate p(x)=d0+(x-x0)(d1+(x-x1)+...+(d(n-1)+(x-x(n-1))*dn))) +// via Horner's rule: n-1 multiplications, 2n-2 additions. +fhorner fhorner(horner sh) +{ + int n=sh.x.length; + checklengths(n,sh.a.length); + return new real(real x) { + real s=sh.a[n-1]; + for(int k=n-2; k >= 0; --k) + s=sh.a[k]+(x-sh.x[k])*s; + return s; + }; +} + +// Newton's Divided Difference method: n(n-1)/2 divisions, n(n-1) additions. +horner diffdiv(real[] x, real[] y) +{ + int n=x.length; + horner s; + checklengths(n,y.length); + for(int i=0; i < n; ++i) + s.a[i]=y[i]; + for(int k=0; k < n-1; ++k) { + for(int i=n-1; i > k; --i) { + s.a[i]=(s.a[i]-s.a[i-1])/(x[i]-x[i-k-1]); + } + } + s.x=x; + return s; +} + +// Newton's Divided Difference for simple Hermite interpolation, +// where one specifies both p(x_i) and p'(x_i). +horner hdiffdiv(real[] x, real[] y, real[] dy) +{ + int n=x.length; + horner s; + checklengths(n,y.length); + checklengths(n,dy.length); + for(int i=0; i < n; ++i) { + s.a[2*i]=y[i]; + s.a[2*i+1]=dy[i]; + s.x[2*i]=x[i]; + s.x[2*i+1]=x[i]; + } + + for(int i=n-1; i > 0; --i) + s.a[2*i]=(s.a[2*i]-s.a[2*i-2])/(x[i]-x[i-1]); + + int stop=2*n-1; + for(int k=1; k < stop; ++k) { + for(int i=stop; i > k; --i) { + s.a[i]=(s.a[i]-s.a[i-1])/(s.x[i]-s.x[i-k-1]); + } + } + return s; +} + +typedef real realfunction(real); + +// piecewise Hermite interpolation: +// return the piecewise polynomial p(x), where on [x_i,x_i+1], deg(p) <= 3, +// p(x_i)=y_i, p(x_{i+1})=y_i+1, p'(x_i)=dy_i, and p'(x_{i+1})=dy_i+1. +// Outside [x_1,x_n] the returned function is constant: y_1 on (infinity,x_1] +// and y_n on [x_n,infinity). +realfunction pwhermite(real[] x, real[] y, real[] dy) +{ + int n=x.length; + checklengths(n,y.length); + checklengths(n,dy.length); + if(n < 2) abort(morepoints); + if(!increasing(x,strict=true)) abort("array x is not strictly increasing"); + return new real(real t) { + int i=search(x,t); + if(i == n-1) { + i=n-2; + t=x[n-1]; + } else if(i == -1) { + i=0; + t=x[0]; + } + real h=x[i+1]-x[i]; + real delta=(y[i+1]-y[i])/h; + real e=(3*delta-2*dy[i]-dy[i+1])/h; + real f=(dy[i]-2*delta+dy[i+1])/h^2; + real s=t-x[i]; + return y[i]+s*(dy[i]+s*(e+s*f)); + }; +} + +realfunction fspline(real[] x, real[] y, splinetype splinetype=notaknot) +{ + real[] dy=splinetype(x,y); + return new real(real t) { + return pwhermite(x,y,dy)(t); + }; +} diff --git a/Build/source/utils/asymptote/base/labelpath.asy b/Build/source/utils/asymptote/base/labelpath.asy new file mode 100644 index 00000000000..39c1908efd3 --- /dev/null +++ b/Build/source/utils/asymptote/base/labelpath.asy @@ -0,0 +1,28 @@ +usepackage("pstricks"); +usepackage("pst-text"); + +string LeftJustified="l"; +string RightJustified="r"; +string Centered="c"; + +void labelpath(frame f, Label L, path g, string justify=Centered, + pen p=currentpen) +{ + if(latex() && !pdf()) { + _labelpath(f,L.s,L.size,g,justify,(L.T.x,L.T.y+0.5linewidth(p)),p); + return; + } + warning("labelpathlatex","labelpath requires -tex latex"); +} + +void labelpath(picture pic=currentpicture, Label L, path g, + string justify=Centered, pen p=currentpen) +{ + pic.add(new void(frame f, transform t) { + labelpath(f,L,t*g,justify,p); + }); + frame f; + label(f,Label(L.s,L.size)); + real w=size(f).y+L.T.y+0.5linewidth(p); + pic.addBox(min(g),max(g),-w,w); +} diff --git a/Build/source/utils/asymptote/base/labelpath3.asy b/Build/source/utils/asymptote/base/labelpath3.asy new file mode 100644 index 00000000000..2c9529dc7e8 --- /dev/null +++ b/Build/source/utils/asymptote/base/labelpath3.asy @@ -0,0 +1,83 @@ +// Fit a label to a path3. +// Author: Jens Schwaiger + +import three; +private real eps=100*realEpsilon; + +triple nextnormal(triple p, triple q) +{ + triple nw=p-(dot(p,q)*q); + return abs(nw) < 0.0001 ? p : unit(nw); +} + +triple[] firstframe(path3 p, triple optional=O) +{ + triple[] start=new triple[3]; + start[0]=dir(p,reltime(p,0)); + start[1]=(abs(cross(start[0],optional)) < eps) ? perp(start[0]) : + unit(cross(start[0],optional)); + start[2]=cross(start[0],start[1]); + return start; +} + +// Modification of the bishop frame construction contained in +// space_tube.asy (from Philippe Ivaldi's modules). +// For noncyclic path3s only +triple[] nextframe(path3 p, real reltimestart, triple[] start, real + reltimeend, int subdiv=20) +{ + triple[][] bf=new triple[subdiv+1][3]; + real lg=reltimeend-reltimestart; + if(lg <= 0) return start; + bf[0]=start; + int n=subdiv+1; + for(int i=1; i < n; ++i) + bf[i][0]=dir(p,reltime(p,reltimestart+(i/subdiv)*lg)); + + for(int i=1; i < n; ++i) { + bf[i][1]=nextnormal(bf[i-1][1],bf[i][0]); + bf[i][2]=cross(bf[i][0],bf[i][1]); + } + return bf[subdiv]; +} + +surface labelpath(string s, path3 p, real angle=90, triple optional=O) +{ + real Cos=Cos(angle); + real Sin=Sin(angle); + path[] text=texpath(Label(s,(0,0),Align,basealign)); + text=scale(1/(max(text).x-min(text).x))*text; + path[][] decompose=containmentTree(text); + + real[][] xpos=new real[decompose.length][2]; + surface sf; + for(int i=0; i < decompose.length; ++i) {// Identify positions along x-axis + xpos[i][1]=i; + real pos0=0.5(max(decompose[i]).x+min(decompose[i]).x); + xpos[i][0]=pos0; + } + xpos=sort(xpos); // sort by distance from 0; + triple[] pos=new triple[decompose.length]; + real lg=arclength(p); + //create frames; + triple[] first=firstframe(p,optional); + triple[] t0=first; + real tm0=0; + triple[][] bfr=new triple[decompose.length][3]; + for(int j=0; j < decompose.length; ++j) { + bfr[j]=nextframe(p,tm0,t0,xpos[j][0]); + tm0=xpos[j][0]; t0=bfr[j]; + } + transform3[] mt=new transform3[bfr.length]; + for(int j=0; j < bfr.length; ++j) { + triple f2=Cos*bfr[j][1]+Sin*bfr[j][2]; + triple f3=Sin*bfr[j][1]+Cos*bfr[j][2]; + mt[j]=shift(relpoint(p,xpos[j][0]))*transform3(bfr[j][0],f2,f3); + } + for(int j=0; j < bfr.length; ++j) { + path[] dc=decompose[(int) xpos[j][1]]; + pair pos0=(0.5(max(dc).x+min(dc).x),0); + sf.append(mt[j]*surface(scale(lg)*shift(-pos0)*dc)); + } + return sf; +} diff --git a/Build/source/utils/asymptote/base/lmfit.asy b/Build/source/utils/asymptote/base/lmfit.asy new file mode 100644 index 00000000000..63e0d7e2386 --- /dev/null +++ b/Build/source/utils/asymptote/base/lmfit.asy @@ -0,0 +1,881 @@ +/* + Copyright (c) 2009 Philipp Stephani + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + Fitting $n$ data points $(x_1, y_1 \pm \Delta y_1), \dots, (x_n, y_n \pm \Delta y_n)$ + to a function $f$ that depends on $m$ parameters $a_1, \dots, a_m$ means minimizing + the least-squares sum + % + \begin{equation*} + \sum_{i = 1}^n \left( \frac{y_i - f(a_1, \dots, a_m; x_i)}{\Delta y_i} \right)^2 + \end{equation*} + % + with respect to the parameters $a_1, \dots, a_m$. +*/ + +/* + This module provides an implementation of the Levenberg--Marquardt + (LM) algorithm, converted from the C lmfit routine by Joachim Wuttke + (see http://www.messen-und-deuten.de/lmfit/). + + Implementation strategy: Fortunately, Asymptote's syntax is very + similar to C, and the original code cleanly separates the + customizable parts (user-provided data, output routines, etc.) from + the dirty number crunching. Thus, mst of the code was just copied + and slightly modified from the original source files. I have + amended the lm_data_type structure and the callback routines with a + weight array that can be used to provide experimental errors. I + have also created two simple wrapper functions. +*/ + + +// copied from the C code +private real LM_MACHEP = realEpsilon; +private real LM_DWARF = realMin; +private real LM_SQRT_DWARF = sqrt(realMin); +private real LM_SQRT_GIANT = sqrt(realMax); +private real LM_USERTOL = 30 * LM_MACHEP; + +restricted string lm_infmsg[] = { + "improper input parameters", + "the relative error in the sum of squares is at most tol", + "the relative error between x and the solution is at most tol", + "both errors are at most tol", + "fvec is orthogonal to the columns of the jacobian to machine precision", + "number of calls to fcn has reached or exceeded maxcall*(n+1)", + "ftol is too small: no further reduction in the sum of squares is possible", + "xtol too small: no further improvement in approximate solution x possible", + "gtol too small: no further improvement in approximate solution x possible", + "not enough memory", + "break requested within function evaluation" +}; + +restricted string lm_shortmsg[] = { + "invalid input", + "success (f)", + "success (p)", + "success (f,p)", + "degenerate", + "call limit", + "failed (f)", + "failed (p)", + "failed (o)", + "no memory", + "user break" +}; + + +// copied from the C code and amended with the weight (user_w) array +struct lm_data_type { + real[] user_t; + real[] user_y; + real[] user_w; + real user_func(real user_t_point, real[] par); +}; + + +// Asymptote has no pointer support, so we need reference wrappers for +// the int and real types +struct lm_int_type { + int val; + + void operator init(int val) { + this.val = val; + } +}; + + +struct lm_real_type { + real val; + + void operator init(real val) { + this.val = val; + } +}; + + +// copied from the C code; the lm_initialize_control function turned +// into a constructor +struct lm_control_type { + real ftol; + real xtol; + real gtol; + real epsilon; + real stepbound; + real fnorm; + int maxcall; + lm_int_type nfev; + lm_int_type info; + + void operator init() { + maxcall = 100; + epsilon = LM_USERTOL; + stepbound = 100; + ftol = LM_USERTOL; + xtol = LM_USERTOL; + gtol = LM_USERTOL; + } +}; + + +// copied from the C code +typedef void lm_evaluate_ftype(real[] par, int m_dat, real[] fvec, lm_data_type data, lm_int_type info); +typedef void lm_print_ftype(int n_par, real[] par, int m_dat, real[] fvec, lm_data_type data, int iflag, int iter, int nfev); + + +// copied from the C code +private real SQR(real x) { + return x * x; +} + + +// Asymptote doesn't support pointers to arbitrary array elements, so +// we provide an offset parameter. +private real lm_enorm(int n, real[] x, int offset=0) { + real s1 = 0; + real s2 = 0; + real s3 = 0; + real x1max = 0; + real x3max = 0; + real agiant = LM_SQRT_GIANT / n; + real xabs, temp; + + for (int i = 0; i < n; ++i) { + xabs = fabs(x[offset + i]); + if (xabs > LM_SQRT_DWARF && xabs < agiant) { + s2 += SQR(xabs); + continue; + } + + if (xabs > LM_SQRT_DWARF) { + if (xabs > x1max) { + temp = x1max / xabs; + s1 = 1 + s1 * SQR(temp); + x1max = xabs; + } else { + temp = xabs / x1max; + s1 += SQR(temp); + } + continue; + } + if (xabs > x3max) { + temp = x3max / xabs; + s3 = 1 + s3 * SQR(temp); + x3max = xabs; + } else { + if (xabs != 0.0) { + temp = xabs / x3max; + s3 += SQR(temp); + } + } + } + + if (s1 != 0) + return x1max * sqrt(s1 + (s2 / x1max) / x1max); + if (s2 != 0) { + if (s2 >= x3max) + return sqrt(s2 * (1 + (x3max / s2) * (x3max * s3))); + else + return sqrt(x3max * ((s2 / x3max) + (x3max * s3))); + } + + return x3max * sqrt(s3); +} + + +// This function calculated the vector whose square sum is to be +// minimized. We use a slight modification of the original code that +// includes the weight factor. The user may provide different +// customizations. +void lm_evaluate_default(real[] par, int m_dat, real[] fvec, lm_data_type data, lm_int_type info) { + for (int i = 0; i < m_dat; ++i) { + fvec[i] = data.user_w[i] * (data.user_y[i] - data.user_func(data.user_t[i], par)); + } +} + + +// Helper functions to print padded strings and numbers (until +// Asymptote provides a real printf function) +private string pad(string str, int count, string pad=" ") { + string res = str; + while (length(res) < count) + res = pad + res; + return res; +} + + +private string pad(int num, int digits, string pad=" ") { + return pad(string(num), digits, pad); +} + + +private string pad(real num, int digits, string pad=" ") { + return pad(string(num), digits, pad); +} + + +// Similar to the C code, also prints weights +void lm_print_default(int n_par, real[] par, int m_dat, real[] fvec, lm_data_type data, int iflag, int iter, int nfev) { + real f, y, t, w; + int i; + + if (iflag == 2) { + write("trying step in gradient direction"); + } else if (iflag == 1) { + write(format("determining gradient (iteration %d)", iter)); + } else if (iflag == 0) { + write("starting minimization"); + } else if (iflag == -1) { + write(format("terminated after %d evaluations", nfev)); + } + + write(" par: ", none); + for (i = 0; i < n_par; ++i) { + write(" " + pad(par[i], 12), none); + } + write(" => norm: " + pad(lm_enorm(m_dat, fvec), 12)); + + if (iflag == -1) { + write(" fitting data as follows:"); + for (i = 0; i < m_dat; ++i) { + t = data.user_t[i]; + y = data.user_y[i]; + w = data.user_w[i]; + f = data.user_func(t, par); + write(format(" t[%2d]=", i) + pad(t, 12) + " y=" + pad(y, 12) + " w=" + pad(w, 12) + " fit=" + pad(f, 12) + " residue=" + pad(y - f, 12)); + } + } +} + + +// Prints nothing +void lm_print_quiet(int n_par, real[] par, int m_dat, real[] fvec, lm_data_type data, int iflag, int iter, int nfev) { +} + + +// copied from the C code +private void lm_qrfac(int m, int n, real[] a, bool pivot, int[] ipvt, real[] rdiag, real[] acnorm, real[] wa) { + int i, j, k, kmax, minmn; + real ajnorm, sum, temp; + static real p05 = 0.05; + + for (j = 0; j < n; ++j) { + acnorm[j] = lm_enorm(m, a, j * m); + rdiag[j] = acnorm[j]; + wa[j] = rdiag[j]; + if (pivot) + ipvt[j] = j; + } + + minmn = min(m, n); + for (j = 0; j < minmn; ++j) { + while (pivot) { + kmax = j; + for (k = j + 1; k < n; ++k) + if (rdiag[k] > rdiag[kmax]) + kmax = k; + if (kmax == j) + break; + + for (i = 0; i < m; ++i) { + temp = a[j * m + i]; + a[j * m + i] = a[kmax * m + i]; + a[kmax * m + i] = temp; + } + rdiag[kmax] = rdiag[j]; + wa[kmax] = wa[j]; + k = ipvt[j]; + ipvt[j] = ipvt[kmax]; + ipvt[kmax] = k; + + break; + } + + ajnorm = lm_enorm(m - j, a, j * m + j); + if (ajnorm == 0.0) { + rdiag[j] = 0; + continue; + } + + if (a[j * m + j] < 0.0) + ajnorm = -ajnorm; + for (i = j; i < m; ++i) + a[j * m + i] /= ajnorm; + a[j * m + j] += 1; + + for (k = j + 1; k < n; ++k) { + sum = 0; + + for (i = j; i < m; ++i) + sum += a[j * m + i] * a[k * m + i]; + + temp = sum / a[j + m * j]; + + for (i = j; i < m; ++i) + a[k * m + i] -= temp * a[j * m + i]; + + if (pivot && rdiag[k] != 0.0) { + temp = a[m * k + j] / rdiag[k]; + temp = max(0.0, 1 - SQR(temp)); + rdiag[k] *= sqrt(temp); + temp = rdiag[k] / wa[k]; + if (p05 * SQR(temp) <= LM_MACHEP) { + rdiag[k] = lm_enorm(m - j - 1, a, m * k + j + 1); + wa[k] = rdiag[k]; + } + } + } + + rdiag[j] = -ajnorm; + } +} + + +// copied from the C code +private void lm_qrsolv(int n, real[] r, int ldr, int[] ipvt, real[] diag, real[] qtb, real[] x, real[] sdiag, real[] wa) { + static real p25 = 0.25; + static real p5 = 0.5; + + int i, kk, j, k, nsing; + real qtbpj, sum, temp; + real _sin, _cos, _tan, _cot; + + for (j = 0; j < n; ++j) { + for (i = j; i < n; ++i) + r[j * ldr + i] = r[i * ldr + j]; + x[j] = r[j * ldr + j]; + wa[j] = qtb[j]; + } + + for (j = 0; j < n; ++j) { + while (diag[ipvt[j]] != 0.0) { + for (k = j; k < n; ++k) + sdiag[k] = 0.0; + sdiag[j] = diag[ipvt[j]]; + + qtbpj = 0.; + for (k = j; k < n; ++k) { + if (sdiag[k] == 0.) + continue; + kk = k + ldr * k; + if (fabs(r[kk]) < fabs(sdiag[k])) { + _cot = r[kk] / sdiag[k]; + _sin = p5 / sqrt(p25 + p25 * _cot * _cot); + _cos = _sin * _cot; + } else { + _tan = sdiag[k] / r[kk]; + _cos = p5 / sqrt(p25 + p25 * _tan * _tan); + _sin = _cos * _tan; + } + + r[kk] = _cos * r[kk] + _sin * sdiag[k]; + temp = _cos * wa[k] + _sin * qtbpj; + qtbpj = -_sin * wa[k] + _cos * qtbpj; + wa[k] = temp; + + for (i = k + 1; i < n; ++i) { + temp = _cos * r[k * ldr + i] + _sin * sdiag[i]; + sdiag[i] = -_sin * r[k * ldr + i] + _cos * sdiag[i]; + r[k * ldr + i] = temp; + } + } + break; + } + + sdiag[j] = r[j * ldr + j]; + r[j * ldr + j] = x[j]; + } + + nsing = n; + for (j = 0; j < n; ++j) { + if (sdiag[j] == 0.0 && nsing == n) + nsing = j; + if (nsing < n) + wa[j] = 0; + } + + for (j = nsing - 1; j >= 0; --j) { + sum = 0; + for (i = j + 1; i < nsing; ++i) + sum += r[j * ldr + i] * wa[i]; + wa[j] = (wa[j] - sum) / sdiag[j]; + } + + for (j = 0; j < n; ++j) + x[ipvt[j]] = wa[j]; +} + + +// copied from the C code +private void lm_lmpar(int n, real[] r, int ldr, int[] ipvt, real[] diag, real[] qtb, real delta, lm_real_type par, real[] x, real[] sdiag, real[] wa1, real[] wa2) { + static real p1 = 0.1; + static real p001 = 0.001; + + int nsing = n; + real parl = 0.0; + + int i, iter, j; + real dxnorm, fp, fp_old, gnorm, parc, paru; + real sum, temp; + + for (j = 0; j < n; ++j) { + wa1[j] = qtb[j]; + if (r[j * ldr + j] == 0 && nsing == n) + nsing = j; + if (nsing < n) + wa1[j] = 0; + } + for (j = nsing - 1; j >= 0; --j) { + wa1[j] = wa1[j] / r[j + ldr * j]; + temp = wa1[j]; + for (i = 0; i < j; ++i) + wa1[i] -= r[j * ldr + i] * temp; + } + + for (j = 0; j < n; ++j) + x[ipvt[j]] = wa1[j]; + + iter = 0; + for (j = 0; j < n; ++j) + wa2[j] = diag[j] * x[j]; + dxnorm = lm_enorm(n, wa2); + fp = dxnorm - delta; + if (fp <= p1 * delta) { + par.val = 0; + return; + } + + if (nsing >= n) { + for (j = 0; j < n; ++j) + wa1[j] = diag[ipvt[j]] * wa2[ipvt[j]] / dxnorm; + + for (j = 0; j < n; ++j) { + sum = 0.0; + for (i = 0; i < j; ++i) + sum += r[j * ldr + i] * wa1[i]; + wa1[j] = (wa1[j] - sum) / r[j + ldr * j]; + } + temp = lm_enorm(n, wa1); + parl = fp / delta / temp / temp; + } + + for (j = 0; j < n; ++j) { + sum = 0; + for (i = 0; i <= j; ++i) + sum += r[j * ldr + i] * qtb[i]; + wa1[j] = sum / diag[ipvt[j]]; + } + gnorm = lm_enorm(n, wa1); + paru = gnorm / delta; + if (paru == 0.0) + paru = LM_DWARF / min(delta, p1); + + par.val = max(par.val, parl); + par.val = min(par.val, paru); + if (par.val == 0.0) + par.val = gnorm / dxnorm; + + for (;; ++iter) { + if (par.val == 0.0) + par.val = max(LM_DWARF, p001 * paru); + temp = sqrt(par.val); + for (j = 0; j < n; ++j) + wa1[j] = temp * diag[j]; + lm_qrsolv(n, r, ldr, ipvt, wa1, qtb, x, sdiag, wa2); + for (j = 0; j < n; ++j) + wa2[j] = diag[j] * x[j]; + dxnorm = lm_enorm(n, wa2); + fp_old = fp; + fp = dxnorm - delta; + + if (fabs(fp) <= p1 * delta || (parl == 0.0 && fp <= fp_old && fp_old < 0.0) || iter == 10) + break; + + for (j = 0; j < n; ++j) + wa1[j] = diag[ipvt[j]] * wa2[ipvt[j]] / dxnorm; + + for (j = 0; j < n; ++j) { + wa1[j] = wa1[j] / sdiag[j]; + for (i = j + 1; i < n; ++i) + wa1[i] -= r[j * ldr + i] * wa1[j]; + } + temp = lm_enorm(n, wa1); + parc = fp / delta / temp / temp; + + if (fp > 0) + parl = max(parl, par.val); + else if (fp < 0) + paru = min(paru, par.val); + + par.val = max(parl, par.val + parc); + } +} + + +// copied from the C code; the main function +void lm_lmdif(int m, int n, real[] x, real[] fvec, real ftol, real xtol, real gtol, int maxfev, real epsfcn, real[] diag, int mode, real factor, lm_int_type info, lm_int_type nfev, real[] fjac, int[] ipvt, real[] qtf, real[] wa1, real[] wa2, real[] wa3, real[] wa4, lm_evaluate_ftype evaluate, lm_print_ftype printout, lm_data_type data) { + static real p1 = 0.1; + static real p5 = 0.5; + static real p25 = 0.25; + static real p75 = 0.75; + static real p0001 = 1.0e-4; + + nfev.val = 0; + int iter = 1; + lm_real_type par = lm_real_type(0); + real delta = 0; + real xnorm = 0; + real temp = max(epsfcn, LM_MACHEP); + real eps = sqrt(temp); + int i, j; + real actred, dirder, fnorm, fnorm1, gnorm, pnorm, prered, ratio, step, sum, temp1, temp2, temp3; + + if ((n <= 0) || (m < n) || (ftol < 0.0) || (xtol < 0.0) || (gtol < 0.0) || (maxfev <= 0) || (factor <= 0)) { + info.val = 0; + return; + } + if (mode == 2) { + for (j = 0; j < n; ++j) { + if (diag[j] <= 0.0) { + info.val = 0; + return; + } + } + } + + info.val = 0; + evaluate(x, m, fvec, data, info); + if(printout != null) printout(n, x, m, fvec, data, 0, 0, ++nfev.val); + if (info.val < 0) + return; + fnorm = lm_enorm(m, fvec); + + do { + for (j = 0; j < n; ++j) { + temp = x[j]; + step = eps * fabs(temp); + if (step == 0.0) + step = eps; + x[j] = temp + step; + info.val = 0; + evaluate(x, m, wa4, data, info); + if(printout != null) printout(n, x, m, wa4, data, 1, iter, ++nfev.val); + if (info.val < 0) + return; + for (i = 0; i < m; ++i) + fjac[j * m + i] = (wa4[i] - fvec[i]) / (x[j] - temp); + x[j] = temp; + } + + lm_qrfac(m, n, fjac, true, ipvt, wa1, wa2, wa3); + + if (iter == 1) { + if (mode != 2) { + for (j = 0; j < n; ++j) { + diag[j] = wa2[j]; + if (wa2[j] == 0.0) + diag[j] = 1.0; + } + } + for (j = 0; j < n; ++j) + wa3[j] = diag[j] * x[j]; + xnorm = lm_enorm(n, wa3); + delta = factor * xnorm; + if (delta == 0.0) + delta = factor; + } + + for (i = 0; i < m; ++i) + wa4[i] = fvec[i]; + + for (j = 0; j < n; ++j) { + temp3 = fjac[j * m + j]; + if (temp3 != 0.0) { + sum = 0; + for (i = j; i < m; ++i) + sum += fjac[j * m + i] * wa4[i]; + temp = -sum / temp3; + for (i = j; i < m; ++i) + wa4[i] += fjac[j * m + i] * temp; + } + fjac[j * m + j] = wa1[j]; + qtf[j] = wa4[j]; + } + + gnorm = 0; + if (fnorm != 0) { + for (j = 0; j < n; ++j) { + if (wa2[ipvt[j]] == 0) continue; + sum = 0.0; + for (i = 0; i <= j; ++i) + sum += fjac[j * m + i] * qtf[i] / fnorm; + gnorm = max(gnorm, fabs(sum / wa2[ipvt[j]])); + } + } + + if (gnorm <= gtol) { + info.val = 4; + return; + } + + if (mode != 2) { + for (j = 0; j < n; ++j) + diag[j] = max(diag[j], wa2[j]); + } + + do { + lm_lmpar(n, fjac, m, ipvt, diag, qtf, delta, par, wa1, wa2, wa3, wa4); + + for (j = 0; j < n; ++j) { + wa1[j] = -wa1[j]; + wa2[j] = x[j] + wa1[j]; + wa3[j] = diag[j] * wa1[j]; + } + pnorm = lm_enorm(n, wa3); + + if (nfev.val <= 1 + n) + delta = min(delta, pnorm); + + info.val = 0; + evaluate(wa2, m, wa4, data, info); + if(printout != null) printout(n, x, m, wa4, data, 2, iter, ++nfev.val); + if (info.val < 0) + return; + + fnorm1 = lm_enorm(m, wa4); + + if (p1 * fnorm1 < fnorm) + actred = 1 - SQR(fnorm1 / fnorm); + else + actred = -1; + + for (j = 0; j < n; ++j) { + wa3[j] = 0; + for (i = 0; i <= j; ++i) + wa3[i] += fjac[j * m + i] * wa1[ipvt[j]]; + } + temp1 = lm_enorm(n, wa3) / fnorm; + temp2 = sqrt(par.val) * pnorm / fnorm; + prered = SQR(temp1) + 2 * SQR(temp2); + dirder = -(SQR(temp1) + SQR(temp2)); + + ratio = prered != 0 ? actred / prered : 0; + + if (ratio <= p25) { + if (actred >= 0.0) + temp = p5; + else + temp = p5 * dirder / (dirder + p5 * actred); + if (p1 * fnorm1 >= fnorm || temp < p1) + temp = p1; + delta = temp * min(delta, pnorm / p1); + par.val /= temp; + } else if (par.val == 0.0 || ratio >= p75) { + delta = pnorm / p5; + par.val *= p5; + } + + if (ratio >= p0001) { + for (j = 0; j < n; ++j) { + x[j] = wa2[j]; + wa2[j] = diag[j] * x[j]; + } + for (i = 0; i < m; ++i) + fvec[i] = wa4[i]; + xnorm = lm_enorm(n, wa2); + fnorm = fnorm1; + ++iter; + } + + info.val = 0; + if (fabs(actred) <= ftol && prered <= ftol && p5 * ratio <= 1) + info.val = 1; + if (delta <= xtol * xnorm) + info.val += 2; + if (info.val != 0) + return; + + if (nfev.val >= maxfev) + info.val = 5; + if (fabs(actred) <= LM_MACHEP && prered <= LM_MACHEP && p5 * ratio <= 1) + info.val = 6; + if (delta <= LM_MACHEP * xnorm) + info.val = 7; + if (gnorm <= LM_MACHEP) + info.val = 8; + if (info.val != 0) + return; + } while (ratio < p0001); + } while (true); +} + + +// copied from the C code; wrapper of lm_lmdif +void lm_minimize(int m_dat, int n_par, real[] par, lm_evaluate_ftype evaluate, lm_print_ftype printout, lm_data_type data, lm_control_type control) { + int n = n_par; + int m = m_dat; + + real[] fvec = new real[m]; + real[] diag = new real[n]; + real[] qtf = new real[n]; + real[] fjac = new real[n * m]; + real[] wa1 = new real[n]; + real[] wa2 = new real[n]; + real[] wa3 = new real[n]; + real[] wa4 = new real[m]; + int[] ipvt = new int[n]; + + control.info.val = 0; + control.nfev.val = 0; + + lm_lmdif(m, n, par, fvec, control.ftol, control.xtol, control.gtol, control.maxcall * (n + 1), control.epsilon, diag, 1, control.stepbound, control.info, control.nfev, fjac, ipvt, qtf, wa1, wa2, wa3, wa4, evaluate, printout, data); + + if(printout != null) printout(n, par, m, fvec, data, -1, 0, control.nfev.val); + control.fnorm = lm_enorm(m, fvec); + if (control.info.val < 0) + control.info.val = 10; +} + + +// convenience functions; wrappers of lm_minimize + +/* + The structure FitControl specifies various control parameters. +*/ +struct FitControl { + real squareSumTolerance; // relative error desired in the sum of squares + real approximationTolerance; // relative error between last two approximations + real desiredOrthogonality; // orthogonality desired between the residue vector and its derivatives + real epsilon; // step used to calculate the jacobian + real stepBound; // initial bound to steps in the outer loop + int maxIterations; // maximum number of iterations + bool verbose; // whether to print detailed information about every iteration, or nothing + + void operator init(real squareSumTolerance=LM_USERTOL, real approximationTolerance=LM_USERTOL, real desiredOrthogonality=LM_USERTOL, real epsilon=LM_USERTOL, real stepBound=100, int maxIterations=100, bool verbose=false) { + this.squareSumTolerance = squareSumTolerance; + this.approximationTolerance = approximationTolerance; + this.desiredOrthogonality = desiredOrthogonality; + this.epsilon = epsilon; + this.stepBound = stepBound; + this.maxIterations = maxIterations; + this.verbose = verbose; + } + + FitControl copy() { + FitControl result = new FitControl; + result.squareSumTolerance = this.squareSumTolerance; + result.approximationTolerance = this.approximationTolerance; + result.desiredOrthogonality = this.desiredOrthogonality; + result.epsilon = this.epsilon; + result.stepBound = this.stepBound; + result.maxIterations = this.maxIterations; + result.verbose = this.verbose; + return result; + } +}; + +FitControl operator init() { + return FitControl(); +} + +FitControl defaultControl; + + +/* + Upon returning, this structure provides information about the fit. +*/ +struct FitResult { + real norm; // norm of the residue vector + int iterations; // actual number of iterations + int status; // status of minimization + + void operator init(real norm, int iterations, int status) { + this.norm = norm; + this.iterations = iterations; + this.status = status; + } +}; + + +/* + Fits data points to a function that depends on some parameters. + + Parameters: + - xdata: Array of x values. + - ydata: Array of y values. + - errors: Array of experimental errors; each element must be strictly positive + - function: Fit function. + - parameters: Parameter array. Before calling fit(), this must contain the initial guesses for the parameters. + Upon return, it will contain the solution parameters. + - control: object of type FitControl that controls various aspects of the fitting procedure. + + Returns: + An object of type FitResult that conveys information about the fitting process. +*/ +FitResult fit(real[] xdata, real[] ydata, real[] errors, real function(real[], real), real[] parameters, FitControl control=defaultControl) { + int m_dat = min(xdata.length, ydata.length); + int n_par = parameters.length; + lm_evaluate_ftype evaluate = lm_evaluate_default; + lm_print_ftype printout = control.verbose ? lm_print_default : lm_print_quiet; + + lm_data_type data; + data.user_t = xdata; + data.user_y = ydata; + data.user_w = 1 / errors; + data.user_func = new real(real x, real[] params) { + return function(params, x); + }; + + lm_control_type ctrl; + ctrl.ftol = control.squareSumTolerance; + ctrl.xtol = control.approximationTolerance; + ctrl.gtol = control.desiredOrthogonality; + ctrl.epsilon = control.epsilon; + ctrl.stepbound = control.stepBound; + ctrl.maxcall = control.maxIterations; + + lm_minimize(m_dat, n_par, parameters, evaluate, printout, data, ctrl); + + return FitResult(ctrl.fnorm, ctrl.nfev.val, ctrl.info.val); +} + + +/* + Fits data points to a function that depends on some parameters. + + Parameters: + - xdata: Array of x values. + - ydata: Array of y values. + - function: Fit function. + - parameters: Parameter array. Before calling fit(), this must contain the initial guesses for the parameters. + Upon return, it will contain the solution parameters. + - control: object of type FitControl that controls various aspects of the fitting procedure. + + Returns: + An object of type FitResult that conveys information about the fitting process. +*/ +FitResult fit(real[] xdata, real[] ydata, real function(real[], real), real[] parameters, FitControl control=defaultControl) { + return fit(xdata, ydata, array(min(xdata.length, ydata.length), 1.0), function, parameters, control); +} + diff --git a/Build/source/utils/asymptote/base/map.asy b/Build/source/utils/asymptote/base/map.asy new file mode 100644 index 00000000000..2b2277b9568 --- /dev/null +++ b/Build/source/utils/asymptote/base/map.asy @@ -0,0 +1,40 @@ +// Create a struct <name> parameterized by types <key> and <value>, +// that maps keys to values, defaulting to the value in <default>. +void mapTemplate(string name, string key, string value, string default) +{ + type(key,"Key"); + type(value,"Value"); + eval("Value default="+default,true); + + eval(" + struct keyValue { + Key key; + Value T; + void operator init(Key key) { + this.key=key; + } + void operator init(Key key, Value T) { + this.key=key; + this.T=T; + } + } + + struct map { + keyValue[] M; + bool operator < (keyValue a, keyValue b) {return a.key < b.key;} + + void add(Key key, Value T) { + keyValue m=keyValue(key,T); + M.insert(search(M,m,operator <)+1,m); + } + Value lookup(Key key) { + int i=search(M,keyValue(key),operator <); + if(i >= 0 && M[i].key == key) return M[i].T; + return default; + } + } +",true); + + type("map",name); +} + diff --git a/Build/source/utils/asymptote/base/markers.asy b/Build/source/utils/asymptote/base/markers.asy new file mode 100644 index 00000000000..84adc8e6cf6 --- /dev/null +++ b/Build/source/utils/asymptote/base/markers.asy @@ -0,0 +1,218 @@ +// Mark routines and markers written by Philippe Ivaldi. +// http://www.piprime.fr/ + +marker operator * (transform T, marker m) +{ + marker M=new marker; + M.f=T*m.f; + M.above=m.above; + M.markroutine=m.markroutine; + return M; +} + +// Add n frames f midway (in arclength) between n+1 uniformly spaced marks. +markroutine markinterval(int n=1, frame f, bool rotated=false) +{ + return new void(picture pic=currentpicture, frame mark, path g) { + markuniform(n+1,rotated)(pic,mark,g); + markuniform(centered=true,n,rotated)(pic,f,g); + }; +} + +// Return a frame containing n copies of the path g shifted by space +// drawn with pen p. +frame duplicate(path g, int n=1, pair space=0, pen p=currentpen) +{ + if(space == 0) space=dotsize(p); + frame f; + int pos=0; + int sign=1; + int m=(n+1) % 2; + for(int i=1; i <= n; ++i) { + draw(f,shift(space*(pos-0.5*m))*g,p); + pos += i*sign; + sign *= -1; + } + return f; +} + +real tildemarksizefactor=5; +real tildemarksize(pen p=currentpen) +{ + static real golden=(1+sqrt(5))/2; + return (1mm+tildemarksizefactor*sqrt(linewidth(p)))/golden; +} +frame tildeframe(int n=1, real size=0, pair space=0, + real angle=0, pair offset=0, pen p=currentpen) +{ + size=(size == 0) ? tildemarksize(p) : size; + space=(space == 0) ? 1.5*size : space; + path g=yscale(1.25)*((-1.5,-0.5)..(-0.75,0.5)..(0,0)..(0.75,-0.5)..(1.5,0.5)); + return duplicate(shift(offset)*rotate(angle)*scale(size)*g,n,space,p); +} + +frame tildeframe=tildeframe(); + +real stickmarkspacefactor=4; +real stickmarksizefactor=10; +real stickmarksize(pen p=currentpen) +{ + return 1mm+stickmarksizefactor*sqrt(linewidth(p)); +} +real stickmarkspace(pen p=currentpen) +{ + return stickmarkspacefactor*sqrt(linewidth(p)); +} +frame stickframe(int n=1, real size=0, pair space=0, real angle=0, + pair offset=0, pen p=currentpen) +{ + if(size == 0) size=stickmarksize(p); + if(space == 0) space=stickmarkspace(p); + return duplicate(shift(offset)*rotate(angle)*scale(0.5*size)*(N--S),n, + space,p); +} + +frame stickframe=stickframe(); + +real circlemarkradiusfactor=stickmarksizefactor/2; +real circlemarkradius(pen p=currentpen) +{ + static real golden=(1+sqrt(5))/2; + return (1mm+circlemarkradiusfactor*sqrt(linewidth(p)))/golden; +} +real barmarksizefactor=stickmarksizefactor; +real barmarksize(pen p=currentpen) +{ + return 1mm+barmarksizefactor*sqrt(linewidth(p)); +} +frame circlebarframe(int n=1, real barsize=0, + real radius=0,real angle=0, + pair offset=0, pen p=currentpen, + filltype filltype=NoFill, bool above=false) +{ + if(barsize == 0) barsize=barmarksize(p); + if(radius == 0) radius=circlemarkradius(p); + frame opic; + path g=circle(offset,radius); + frame f=stickframe(n,barsize,space=2*radius/(n+1),angle,offset,p); + if(above) { + add(opic,f); + filltype.fill(opic,g,p); + } else { + filltype.fill(opic,g,p); + add(opic,f); + } + return opic; +} + +real crossmarksizefactor=5; +real crossmarksize(pen p=currentpen) +{ + return 1mm+crossmarksizefactor*sqrt(linewidth(p)); +} +frame crossframe(int n=3, real size=0, pair space=0, + real angle=0, pair offset=0, pen p=currentpen) +{ + if(size == 0) size=crossmarksize(p); + frame opic; + draw(opic,shift(offset)*rotate(angle)*scale(size)*cross(n),p); + return opic; +} + +real markanglespacefactor=4; +real markangleradiusfactor=8; +real markangleradius(pen p=currentpen) +{ + return 8mm+markangleradiusfactor*sqrt(linewidth(p)); +} +real markangleradius=markangleradius(); +real markanglespace(pen p=currentpen) +{ + return markanglespacefactor*sqrt(linewidth(p)); +} +real markanglespace=markanglespace(); +// Mark the oriented angle AOB counterclockwise with optional Label, arrows, and markers. +// With radius < 0, AOB-2pi is marked clockwise. +void markangle(picture pic=currentpicture, Label L="", + int n=1, real radius=0, real space=0, + pair A, pair O, pair B, arrowbar arrow=None, + pen p=currentpen, filltype filltype=NoFill, + margin margin=NoMargin, marker marker=nomarker) +{ + if(space == 0) space=markanglespace(p); + if(radius == 0) radius=markangleradius(p); + picture lpic,phantom; + frame ff; + path lpth; + p=squarecap+p; + pair OB=unit(B-O), OA=unit(A-O); + real xoa=degrees(OA,false); + real gle=degrees(acos(dot(OA,OB))); + if((conj(OA)*OB).y < 0) gle *= -1; + bool ccw=radius > 0; + if(!ccw) radius=-radius; + bool drawarrow = !arrow(phantom,arc((0,0),radius,xoa,xoa+gle,ccw),p,margin); + if(drawarrow && margin == NoMargin) margin=TrueMargin(0,0.5linewidth(p)); + if(filltype != NoFill) { + lpth=margin(arc((0,0),radius+(n-1)*space,xoa,xoa+gle,ccw),p).g; + pair p0=relpoint(lpth,0), p1=relpoint(lpth,1); + pair ac=p0-p0-A+O, bd=p1-p1-B+O, det=(conj(ac)*bd).y; + pair op=(det == 0) ? O : p0+(conj(p1-p0)*bd).y*ac/det; + filltype.fill(ff,op--lpth--relpoint(lpth,1)--cycle,p); + add(lpic,ff); + } + for(int i=0; i < n; ++i) { + lpth=margin(arc((0,0),radius+i*space,xoa,xoa+gle,ccw),p).g; + draw(lpic,lpth,p=p,arrow=arrow,margin=NoMargin,marker=marker); + } + Label lL=L.copy(); + real position=lL.position.position.x; + if(lL.defaultposition) {lL.position.relative=true; position=0.5;} + if(lL.position.relative) position=reltime(lpth,position); + if(lL.align.default) { + lL.align.relative=true; + lL.align.dir=unit(point(lpth,position)); + } + label(lpic,lL,point(lpth,position),align=NoAlign, p=p); + add(pic,lpic,O); +} + +marker StickIntervalMarker(int i=2, int n=1, real size=0, real space=0, + real angle=0, pair offset=0, bool rotated=true, + pen p=currentpen, frame uniform=newframe, + bool above=true) +{ + return marker(uniform,markinterval(i,stickframe(n,size,space,angle,offset,p), + rotated),above); +} + + +marker CrossIntervalMarker(int i=2, int n=3, real size=0, real space=0, + real angle=0, pair offset=0, bool rotated=true, + pen p=currentpen, frame uniform=newframe, + bool above=true) +{ + return marker(uniform,markinterval(i,crossframe(n,size,space,angle,offset,p), + rotated=rotated),above); +} + +marker CircleBarIntervalMarker(int i=2, int n=1, real barsize=0, real radius=0, + real angle=0, pair offset=0, bool rotated=true, + pen p=currentpen, filltype filltype=NoFill, + bool circleabove=false, frame uniform=newframe, + bool above=true) +{ + return marker(uniform,markinterval(i,circlebarframe(n,barsize,radius,angle, + offset,p,filltype, + circleabove), + rotated),above); +} + +marker TildeIntervalMarker(int i=2, int n=1, real size=0, real space=0, + real angle=0, pair offset=0, bool rotated=true, + pen p=currentpen, frame uniform=newframe, + bool above=true) +{ + return marker(uniform,markinterval(i,tildeframe(n,size,space,angle,offset,p), + rotated),above); +} diff --git a/Build/source/utils/asymptote/base/math.asy b/Build/source/utils/asymptote/base/math.asy new file mode 100644 index 00000000000..3dde1b9dd4c --- /dev/null +++ b/Build/source/utils/asymptote/base/math.asy @@ -0,0 +1,451 @@ +// Asymptote mathematics routines + +int quadrant(real degrees) +{ + return floor(degrees/90) % 4; +} + +// Roots of unity. +pair unityroot(int n, int k=1) +{ + return expi(2pi*k/n); +} + +real csc(real x) {return 1/sin(x);} +real sec(real x) {return 1/cos(x);} +real cot(real x) {return tan(pi/2-x);} + +real acsc(real x) {return asin(1/x);} +real asec(real x) {return acos(1/x);} +real acot(real x) {return pi/2-atan(x);} + +real frac(real x) {return x-(int)x;} + +pair exp(explicit pair z) {return exp(z.x)*expi(z.y);} +pair log(explicit pair z) {return log(abs(z))+I*angle(z);} + +// Return an Nx by Ny unit square lattice with lower-left corner at (0,0). +picture grid(int Nx, int Ny, pen p=currentpen) +{ + picture pic; + for(int i=0; i <= Nx; ++i) draw(pic,(i,0)--(i,Ny),p); + for(int j=0; j <= Ny; ++j) draw(pic,(0,j)--(Nx,j),p); + return pic; +} + +bool polygon(path p) +{ + return cyclic(p) && piecewisestraight(p); +} + +// Return the intersection time of the point on the line through p and q +// that is closest to z. +real intersect(pair p, pair q, pair z) +{ + pair u=q-p; + real denom=dot(u,u); + return denom == 0 ? infinity : dot(z-p,u)/denom; +} + +// Return the intersection time of the extension of the line segment PQ +// with the plane perpendicular to n and passing through Z. +real intersect(triple P, triple Q, triple n, triple Z) +{ + real d=n.x*Z.x+n.y*Z.y+n.z*Z.z; + real denom=n.x*(Q.x-P.x)+n.y*(Q.y-P.y)+n.z*(Q.z-P.z); + return denom == 0 ? infinity : (d-n.x*P.x-n.y*P.y-n.z*P.z)/denom; +} + +// Return any point on the intersection of the two planes with normals +// n0 and n1 passing through points P0 and P1, respectively. +// If the planes are parallel return (infinity,infinity,infinity). +triple intersectionpoint(triple n0, triple P0, triple n1, triple P1) +{ + real Dx=n0.y*n1.z-n1.y*n0.z; + real Dy=n0.z*n1.x-n1.z*n0.x; + real Dz=n0.x*n1.y-n1.x*n0.y; + if(abs(Dx) > abs(Dy) && abs(Dx) > abs(Dz)) { + Dx=1/Dx; + real d0=n0.y*P0.y+n0.z*P0.z; + real d1=n1.y*P1.y+n1.z*P1.z+n1.x*(P1.x-P0.x); + real y=(d0*n1.z-d1*n0.z)*Dx; + real z=(d1*n0.y-d0*n1.y)*Dx; + return (P0.x,y,z); + } else if(abs(Dy) > abs(Dz)) { + Dy=1/Dy; + real d0=n0.z*P0.z+n0.x*P0.x; + real d1=n1.z*P1.z+n1.x*P1.x+n1.y*(P1.y-P0.y); + real z=(d0*n1.x-d1*n0.x)*Dy; + real x=(d1*n0.z-d0*n1.z)*Dy; + return (x,P0.y,z); + } else { + if(Dz == 0) return (infinity,infinity,infinity); + Dz=1/Dz; + real d0=n0.x*P0.x+n0.y*P0.y; + real d1=n1.x*P1.x+n1.y*P1.y+n1.z*(P1.z-P0.z); + real x=(d0*n1.y-d1*n0.y)*Dz; + real y=(d1*n0.x-d0*n1.x)*Dz; + return (x,y,P0.z); + } +} + +// Given a real array a, return its partial sums. +real[] partialsum(real[] a) +{ + real[] b=new real[a.length]; + real sum=0; + for(int i=0; i < a.length; ++i) { + sum += a[i]; + b[i]=sum; + } + return b; +} + +// Given a real array a, return its partial dx-weighted sums. +real[] partialsum(real[] a, real[] dx) +{ + real[] b=new real[a.length]; + real sum=0; + for(int i=0; i < a.length; ++i) { + sum += a[i]*dx[i]; + b[i]=sum; + } + return b; +} + +// Given an integer array a, return its partial sums. +int[] partialsum(int[] a) +{ + int[] b=new int[a.length]; + int sum=0; + for(int i=0; i < a.length; ++i) { + sum += a[i]; + b[i]=sum; + } + return b; +} + +// Given an integer array a, return its partial dx-weighted sums. +int[] partialsum(int[] a, int[] dx) +{ + int[] b=new int[a.length]; + int sum=0; + for(int i=0; i < a.length; ++i) { + sum += a[i]*dx[i]; + b[i]=sum; + } + return b; +} + +// If strict=false, return whether i > j implies a[i] >= a[j] +// If strict=true, return whether i > j implies a[i] > a[j] +bool increasing(real[] a, bool strict=false) +{ + real[] ap=copy(a); + ap.delete(0); + ap.push(0); + bool[] b=strict ? (ap > a) : (ap >= a); + b[a.length-1]=true; + return all(b); +} + +// Return the first and last indices of consecutive true-element segments +// of bool[] b. +int[][] segmentlimits(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=1; i < edge.length; ++i) { + int start=stop; + stop=edge[i]; + if(b[start]) + segment.push(new int[] {start,stop-1}); + } + return segment; +} + +// Return the indices of consecutive true-element segments of bool[] b. +int[][] segment(bool[] b) +{ + int[][] S=segmentlimits(b); + return sequence(new int[](int i) { + return sequence(S[i][0],S[i][1]); + },S.length); +} + +// If the sorted array a does not contain x, insert it sequentially, +// returning the index of x in the resulting array. +int unique(real[] a, real x) { + int i=search(a,x); + if(i == -1 || x != a[i]) { + ++i; + a.insert(i,x); + } + return i; +} + +int unique(string[] a, string x) { + int i=search(a,x); + if(i == -1 || x != a[i]) { + ++i; + a.insert(i,x); + } + return i; +} + +bool lexorder(pair a, pair b) { + return a.x < b.x || (a.x == b.x && a.y < b.y); +} + +bool lexorder(triple a, triple b) { + return a.x < b.x || (a.x == b.x && (a.y < b.y || (a.y == b.y && a.z < b.z))); +} + +real[] zero(int n) +{ + return sequence(new real(int) {return 0;},n); +} + +real[][] zero(int n, int m) +{ + real[][] M=new real[n][]; + for(int i=0; i < n; ++i) + M[i]=sequence(new real(int) {return 0;},m); + return M; +} + +bool square(real[][] m) +{ + int n=m.length; + for(int i=0; i < n; ++i) + if(m[i].length != n) return false; + return true; +} + +bool rectangular(real[][] m) +{ + int n=m.length; + if(n > 0) { + int m0=m[0].length; + for(int i=1; i < n; ++i) + if(m[i].length != m0) return false; + } + return true; +} + +bool rectangular(pair[][] m) +{ + int n=m.length; + if(n > 0) { + int m0=m[0].length; + for(int i=1; i < n; ++i) + if(m[i].length != m0) return false; + } + return true; +} + +bool rectangular(triple[][] m) +{ + int n=m.length; + if(n > 0) { + int m0=m[0].length; + for(int i=1; i < n; ++i) + if(m[i].length != m0) return false; + } + return true; +} + +// draw the (infinite) line going through P and Q, without altering the +// size of picture pic. +void drawline(picture pic=currentpicture, pair P, pair Q, pen p=currentpen) +{ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen. + m -= min(p); M -= max(p); + + // Calculate the points and direction vector in the transformed space. + t=t*T; + pair z=t*P; + pair v=t*Q-z; + + // Handle horizontal and vertical lines. + if(v.x == 0) { + if(m.x <= z.x && z.x <= M.x) + draw(f,(z.x,m.y)--(z.x,M.y),p); + } else if(v.y == 0) { + if(m.y <= z.y && z.y <= M.y) + draw(f,(m.x,z.y)--(M.x,z.y),p); + } else { + // Calculate the maximum and minimum t values allowed for the + // parametric equation z + t*v + real mx=(m.x-z.x)/v.x, Mx=(M.x-z.x)/v.x; + real my=(m.y-z.y)/v.y, My=(M.y-z.y)/v.y; + real tmin=max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My); + real tmax=min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my); + if(tmin <= tmax) + draw(f,z+tmin*v--z+tmax*v,p); + } + },true); +} + +real interpolate(real[] x, real[] y, real x0, int i) +{ + int n=x.length; + if(n == 0) abort("Zero data points in interpolate"); + if(n == 1) return y[0]; + if(i < 0) { + real dx=x[1]-x[0]; + return y[0]+(y[1]-y[0])/dx*(x0-x[0]); + } + if(i >= n-1) { + real dx=x[n-1]-x[n-2]; + return y[n-1]+(y[n-1]-y[n-2])/dx*(x0-x[n-1]); + } + + real D=x[i+1]-x[i]; + real B=(x0-x[i])/D; + real A=1.0-B; + return A*y[i]+B*y[i+1]; +} + +// Linearly interpolate data points (x,y) to (x0,y0), where the elements of +// real[] x are listed in ascending order and return y0. Values outside the +// available data range are linearly extrapolated using the first derivative +// at the nearest endpoint. +real interpolate(real[] x, real[] y, real x0) +{ + return interpolate(x,y,x0,search(x,x0)); +} + +private string nopoint="point not found"; + +// Return the nth intersection time of path g with the vertical line through x. +real time(path g, real x, int n=0) +{ + real[] t=times(g,x); + if(t.length <= n) abort(nopoint); + return t[n]; +} + +// Return the nth intersection time of path g with the horizontal line through +// (0,z.y). +real time(path g, explicit pair z, int n=0) +{ + real[] t=times(g,z); + if(t.length <= n) abort(nopoint); + return t[n]; +} + +// Return the nth y value of g at x. +real value(path g, real x, int n=0) +{ + return point(g,time(g,x,n)).y; +} + +// Return the nth x value of g at y=z.y. +real value(path g, explicit pair z, int n=0) +{ + return point(g,time(g,(0,z.y),n)).x; +} + +// Return the nth slope of g at x. +real slope(path g, real x, int n=0) +{ + pair a=dir(g,time(g,x,n)); + return a.y/a.x; +} + +// Return the nth slope of g at y=z.y. +real slope(path g, explicit pair z, int n=0) +{ + pair a=dir(g,time(g,(0,z.y),n)); + return a.y/a.x; +} + +// A quartic complex root solver based on these references: +// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html +// Neumark, S., Solution of Cubic and Quartic Equations, Pergamon Press +// Oxford (1965). +pair[] quarticroots(real a, real b, real c, real d, real e) +{ + real Fuzz=100000*realEpsilon; + + // Remove roots at numerical infinity. + if(abs(a) <= Fuzz*(abs(b)+Fuzz*(abs(c)+Fuzz*(abs(d)+Fuzz*abs(e))))) + return cubicroots(b,c,d,e); + + // Detect roots at numerical zero. + if(abs(e) <= Fuzz*(abs(d)+Fuzz*(abs(c)+Fuzz*(abs(b)+Fuzz*abs(a))))) + return cubicroots(a,b,c,d); + + real ainv=1/a; + b *= ainv; + c *= ainv; + d *= ainv; + e *= ainv; + + pair[] roots; + real[] T=cubicroots(1,-2c,c^2+b*d-4e,d^2+b^2*e-b*c*d); + if(T.length == 0) return roots; + + real t0=T[0]; + pair[] sum=quadraticroots((1,0),(b,0),(t0,0)); + pair[] product=quadraticroots((1,0),(t0-c,0),(e,0)); + + if(abs(sum[0]*product[0]+sum[1]*product[1]+d) < + abs(sum[0]*product[1]+sum[1]*product[0]+d)) + product=reverse(product); + + for(int i=0; i < 2; ++i) + roots.append(quadraticroots((1,0),-sum[i],product[i])); + + return roots; +} + +pair[][] fft(pair[][] a, int sign=1) +{ + pair[][] A=new pair[a.length][]; + int k=0; + for(pair[] v : a) { + A[k]=fft(v,sign); + ++k; + } + a=transpose(A); + k=0; + for(pair[] v : a) { + A[k]=fft(v,sign); + ++k; + } + return transpose(A); +} + +// Given a matrix A with independent columns, return +// the unique vector y minimizing |Ay - b|^2 (the L2 norm). +// If the columns of A are not linearly independent, +// throw an error (if warn == true) or return an empty array +// (if warn == false). +real[] leastsquares(real[][] A, real[] b, bool warn=true) +{ + real[] solution=solve(AtA(A),b*A,warn=false); + if (solution.length == 0 && warn) + abort("Cannot compute least-squares approximation for " + + "a matrix with linearly dependent columns."); + return solution; +} + +// Namespace +struct rootfinder_settings { + static real roottolerance=1e-4; +} + +real findroot(real f(real), real a, real b, + real tolerance=rootfinder_settings.roottolerance, + real fa=f(a), real fb=f(b)) +{ + return _findroot(f,a,b,tolerance,fa,fb); +} diff --git a/Build/source/utils/asymptote/base/metapost.asy b/Build/source/utils/asymptote/base/metapost.asy new file mode 100644 index 00000000000..6908fd750d2 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/nopapersize.ps b/Build/source/utils/asymptote/base/nopapersize.ps new file mode 100644 index 00000000000..67e31404744 --- /dev/null +++ b/Build/source/utils/asymptote/base/nopapersize.ps @@ -0,0 +1,3 @@ +@ a4size 0in 0in + +@ letterSize 0in 0in diff --git a/Build/source/utils/asymptote/base/obj.asy b/Build/source/utils/asymptote/base/obj.asy new file mode 100644 index 00000000000..5b6f14e90df --- /dev/null +++ b/Build/source/utils/asymptote/base/obj.asy @@ -0,0 +1,113 @@ +// A module for reading simple obj files with groups. +// Authors: Jens Schwaiger and John Bowman +// +// Here simple means that : +// +// 1) all vertex statements should come before the face statements; +// +// 2) face informations with respect to texture and/or normal vectors are +// ignored; +// +// 3) face statements only contain positive numbers(no relative positions). +// +// The reading process only takes into account lines starting with "v" or +// "f" or "g"(group). + +import three; + +struct obj { + surface s; + material[] surfacepen; + pen[] meshpen; + + path3[][] read(string datafile, bool verbose=false) { + file in=input(datafile).word().line(); + triple[] vert; + path3[][] g; + g[0]=new path3[] ; + string[] G; + void Vertex(real x,real y ,real z) {vert.push((x,y,z));} + void Face(int[] vertnr, int groupnr) { + guide3 gh; + for(int i=0; i < vertnr.length; ++i) + gh=gh--vert[vertnr[i]-1]; + gh=gh--cycle; + g[groupnr].push(gh); + } + if(verbose) write("Reading data from "+datafile+"."); + int groupnr; + while(true) { + string[] str=in; + if(str.length == 0) break; + str=sequence(new string(int i) {return split(str[i],"/")[0];},str.length); + if(str[0] == "g" && str.length > 1) { + int tst=find(G == str[1]); + if(tst == -1) { + G.push(str[1]); + groupnr=G.length-1; + g[groupnr]=new path3[] ; + } + if(tst > -1) groupnr=tst; + } + if(str[0] == "v") Vertex((real) str[1],(real) str[2],(real) str[3]); + if(str[0] == "f") { + int[] vertnr; + for(int i=1; i < str.length; ++i) vertnr[i-1]=(int) str[i]; + Face(vertnr,groupnr); + } + if(eof(in)) break; + } + close(in); + if(verbose) { + write("Number of groups: ",G.length); + write("Groups and their names:"); + write(G); + write("Reading done."); + write("Number of faces contained in the groups: "); + for(int j=0; j < G.length; ++j) + write(G[j],": ",(string) g[j].length); + } + return g; + } + + void operator init(path3[][] g, material[] surfacepen, pen[] meshpen) { + for(int i=0; i < g.length; ++i) { + path3[] gi=g[i]; + for(int j=0; j < gi.length; ++j) { + // Treat all faces as planar to avoid subdivision cracks. + surface sij=surface(gi[j],planar=true); + s.append(sij); + this.surfacepen.append(array(sij.s.length,surfacepen[i])); + this.meshpen.append(array(sij.s.length,meshpen[i])); + } + } + } + + void operator init(string datafile, bool verbose=false, + material[] surfacepen, pen[] meshpen=nullpens) { + operator init(read(datafile,verbose),surfacepen,meshpen); + } + + void operator init(string datafile, bool verbose=false, + material surfacepen, pen meshpen=nullpen) { + material[] surfacepen={surfacepen}; + pen[] meshpen={meshpen}; + surfacepen.cyclic=true; + meshpen.cyclic=true; + operator init(read(datafile,verbose),surfacepen,meshpen); + } +} + +obj operator * (transform3 T, obj o) +{ + obj ot; + ot.s=T*o.s; + ot.surfacepen=copy(o.surfacepen); + ot.meshpen=copy(o.meshpen); + return ot; +} + +void draw(picture pic=currentpicture, obj o, light light=currentlight) +{ + draw(pic,o.s,o.surfacepen,o.meshpen,light); +} diff --git a/Build/source/utils/asymptote/base/ode.asy b/Build/source/utils/asymptote/base/ode.asy new file mode 100644 index 00000000000..99f8505cec2 --- /dev/null +++ b/Build/source/utils/asymptote/base/ode.asy @@ -0,0 +1,457 @@ +real stepfactor=2; // Maximum dynamic step size adjustment factor. + +struct coefficients +{ + real[] steps; + real[] factors; + real[][] weights; + real[] highOrderWeights; + real[] lowOrderWeights; +} + +struct RKTableau +{ + int order; + coefficients a; + void stepDependence(real h, real c, coefficients a) {} + + real pgrow; + real pshrink; + bool exponential; + + void operator init(int order, real[][] weights, real[] highOrderWeights, + real[] lowOrderWeights=new real[], + real[] steps=sequence(new real(int i) { + return sum(weights[i]);},weights.length), + void stepDependence(real, real, coefficients)=null) { + this.order=order; + a.steps=steps; + a.factors=array(a.steps.length+1,1); + a.weights=weights; + a.highOrderWeights=highOrderWeights; + a.lowOrderWeights=lowOrderWeights; + if(stepDependence != null) { + this.stepDependence=stepDependence; + exponential=true; + } + pgrow=(order > 0) ? 1/order : 0; + pshrink=(order > 1) ? 1/(order-1) : pgrow; + } +} + +real[] Coeff={1,1/2,1/6,1/24,1/120,1/720,1/5040,1/40320,1/362880,1/3628800, + 1/39916800.0,1/479001600.0,1/6227020800.0,1/87178291200.0, + 1/1307674368000.0,1/20922789888000.0,1/355687428096000.0, + 1/6402373705728000.0,1/121645100408832000.0, + 1/2432902008176640000.0,1/51090942171709440000.0, + 1/1124000727777607680000.0}; + +real phi1(real x) {return x != 0 ? expm1(x)/x : 1;} + +real phi2(real x) +{ + real x2=x*x; + if(fabs(x) > 1) return (exp(x)-x-1)/x2; + real x3=x2*x; + real x5=x2*x3; + if(fabs(x) < 0.1) + return Coeff[1]+x*Coeff[2]+x2*Coeff[3]+x3*Coeff[4]+x2*x2*Coeff[5] + +x5*Coeff[6]+x3*x3*Coeff[7]+x5*x2*Coeff[8]+x5*x3*Coeff[9]; + else { + real x7=x5*x2; + real x8=x7*x; + return Coeff[1]+x*Coeff[2]+x2*Coeff[3]+x3*Coeff[4]+x2*x2*Coeff[5] + +x5*Coeff[6]+x3*x3*Coeff[7]+x7*Coeff[8]+x8*Coeff[9] + +x8*x*Coeff[10]+x5*x5*Coeff[11]+x8*x3*Coeff[12]+x7*x5*Coeff[13]+ + x8*x5*Coeff[14]+x7*x7*Coeff[15]+x8*x7*Coeff[16]+x8*x8*Coeff[17]; + } +} + +real phi3(real x) +{ + real x2=x*x; + real x3=x2*x; + if(fabs(x) > 1.6) return (exp(x)-0.5*x2-x-1)/x3; + real x5=x2*x3; + if(fabs(x) < 0.1) + return Coeff[2]+x*Coeff[3]+x2*Coeff[4]+x3*Coeff[5] + +x2*x2*Coeff[6]+x5*Coeff[7]+x3*x3*Coeff[8]+x5*x2*Coeff[9] + +x5*x3*Coeff[10]; + else { + real x7=x5*x2; + real x8=x7*x; + real x16=x8*x8; + return Coeff[2]+x*Coeff[3]+x2*Coeff[4]+x3*Coeff[5] + +x2*x2*Coeff[6]+x5*Coeff[7]+x3*x3*Coeff[8]+x5*x2*Coeff[9] + +x5*x3*Coeff[10]+x8*x*Coeff[11] + +x5*x5*Coeff[12]+x8*x3*Coeff[13]+x7*x5*Coeff[14] + +x8*x5*Coeff[15]+x7*x7*Coeff[16]+x8*x7*Coeff[17]+x16*Coeff[18] + +x16*x*Coeff[19]+x16*x2*Coeff[20]; + } +} + +void expfactors(real x, coefficients a) +{ + for(int i=0; i < a.steps.length; ++i) + a.factors[i]=exp(x*a.steps[i]); + a.factors[a.steps.length]=exp(x); +} + +// First-Order Euler +RKTableau Euler=RKTableau(1,new real[][], new real[] {1}); + +// First-Order Exponential Euler +RKTableau E_Euler=RKTableau(1,new real[][], new real[] {1}, + new void(real h, real c, coefficients a) { + real x=-c*h; + expfactors(x,a); + a.highOrderWeights[0]=phi1(x); + }); + +// Second-Order Runge-Kutta +RKTableau RK2=RKTableau(2,new real[][] {{1/2}}, + new real[] {0,1}, // 2nd order + new real[] {1,0}); // 1st order + +// Second-Order Exponential Runge-Kutta +RKTableau E_RK2=RKTableau(2,new real[][] {{1/2}}, + new real[] {0,1}, // 2nd order + new real[] {1,0}, // 1st order + new void(real h, real c, coefficients a) { + real x=-c*h; + expfactors(x,a); + a.weights[0][0]=1/2*phi1(x/2); + real w=phi1(x); + a.highOrderWeights[0]=0; + a.highOrderWeights[1]=w; + a.lowOrderWeights[0]=w; + }); + +// Second-Order Predictor-Corrector +RKTableau PC=RKTableau(2,new real[][] {{1}}, + new real[] {1/2,1/2}, // 2nd order + new real[] {1,0}); // 1st order + +// Second-Order Exponential Predictor-Corrector +RKTableau E_PC=RKTableau(2,new real[][] {{1}}, + new real[] {1/2,1/2}, // 2nd order + new real[] {1,0}, // 1st order + new void(real h, real c, coefficients a) { + real x=-c*h; + expfactors(x,a); + real w=phi1(x); + a.weights[0][0]=w; + a.highOrderWeights[0]=w/2; + a.highOrderWeights[1]=w/2; + a.lowOrderWeights[0]=w; + }); + +// Third-Order Classical Runge-Kutta +RKTableau RK3=RKTableau(3,new real[][] {{1/2},{-1,2}}, + new real[] {1/6,2/3,1/6}); + +// Third-Order Bogacki-Shampine Runge-Kutta +RKTableau RK3BS=RKTableau(3,new real[][] {{1/2},{0,3/4}}, + new real[] {2/9,1/3,4/9}, // 3rd order + new real[] {7/24,1/4,1/3,1/8}); // 2nd order + +// Third-Order Exponential Bogacki-Shampine Runge-Kutta +RKTableau E_RK3BS=RKTableau(3,new real[][] {{1/2},{0,3/4}}, + new real[] {2/9,1/3,4/9}, // 3rd order + new real[] {7/24,1/4,1/3,1/8}, // 2nd order + new void(real h, real c, coefficients a) { + real x=-c*h; + expfactors(x,a); + real w=phi1(x); + real w2=phi2(x); + a.weights[0][0]=1/2*phi1(x/2); + real a11=9/8*phi2(3/4*x)+3/8*phi2(x/2); + a.weights[1][0]=3/4*phi1(3/4*x)-a11; + a.weights[1][1]=a11; + real a21=1/3*w; + real a22=4/3*w2-2/9*w; + a.highOrderWeights[0]=w-a21-a22; + a.highOrderWeights[1]=a21; + a.highOrderWeights[2]=a22; + a.lowOrderWeights[0]=w-17/12*w2; + a.lowOrderWeights[1]=w2/2; + a.lowOrderWeights[2]=2/3*w2; + a.lowOrderWeights[3]=w2/4; + }); + +// Fourth-Order Classical Runge-Kutta +RKTableau RK4=RKTableau(4,new real[][] {{1/2},{0,1/2},{0,0,1}}, + new real[] {1/6,1/3,1/3,1/6}); + +// Fifth-Order Cash-Karp Runge-Kutta +RKTableau RK5=RKTableau(5,new real[][] {{1/5}, + {3/40,9/40}, + {3/10,-9/10,6/5}, + {-11/54,5/2,-70/27,35/27}, + {1631/55296,175/512,575/13824, + 44275/110592,253/4096}}, + new real[] {37/378,0,250/621,125/594, + 0,512/1771}, // 5th order + new real[] {2825/27648,0,18575/48384,13525/55296, + 277/14336,1/4}); // 4th order + +// Fifth-Order Fehlberg Runge-Kutta +RKTableau RK5F=RKTableau(5,new real[][] {{1/4}, + {3/32,9/32}, + {1932/2197,-7200/2197,7296/2197}, + {439/216,-8,3680/513,-845/4104}, + {-8/27,2,-3544/2565,1859/4104, + -11/40}}, + new real[] {16/135,0,6656/12825,28561/56430,-9/50,2/55}, // 5th order + new real[] {25/216,0,1408/2565,2197/4104,-1/5,0}); // 4th order + +// Fifth-Order Dormand-Prince Runge-Kutta +RKTableau RK5DP=RKTableau(5,new real[][] {{1/5}, + {3/40,9/40}, + {44/45,-56/15,32/9}, + {19372/6561,-25360/2187,64448/6561, + -212/729}, + {9017/3168,-355/33,46732/5247,49/176, + -5103/18656}}, + new real[] {35/384,0,500/1113,125/192,-2187/6784, + 11/84}, // 5th order + new real[] {5179/57600,0,7571/16695,393/640, + -92097/339200,187/2100,1/40}); // 4th order + +real error(real error, real initial, real lowOrder, real norm, real diff) +{ + if(initial != 0 && lowOrder != initial) { + static real epsilon=realMin/realEpsilon; + real denom=max(abs(norm),abs(initial))+epsilon; + return max(error,max(abs(diff)/denom)); + } + return error; +} + +void report(real old, real h, real t) +{ + write("Time step changed from "+(string) old+" to "+(string) h+" at t="+ + (string) t+"."); +} + +real adjust(real h, real error, real tolmin, real tolmax, RKTableau tableau) +{ + if(error > tolmax) + h *= max((tolmin/error)^tableau.pshrink,1/stepfactor); + else if(error > 0 && error < tolmin) + h *= min((tolmin/error)^tableau.pgrow,stepfactor); + return h; +} + +struct solution +{ + real[] t; + real[] y; +} + +void write(solution S) +{ + for(int i=0; i < S.t.length; ++i) + write(S.t[i],S.y[i]); +} + +// Integrate dy/dt+cy=f(t,y) from a to b using initial conditions y, +// specifying either the step size h or the number of steps n. +solution integrate(real y, real c=0, real f(real t, real y), real a, real b=a, + real h=0, int n=0, bool dynamic=false, real tolmin=0, + real tolmax=0, real dtmin=0, real dtmax=realMax, + RKTableau tableau, bool verbose=false) +{ + solution S; + S.t=new real[] {a}; + S.y=new real[] {y}; + + if(h == 0) { + if(b == a) return S; + if(n == 0) abort("Either n or h must be specified"); + else h=(b-a)/n; + } + + real F(real t, real y)=(c == 0 || tableau.exponential) ? f : + new real(real t, real y) {return f(t,y)-c*y;}; + + tableau.stepDependence(h,c,tableau.a); + + real t=a; + real f0; + if(tableau.a.lowOrderWeights.length == 0) dynamic=false; + bool fsal=dynamic && + (tableau.a.lowOrderWeights.length > tableau.a.highOrderWeights.length); + if(fsal) f0=F(t,y); + + real dt=h; + while(t < b) { + h=min(h,b-t); + if(t+h == t) break; + if(h != dt) { + if(verbose) report(dt,h,t); + tableau.stepDependence(h,c,tableau.a); + dt=h; + } + + real[] predictions={fsal ? f0 : F(t,y)}; + for(int i=0; i < tableau.a.steps.length; ++i) + predictions.push(F(t+h*tableau.a.steps[i], + tableau.a.factors[i]*y+h*dot(tableau.a.weights[i], + predictions))); + + real highOrder=h*dot(tableau.a.highOrderWeights,predictions); + real y0=tableau.a.factors[tableau.a.steps.length]*y; + if(dynamic) { + real f1; + if(fsal) { + f1=F(t+h,y0+highOrder); + predictions.push(f1); + } + real lowOrder=h*dot(tableau.a.lowOrderWeights,predictions); + real error; + error=error(error,y,y0+lowOrder,y0+highOrder,highOrder-lowOrder); + h=adjust(h,error,tolmin,tolmax,tableau); + if(h >= dt) { + t += dt; + y=y0+highOrder; + S.t.push(t); + S.y.push(y); + f0=f1; + } + h=min(max(h,dtmin),dtmax); + } else { + t += h; + y=y0+highOrder; + S.t.push(t); + S.y.push(y); + } + } + return S; +} + +struct Solution +{ + real[] t; + real[][] y; +} + +void write(Solution S) +{ + for(int i=0; i < S.t.length; ++i) { + write(S.t[i],tab); + for(real y : S.y[i]) + write(y,tab); + write(); + } +} + +// Integrate a set of equations, dy/dt=f(t,y), from a to b using initial +// conditions y, specifying either the step size h or the number of steps n. +Solution integrate(real[] y, real[] f(real t, real[] y), real a, real b=a, + real h=0, int n=0, bool dynamic=false, + real tolmin=0, real tolmax=0, real dtmin=0, + real dtmax=realMax, RKTableau tableau, bool verbose=false) +{ + Solution S; + S.t=new real[] {a}; + S.y=new real[][] {copy(y)}; + + if(h == 0) { + if(b == a) return S; + if(n == 0) abort("Either n or h must be specified"); + else h=(b-a)/n; + } + real t=a; + real[] f0; + if(tableau.a.lowOrderWeights.length == 0) dynamic=false; + bool fsal=dynamic && + (tableau.a.lowOrderWeights.length > tableau.a.highOrderWeights.length); + if(fsal) f0=f(t,y); + + real dt=h; + while(t < b) { + h=min(h,b-t); + if(t+h == t) break; + if(h != dt) { + if(verbose) report(dt,h,t); + dt=h; + } + + real[][] predictions={fsal ? f0 : f(t,y)}; + for(int i=0; i < tableau.a.steps.length; ++i) + predictions.push(f(t+h*tableau.a.steps[i], + y+h*tableau.a.weights[i]*predictions)); + + real[] highOrder=h*tableau.a.highOrderWeights*predictions; + if(dynamic) { + real[] f1; + if(fsal) { + f1=f(t+h,y+highOrder); + predictions.push(f1); + } + real[] lowOrder=h*tableau.a.lowOrderWeights*predictions; + real error; + for(int i=0; i < y.length; ++i) + error=error(error,y[i],y[i]+lowOrder[i],y[i]+highOrder[i], + highOrder[i]-lowOrder[i]); + h=adjust(h,error,tolmin,tolmax,tableau); + if(h >= dt) { + t += dt; + y += highOrder; + S.t.push(t); + S.y.push(y); + f0=f1; + } + h=min(max(h,dtmin),dtmax); + } else { + t += h; + y += highOrder; + S.t.push(t); + S.y.push(y); + } + } + return S; +} + +real[][] finiteDifferenceJacobian(real[] f(real[]), real[] t, + real[] h=sqrtEpsilon*abs(t)) +{ + real[] ft=f(t); + real[][] J=new real[t.length][ft.length]; + real[] ti=copy(t); + real tlast=ti[0]; + ti[0] += h[0]; + J[0]=(f(ti)-ft)/h[0]; + for(int i=1; i < t.length; ++i) { + ti[i-1]=tlast; + tlast=ti[i]; + ti[i] += h[i]; + J[i]=(f(ti)-ft)/h[i]; + } + return transpose(J); +} + +// Solve simultaneous nonlinear system by Newton's method. +real[] newton(int iterations=100, real[] f(real[]), real[][] jacobian(real[]), + real[] t) +{ + real[] t=copy(t); + for(int i=0; i < iterations; ++i) + t += solve(jacobian(t),-f(t)); + return t; +} + +real[] solveBVP(real[] f(real, real[]), real a, real b=a, real h=0, int n=0, + bool dynamic=false, real tolmin=0, real tolmax=0, real dtmin=0, + real dtmax=realMax, RKTableau tableau, bool verbose=false, + real[] initial(real[]), real[] discrepancy(real[]), + real[] guess, int iterations=100) +{ + real[] g(real[] t) { + real[][] y=integrate(initial(t),f,a,b,h,n,dynamic,tolmin,tolmax,dtmin,dtmax, + tableau,verbose).y;return discrepancy(y[y.length-1]); + } + real[][] jacobian(real[] t) {return finiteDifferenceJacobian(g,t);} + return initial(newton(iterations,g,jacobian,guess)); +} diff --git a/Build/source/utils/asymptote/base/palette.asy b/Build/source/utils/asymptote/base/palette.asy new file mode 100644 index 00000000000..9923e5b3aaf --- /dev/null +++ b/Build/source/utils/asymptote/base/palette.asy @@ -0,0 +1,537 @@ +private import graph; + +private transform swap=(0,0,0,1,1,0); + +typedef bounds range(picture pic, real min, real max); + +range Range(bool automin=false, real min=-infinity, + bool automax=false, real max=infinity) +{ + return new bounds(picture pic, real dmin, real dmax) { + // autoscale routine finds reasonable limits + bounds mz=autoscale(pic.scale.z.T(dmin), + pic.scale.z.T(dmax), + pic.scale.z.scale); + // If automin/max, use autoscale result, else + // if min/max is finite, use specified value, else + // use minimum/maximum data value + real pmin=automin ? pic.scale.z.Tinv(mz.min) : (finite(min) ? min : dmin); + real pmax=automax ? pic.scale.z.Tinv(mz.max) : (finite(max) ? max : dmax); + return bounds(pmin,pmax); + }; +} + +range Automatic=Range(true,true); +range Full=Range(); + +void image(frame f, real[][] data, pair initial, pair final, pen[] palette, + bool transpose=(initial.x < final.x && initial.y < final.y), + transform t=identity(), bool copy=true, bool antialias=false) +{ + transform T=transpose ? swap : identity(); + _image(f,copy ? copy(data) : data,T*initial,T*final,palette,t*T,copy=false, + antialias=antialias); +} + +void image(frame f, pen[][] data, pair initial, pair final, + bool transpose=(initial.x < final.x && initial.y < final.y), + transform t=identity(), bool copy=true, bool antialias=false) +{ + transform T=transpose ? swap : identity(); + _image(f,copy ? copy(data) : data,T*initial,T*final,t*T,copy=false, + antialias=antialias); +} + +// Reduce color palette to approximate range of data relative to "display" +// range => errors of 1/palette.length in resulting color space. +pen[] adjust(picture pic, real min, real max, real rmin, real rmax, + pen[] palette) +{ + real dmin=pic.scale.z.T(min); + real dmax=pic.scale.z.T(max); + real delta=rmax-rmin; + if(delta > 0) { + real factor=palette.length/delta; + int minindex=floor(factor*(dmin-rmin)); + if(minindex < 0) minindex=0; + int maxindex=ceil(factor*(dmax-rmin)); + if(maxindex > palette.length) maxindex=palette.length; + if(minindex > 0 || maxindex < palette.length) + return palette[minindex:maxindex]; + } + return palette; +} + +private real[] sequencereal; + +bounds image(picture pic=currentpicture, real[][] f, range range=Full, + pair initial, pair final, pen[] palette, + bool transpose=(initial.x < final.x && initial.y < final.y), + bool copy=true, bool antialias=false) +{ + if(copy) f=copy(f); + if(copy) palette=copy(palette); + + real m=min(f); + real M=max(f); + bounds bounds=range(pic,m,M); + real rmin=pic.scale.z.T(bounds.min); + real rmax=pic.scale.z.T(bounds.max); + palette=adjust(pic,m,M,rmin,rmax,palette); + + // Crop data to allowed range and scale + if(range != Full || pic.scale.z.scale.T != identity || + pic.scale.z.postscale.T != identity) { + scalefcn T=pic.scale.z.T; + real m=bounds.min; + real M=bounds.max; + for(int i=0; i < f.length; ++i) + f[i]=map(new real(real x) {return T(min(max(x,m),M));},f[i]); + } + + initial=Scale(pic,initial); + final=Scale(pic,final); + + pic.addBox(initial,final); + + transform T; + if(transpose) { + T=swap; + initial=T*initial; + final=T*final; + } + + pic.add(new void(frame F, transform t) { + _image(F,f,initial,final,palette,t*T,copy=false,antialias=antialias); + },true); + 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)/ny)); + 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)/nx)),y); + },nx); + } + return image(pic,data,range,initial,final,palette,transpose=false, + copy=false,antialias=antialias); +} + +void image(picture pic=currentpicture, pen[][] data, pair initial, pair final, + bool transpose=(initial.x < final.x && initial.y < final.y), + bool copy=true, bool antialias=false) +{ + if(copy) data=copy(data); + + initial=Scale(pic,initial); + final=Scale(pic,final); + + pic.addBox(initial,final); + + transform T; + if(transpose) { + T=swap; + initial=T*initial; + final=T*final; + } + + pic.add(new void(frame F, transform t) { + _image(F,data,initial,final,t*T,copy=false,antialias=antialias); + },true); +} + +void image(picture pic=currentpicture, pen f(int, int), int width, int height, + pair initial, pair final, + bool transpose=(initial.x < final.x && initial.y < final.y), + bool antialias=false) +{ + initial=Scale(pic,initial); + final=Scale(pic,final); + + pic.addBox(initial,final); + + transform T; + if(transpose) { + T=swap; + int temp=width; + width=height; + height=temp; + initial=T*initial; + final=T*final; + } + + pic.add(new void(frame F, transform t) { + _image(F,f,width,height,initial,final,t*T,antialias=antialias); + },true); +} + +bounds image(picture pic=currentpicture, pair[] z, real[] f, + range range=Full, pen[] palette) +{ + if(z.length != f.length) + abort("z and f arrays have different lengths"); + + real m=min(f); + real M=max(f); + bounds bounds=range(pic,m,M); + real rmin=pic.scale.z.T(bounds.min); + real rmax=pic.scale.z.T(bounds.max); + + palette=adjust(pic,m,M,rmin,rmax,palette); + rmin=max(rmin,pic.scale.z.T(m)); + rmax=min(rmax,pic.scale.z.T(M)); + + // Crop data to allowed range and scale + if(range != Full || pic.scale.z.scale.T != identity || + pic.scale.z.postscale.T != identity) { + scalefcn T=pic.scale.z.T; + real m=bounds.min; + real M=bounds.max; + f=map(new real(real x) {return T(min(max(x,m),M));},f); + } + if(pic.scale.x.scale.T != identity || pic.scale.x.postscale.T != identity || + pic.scale.y.scale.T != identity || pic.scale.y.postscale.T != identity) { + scalefcn Tx=pic.scale.x.T; + scalefcn Ty=pic.scale.y.T; + z=map(new pair(pair z) {return (Tx(z.x),Ty(z.y));},z); + } + + 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; + pen[][] p=new pen[n][]; + 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)];}, + f[i].length); + } + 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(); +paletteticks NoTicks=new ticks(int sign=-1) {return NoTicks;}; + +void palette(picture pic=currentpicture, Label L="", bounds bounds, + pair initial, pair final, axis axis=Right, pen[] palette, + pen p=currentpen, paletteticks ticks=PaletteTicks, + bool copy=true, bool antialias=false) +{ + real initialz=pic.scale.z.T(bounds.min); + real finalz=pic.scale.z.T(bounds.max); + bounds mz=autoscale(initialz,finalz,pic.scale.z.scale); + + axisT axis; + axis(pic,axis); + real angle=degrees(axis.align.dir); + + initial=Scale(pic,initial); + final=Scale(pic,final); + + pair lambda=final-initial; + bool vertical=(floor((angle+45)/90) % 2 == 0); + pair perp,par; + + if(vertical) {perp=E; par=N;} else {perp=N; par=E;} + + path g=(final-dot(lambda,par)*par)--final; + path g2=initial--final-dot(lambda,perp)*perp; + + if(sgn(dot(lambda,perp)*dot(axis.align.dir,perp)) == -1) { + path tmp=g; + g=g2; + g2=tmp; + } + + if(copy) palette=copy(palette); + Label L=L.copy(); + if(L.defaultposition) L.position(0.5); + L.align(axis.align); + L.p(p); + if(vertical && L.defaulttransform) { + frame f; + add(f,Label(L.s,(0,0),L.p)); + if(length(max(f)-min(f)) > ylabelwidth*fontsize(L.p)) + L.transform(rotate(90)); + } + real[][] pdata={sequence(palette.length)}; + + transform T; + pair Tinitial,Tfinal; + if(vertical) { + T=swap; + Tinitial=T*initial; + Tfinal=T*final; + } else { + Tinitial=initial; + Tfinal=final; + } + + pic.add(new void(frame f, transform t) { + _image(f,pdata,Tinitial,Tfinal,palette,t*T,copy=false, + antialias=antialias); + },true); + + ticklocate locate=ticklocate(initialz,finalz,pic.scale.z,mz.min,mz.max); + axis(pic,L,g,g2,p,ticks(sgn(axis.side.x*dot(lambda,par))),locate,mz.divisor, + true); + + pic.add(new void(frame f, transform t) { + pair Z0=t*initial; + pair Z1=t*final; + draw(f,Z0--(Z0.x,Z1.y)--Z1--(Z1.x,Z0.y)--cycle,p); + },true); + + pic.addBox(initial,final); +} + +// A grayscale palette +pen[] Grayscale(int NColors=256) +{ + real ninv=1.0/(NColors-1.0); + return sequence(new pen(int i) {return gray(i*ninv);},NColors); +} + +// A color wheel palette +pen[] Wheel(int NColors=32766) +{ + if(settings.gray) return Grayscale(NColors); + + int nintervals=6; + if(NColors <= nintervals) NColors=nintervals+1; + int n=-quotient(NColors,-nintervals); + + pen[] 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; + if(NColors <= nintervals) NColors=nintervals+1; + int n=-quotient(NColors-1,-nintervals); + + pen[] 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 Nintervals=nintervals*divisor; + if(NColors <= Nintervals) NColors=Nintervals+1; + int num=NColors-offset; + int n=-quotient(num,-Nintervals)*divisor; + NColors=n*nintervals+offset; + + pen[] 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/Build/source/utils/asymptote/base/patterns.asy b/Build/source/utils/asymptote/base/patterns.asy new file mode 100644 index 00000000000..559e36cec11 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/plain.asy b/Build/source/utils/asymptote/base/plain.asy new file mode 100644 index 00000000000..365aad02c74 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain.asy @@ -0,0 +1,308 @@ +/***** + * plain.asy + * Andy Hammerlindl and John Bowman 2004/08/19 + * + * A package for general purpose drawing, with automatic sizing of pictures. + * + *****/ + +access settings; + +if(settings.command != "") { + string s=settings.command; + settings.command=""; + settings.multipleView=settings.batchView=settings.interactiveView; + _eval(s+";",false,true); + exit(); +} + +include plain_constants; + +access version; +if(version.VERSION != VERSION) { + warning("version","using possibly incompatible version "+ + version.VERSION+" of plain.asy"+'\n'); + nowarn("version"); +} + +include plain_strings; +include plain_pens; +include plain_paths; +include plain_filldraw; +include plain_margins; +include plain_picture; +include plain_Label; +include plain_arcs; +include plain_boxes; +include plain_shipout; +include plain_markers; +include plain_arrows; +include plain_debugger; + +real RELEASE=(real) split(VERSION,"-")[0]; + +typedef void exitfcn(); + +void updatefunction() +{ + implicitshipout=true; + if(!currentpicture.uptodate) shipout(); + implicitshipout=false; +} + +void exitfunction() +{ + implicitshipout=true; + if(!currentpicture.empty()) + shipout(); + implicitshipout=false; +} + +atupdate(updatefunction); +atexit(exitfunction); + +// A restore thunk is a function, that when called, restores the graphics state +// to what it was when the restore thunk was created. +typedef void restoreThunk(); +typedef restoreThunk saveFunction(); +saveFunction[] saveFunctions={}; + +// When save is called, this will be redefined to do the corresponding restore. +void restore() +{ + warning("nomatchingsave","restore called with no matching save"); +} + +void addSaveFunction(saveFunction s) +{ + saveFunctions.push(s); +} + +restoreThunk buildRestoreThunk() +{ + // Call the save functions in reverse order, storing their restore thunks. + restoreThunk[] thunks={}; + for (int i=saveFunctions.length-1; i >= 0; --i) + thunks.push(saveFunctions[i]()); + + return new void() { + // Call the restore thunks in an order matching the saves. + for (int i=thunks.length-1; i >= 0; --i) + thunks[i](); + }; +} + +// Add the default save function. +addSaveFunction(new restoreThunk () { + pen defaultpen=defaultpen(); + pen p=currentpen; + picture pic=currentpicture.copy(); + restoreThunk r=restore; + return new void() { + defaultpen(defaultpen); + currentpen=p; + currentpicture=pic; + currentpicture.uptodate=false; + restore=r; + }; + }); + +// Save the current state, so that restore will put things back in that state. +restoreThunk save() +{ + return restore=buildRestoreThunk(); +} + +void restoredefaults() +{ + warning("nomatchingsavedefaults", + "restoredefaults called with no matching savedefaults"); +} + +restoreThunk buildRestoreDefaults() +{ + pen defaultpen=defaultpen(); + exitfcn atupdate=atupdate(); + exitfcn atexit=atexit(); + restoreThunk r=restoredefaults; + return new void() { + defaultpen(defaultpen); + atupdate(atupdate); + atexit(atexit); + restoredefaults=r; + }; +} + +// Save the current state, so that restore will put things back in that state. +restoreThunk savedefaults() +{ + return restoredefaults=buildRestoreDefaults(); +} + +void initdefaults() +{ + savedefaults(); + resetdefaultpen(); + atupdate(null); + atexit(null); +} + +// Return the sequence n,...,m +int[] sequence(int n, int m) +{ + return sequence(new int(int x){return x;},m-n+1)+n; +} + +int[] reverse(int n) {return sequence(new int(int x){return n-1-x;},n);} +bool[] reverse(bool[] a) {return a[reverse(a.length)];} +int[] reverse(int[] a) {return a[reverse(a.length)];} +real[] reverse(real[] a) {return a[reverse(a.length)];} +pair[] reverse(pair[] a) {return a[reverse(a.length)];} +triple[] reverse(triple[] a) {return a[reverse(a.length)];} +string[] reverse(string[] a) {return a[reverse(a.length)];} + +// Return a uniform partition dividing [a,b] into n subintervals. +real[] uniform(real a, real b, int n) +{ + if(n <= 0) return new real[]; + return a+(b-a)/n*sequence(n+1); +} + +void eval(string s, bool embedded=false) +{ + if(!embedded) initdefaults(); + _eval(s+";",embedded); + if(!embedded) restoredefaults(); +} + +void eval(code s, bool embedded=false) +{ + if(!embedded) initdefaults(); + _eval(s,embedded); + if(!embedded) restoredefaults(); +} + +// Associate a parametrized type with a name. +void type(string type, string name) +{ + eval("typedef "+type+" "+name,true); +} + +void mapArray(string From, string To) +{ + type(From,"From"); + type(To,"To"); + eval("To[] map(To f(From), From[] a) {return sequence(new To(int i) {return f(a[i]);},a.length);}",true); +} + +// Evaluate user command line option. +void usersetting() +{ + eval(settings.user,true); +} + +string stripsuffix(string f, string suffix=".asy") +{ + int n=rfind(f,suffix); + if(n != -1) f=erase(f,n,-1); + return f; +} + +string outdirectory() +{ + return stripfile(outprefix()); +} + +// Conditionally process each file name in array s in a new environment. +void asy(string format, bool overwrite=false ... string[] s) +{ + for(string f : s) { + f=stripsuffix(f); + string suffix="."+format; + string fsuffix=stripdirectory(f+suffix); + if(overwrite || error(input(outdirectory()+fsuffix,check=false))) { + string outformat=settings.outformat; + bool interactiveView=settings.interactiveView; + bool batchView=settings.batchView; + settings.outformat=format; + settings.interactiveView=false; + settings.batchView=false; + string outname=outname(); + delete(outname+"_"+".aux"); + eval("import \""+f+"\" as dummy"); + rename(stripsuffix(outname)+suffix,fsuffix); + settings.outformat=outformat; + settings.interactiveView=interactiveView; + settings.batchView=batchView; + } + } +} + +void beep() +{ + write('\7',flush); +} + +struct processtime { + real user; + real system; + real clock; +} + +struct cputime { + processtime parent; + processtime child; + processtime change; +} + +cputime cputime() +{ + static processtime last; + real [] a=_cputime(); + cputime cputime; + real clock=a[4]; + cputime.parent.user=a[0]; + cputime.parent.system=a[1]; + cputime.parent.clock=clock; + cputime.child.user=a[2]; + cputime.child.system=a[3]; + cputime.child.clock=0; + real user=cputime.parent.user+cputime.child.user; + real system=cputime.parent.system+cputime.child.system; + cputime.change.user=user-last.user; + cputime.change.system=system-last.system; + cputime.change.clock=clock-last.clock; + last.user=user; + last.system=system; + last.clock=clock; + 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); + atupdate(updatefunction); + atexit(exitfunction); + settings.autoimport=s; +} + +cputime(); diff --git a/Build/source/utils/asymptote/base/plain_Label.asy b/Build/source/utils/asymptote/base/plain_Label.asy new file mode 100644 index 00000000000..0a2c270d689 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_Label.asy @@ -0,0 +1,691 @@ +real angle(transform t) +{ + pair z=(2t.xx*t.yy,t.yx*t.yy-t.xx*t.xy); + if(t.xx < 0 || t.yy < 0) z=-z; + return degrees(z,warn=false); +} + +transform rotation(transform t) +{ + return rotate(angle(t)); +} + +transform scaleless(transform t) +{ + real a=t.xx, b=t.xy, c=t.yx, d=t.yy; + real arg=(a-d)^2+4b*c; + pair delta=arg >= 0 ? sqrt(arg) : I*sqrt(-arg); + real trace=a+d; + pair l1=0.5(trace+delta); + pair l2=0.5(trace-delta); + + if(abs(delta) < sqrtEpsilon*max(abs(l1),abs(l2))) { + real s=abs(0.5trace); + return (s != 0) ? scale(1/s)*t : t; + } + + if(abs(l1-d) < abs(l2-d)) {pair temp=l1; l1=l2; l2=temp;} + + pair dot(pair[] u, pair[] v) {return conj(u[0])*v[0]+conj(u[1])*v[1];} + + pair[] unit(pair[] u) { + real norm2=abs(u[0])^2+abs(u[1])^2; + return norm2 != 0 ? u/sqrt(norm2) : u; + } + + pair[] u={l1-d,b}; + pair[] v={c,l2-a}; + u=unit(u); + pair d=dot(u,u); + if(d != 0) v -= dot(u,v)/d*u; + v=unit(v); + + pair[][] U={{u[0],v[0]},{u[1],v[1]}}; + pair[][] A={{a,b},{c,d}}; + + pair[][] operator *(pair[][] a, pair[][] b) { + pair[][] c=new pair[2][2]; + for(int i=0; i < 2; ++i) { + for(int j=0; j < 2; ++j) { + c[i][j]=a[i][0]*b[0][j]+a[i][1]*b[1][j]; + } + } + return c; + } + + pair[][] conj(pair[][] a) { + pair[][] c=new pair[2][2]; + for(int i=0; i < 2; ++i) { + for(int j=0; j < 2; ++j) { + c[i][j]=conj(a[j][i]); + } + } + return c; + } + + A=conj(U)*A*U; + + real D=abs(A[0][0]); + if(D != 0) { + A[0][0] /= D; + A[0][1] /= D; + } + + D=abs(A[1][1]); + if(D != 0) { + A[1][0] /= D; + A[1][1] /= D; + } + + A=U*A*conj(U); + + return (0,0,A[0][0].x,A[0][1].x,A[1][0].x,A[1][1].x); +} + +struct align { + pair dir; + triple dir3; + bool relative=false; + bool default=true; + bool is3D=false; + void init(pair dir=0, bool relative=false, bool default=false) { + this.dir=dir; + this.relative=relative; + this.default=default; + is3D=false; + } + void init(triple dir=(0,0,0), bool relative=false, bool default=false) { + this.dir3=dir; + this.relative=relative; + this.default=default; + is3D=true; + } + align copy() { + align align=new align; + align.init(dir,relative,default); + align.dir3=dir3; + align.is3D=is3D; + return align; + } + void align(align align) { + if(!align.default) { + bool is3D=align.is3D; + init(align.dir,align.relative); + dir3=align.dir3; + this.is3D=is3D; + } + } + void align(align align, align default) { + align(align); + if(this.default) { + init(default.dir,default.relative,default.default); + dir3=default.dir3; + is3D=default.is3D; + } + } + void write(file file=stdout, suffix suffix=endl) { + if(!default) { + if(relative) { + write(file,"Relative("); + if(is3D) + write(file,dir3); + else + write(file,dir); + write(file,")",suffix); + } else { + if(is3D) + write(file,dir3,suffix); + else + write(file,dir,suffix); + } + } + } + bool Center() { + return relative && (is3D ? dir3 == (0,0,0) : dir == 0); + } +} + +struct side { + pair align; +} + +side Relative(explicit pair align) +{ + side s; + s.align=align; + return s; +} + +restricted side NoSide; +restricted side LeftSide=Relative(W); +restricted side Center=Relative((0,0)); +restricted side RightSide=Relative(E); + +side operator * (real x, side s) +{ + side S; + S.align=x*s.align; + return S; +} + +align operator cast(pair dir) {align A; A.init(dir,false); return A;} +align operator cast(triple dir) {align A; A.init(dir,false); return A;} +align operator cast(side side) {align A; A.init(side.align,true); return A;} +restricted align NoAlign; + +void write(file file=stdout, align align, suffix suffix=endl) +{ + align.write(file,suffix); +} + +struct position { + pair position; + bool relative; +} + +position Relative(real position) +{ + position p; + p.position=position; + p.relative=true; + return p; +} + +restricted position BeginPoint=Relative(0); +restricted position MidPoint=Relative(0.5); +restricted position EndPoint=Relative(1); + +position operator cast(pair x) {position P; P.position=x; return P;} +position operator cast(real x) {return (pair) x;} +position operator cast(int x) {return (pair) x;} + +pair operator cast(position P) {return P.position;} + +typedef transform embed(transform); +transform Shift(transform t) {return identity();} +transform Rotate(transform t) {return rotation(t);} +transform Slant(transform t) {return scaleless(t);} +transform Scale(transform t) {return t;} + +embed Rotate(pair z) { + return new transform(transform t) {return rotate(degrees(shiftless(t)*z, + warn=false));}; +} + +path[] texpath(string s, pen p, bool tex=settings.tex != "none", + bool bbox=false); + +struct Label { + string s,size; + position position; + bool defaultposition=true; + align align; + pen p=nullpen; + transform T; + transform3 T3=identity(4); + bool defaulttransform=true; + bool defaulttransform3=true; + embed embed=Rotate; // Shift, Rotate, Slant, or Scale with embedded picture + filltype filltype=NoFill; + + void init(string s="", string size="", position position=0, + bool defaultposition=true, align align=NoAlign, pen p=nullpen, + transform T=identity(), transform3 T3=identity4, + bool defaulttransform=true, bool defaulttransform3=true, + embed embed=Rotate, filltype filltype=NoFill) { + this.s=s; + this.size=size; + this.position=position; + this.defaultposition=defaultposition; + this.align=align.copy(); + this.p=p; + this.T=T; + this.T3=copy(T3); + this.defaulttransform=defaulttransform; + this.defaulttransform3=defaulttransform3; + this.embed=embed; + this.filltype=filltype; + } + + void initalign(string s="", string size="", align align, pen p=nullpen, + embed embed=Rotate, filltype filltype=NoFill) { + init(s,size,align,p,embed,filltype); + } + + void transform(transform T) { + this.T=T; + defaulttransform=false; + } + + void transform3(transform3 T) { + this.T3=copy(T); + defaulttransform3=false; + } + + Label copy(transform3 T3=this.T3) { + Label L=new Label; + L.init(s,size,position,defaultposition,align,p,T,T3,defaulttransform, + defaulttransform3,embed,filltype); + return L; + } + + void position(position pos) { + this.position=pos; + defaultposition=false; + } + + void align(align a) { + align.align(a); + } + void align(align a, align default) { + align.align(a,default); + } + + void p(pen p0) { + if(this.p == nullpen) this.p=p0; + } + + void filltype(filltype filltype0) { + if(this.filltype == NoFill) this.filltype=filltype0; + } + + void label(frame f, transform t=identity(), pair position, pair align) { + pen p0=p == nullpen ? currentpen : p; + align=length(align)*unit(rotation(t)*align); + pair S=t*position+align*labelmargin(p0)+shift(T)*0; + if(settings.tex != "none") + label(f,s,size,embed(t)*shiftless(T),S,align,p0); + else + fill(f,align(texpath(s,p0),S,align,p0),p0); + } + + void out(frame f, transform t=identity(), pair position=position.position, + 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 ? + inverse(rotation(t))*-Align*dir(t*g,position)*I : Align); + },!alignrelative); + + frame f; + pair align=alignrelative ? -Align*dir(g,position)*I : Align; + label(f,(0,0),align); + pair position=point(g,position); + pic.addBox(position,position,min(f),max(f)); + } + + void write(file file=stdout, suffix suffix=endl) { + write(file,"\""+s+"\""); + if(!defaultposition) write(file,", position=",position.position); + if(!align.default) write(file,", align="); + write(file,align); + if(p != nullpen) write(file,", pen=",p); + if(!defaulttransform) + write(file,", transform=",T); + if(!defaulttransform3) { + write(file,", transform3=",endl); + write(file,T3); + } + write(file,"",suffix); + } + + real relative() { + return defaultposition ? 0.5 : position.position.x; + }; + + real relative(path g) { + return position.relative ? reltime(g,relative()) : relative(); + }; +} + +Label Label; + +void add(frame f, transform t=identity(), Label L) +{ + L.out(f,t); +} + +void add(picture pic=currentpicture, Label L) +{ + L.out(pic); +} + +Label operator * (transform t, Label L) +{ + Label tL=L.copy(); + tL.align.dir=L.align.dir; + tL.transform(t*L.T); + return tL; +} + +Label operator * (transform3 t, Label L) +{ + Label tL=L.copy(t*L.T3); + tL.align.dir=L.align.dir; + tL.defaulttransform3=false; + return tL; +} + +Label Label(string s, string size="", explicit position position, + align align=NoAlign, pen p=nullpen, embed embed=Rotate, + filltype filltype=NoFill) +{ + Label L; + L.init(s,size,position,false,align,p,embed,filltype); + return L; +} + +Label Label(string s, string size="", pair position, align align=NoAlign, + pen p=nullpen, embed embed=Rotate, filltype filltype=NoFill) +{ + return Label(s,size,(position) position,align,p,embed,filltype); +} + +Label Label(explicit pair position, align align=NoAlign, pen p=nullpen, + embed embed=Rotate, filltype filltype=NoFill) +{ + return Label((string) position,position,align,p,embed,filltype); +} + +Label Label(string s="", string size="", align align=NoAlign, pen p=nullpen, + embed embed=Rotate, filltype filltype=NoFill) +{ + Label L; + L.initalign(s,size,align,p,embed,filltype); + return L; +} + +Label Label(Label L, align align=NoAlign, pen p=nullpen, embed embed=L.embed, + filltype filltype=NoFill) +{ + Label L=L.copy(); + L.align(align); + L.p(p); + L.embed=embed; + L.filltype(filltype); + return L; +} + +Label Label(Label L, explicit position position, align align=NoAlign, + pen p=nullpen, embed embed=L.embed, filltype filltype=NoFill) +{ + Label L=Label(L,align,p,embed,filltype); + L.position(position); + return L; +} + +Label Label(Label L, pair position, align align=NoAlign, + pen p=nullpen, embed embed=L.embed, filltype filltype=NoFill) +{ + return Label(L,(position) position,align,p,embed,filltype); +} + +void write(file file=stdout, Label L, suffix suffix=endl) +{ + L.write(file,suffix); +} + +void label(frame f, Label L, pair position, align align=NoAlign, + pen p=currentpen, filltype filltype=NoFill) +{ + add(f,Label(L,position,align,p,filltype)); +} + +void label(frame f, Label L, align align=NoAlign, + pen p=currentpen, filltype filltype=NoFill) +{ + add(f,Label(L,L.position,align,p,filltype)); +} + +void label(picture pic=currentpicture, Label L, pair position, + align align=NoAlign, pen p=currentpen, filltype filltype=NoFill) +{ + Label L=Label(L,position,align,p,filltype); + add(pic,L); +} + +void label(picture pic=currentpicture, Label L, align align=NoAlign, + pen p=currentpen, filltype filltype=NoFill) +{ + label(pic,L,L.position,align,p,filltype); +} + +// Label, but with postscript coords instead of asy +void label(pair origin, picture pic=currentpicture, Label L, align align=NoAlign, + pen p=currentpen, filltype filltype=NoFill) +{ + picture opic; + label(opic,L,L.position,align,p,filltype); + add(pic,opic,origin); +} + +void label(picture pic=currentpicture, Label L, explicit path g, + align align=NoAlign, pen p=currentpen, filltype filltype=NoFill) +{ + Label L=Label(L,align,p,filltype); + L.out(pic,g); +} + +void label(picture pic=currentpicture, Label L, explicit guide g, + align align=NoAlign, pen p=currentpen, filltype filltype=NoFill) +{ + label(pic,L,(path) g,align,p,filltype); +} + +Label operator cast(string s) {return Label(s);} + +// A structure that a string, Label, or frame can be cast to. +struct object { + frame f; + Label L=Label; + path g; // Bounding path + + void operator init(frame f) { + this.f=f; + g=box(min(f),max(f)); + } + + void operator init(Label L) { + this.L=L.copy(); + if(L != Label) L.out(f); + g=box(min(f),max(f)); + } +} + +object operator cast(frame f) { + return object(f); +} + +object operator cast(Label L) +{ + return object(L); +} + +object operator cast(string s) +{ + return object(s); +} + +Label operator cast(object F) +{ + return F.L; +} + +frame operator cast(object F) +{ + return F.f; +} + +object operator * (transform t, explicit object F) +{ + object f; + f.f=t*F.f; + f.L=t*F.L; + f.g=t*F.g; + return f; +} + +// Returns a copy of object F aligned in the direction align +object align(object F, pair align) +{ + return shift(F.f,align)*F; +} + +void add(picture dest=currentpicture, object F, pair position=0, + bool group=true, filltype filltype=NoFill, bool above=true) +{ + add(dest,F.f,position,group,filltype,above); +} + +// Pack a list of objects into a frame. +frame pack(pair align=2S ... object inset[]) +{ + frame F; + int n=inset.length; + pair z; + for (int i=0; i < n; ++i) { + add(F,inset[i].f,z); + z += align+realmult(unit(align),size(inset[i].f)); + } + return F; +} + +path[] texpath(Label L, bool tex=settings.tex != "none", bool bbox=false) +{ + struct stringfont + { + string s; + real fontsize; + string font; + + void operator init(Label L) + { + s=replace(L.s,'\n',' '); + fontsize=fontsize(L.p); + font=font(L.p); + } + + pen pen() {return fontsize(fontsize)+fontcommand(font);} + } + + bool lexorder(stringfont a, stringfont b) { + return a.s < b.s || (a.s == b.s && (a.fontsize < b.fontsize || + (a.fontsize == b.fontsize && + a.font < b.font))); + } + + static stringfont[] stringcache; + static path[][] pathcache; + + static stringfont[] stringlist; + static bool adjust[]; + + path[] G; + + stringfont s=stringfont(L); + pen p=s.pen(); + + int i=search(stringcache,s,lexorder); + if(i == -1 || lexorder(stringcache[i],s)) { + int k=search(stringlist,s,lexorder); + if(k == -1 || lexorder(stringlist[k],s)) { + ++k; + stringlist.insert(k,s); + // PDF tex engines lose track of the baseline. + adjust.insert(k,tex && basealign(L.p) == 1 && pdf()); + } + } + + path[] transform(path[] g, Label L) { + if(g.length == 0) return g; + pair m=min(g); + pair M=max(g); + pair dir=rectify(inverse(L.T)*-L.align.dir); + if(tex && basealign(L.p) == 1) + dir -= (0,(1-dir.y)*m.y/(M.y-m.y)); + pair a=m+realmult(dir,M-m); + + return shift(L.position+L.align.dir*labelmargin(L.p))*L.T*shift(-a)*g; + } + + if(tex && bbox) { + frame f; + label(f,L); + return transform(box(min(f),max(f)),L); + } + + if(stringlist.length > 0) { + path[][] g; + int n=stringlist.length; + string[] s=new string[n]; + pen[] p=new pen[n]; + for(int i=0; i < n; ++i) { + stringfont S=stringlist[i]; + s[i]=adjust[i] ? "."+S.s : S.s; + p[i]=adjust[i] ? S.pen()+basealign : S.pen(); + } + + g=tex ? _texpath(s,p) : textpath(s,p); + + if(tex) + for(int i=0; i < n; ++i) + if(adjust[i]) { + real y=min(g[i][0]).y; + g[i].delete(0); + g[i]=shift(0,-y)*g[i]; + } + + + for(int i=0; i < stringlist.length; ++i) { + stringfont s=stringlist[i]; + int j=search(stringcache,s,lexorder)+1; + stringcache.insert(j,s); + pathcache.insert(j,g[i]); + } + stringlist.delete(); + adjust.delete(); + } + + return transform(pathcache[search(stringcache,stringfont(L),lexorder)],L); +} + +texpath=new path[](string s, pen p, bool tex=settings.tex != "none", bool bbox=false) + { + return texpath(Label(s,p)); + }; diff --git a/Build/source/utils/asymptote/base/plain_arcs.asy b/Build/source/utils/asymptote/base/plain_arcs.asy new file mode 100644 index 00000000000..11c603b6152 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_arcs.asy @@ -0,0 +1,44 @@ +bool CCW=true; +bool CW=false; + +path circle(pair c, real r) +{ + return shift(c)*scale(r)*unitcircle; +} + +path ellipse(pair c, real a, real b) +{ + return shift(c)*scale(a,b)*unitcircle; +} + +// return an arc centered at c from pair z1 to z2 (assuming |z2-c|=|z1-c|), +// drawing in the given direction. +path arc(pair c, explicit pair z1, explicit pair z2, bool direction=CCW) +{ + z1 -= c; + real r=abs(z1); + z1=unit(z1); + z2=unit(z2-c); + + real t1=intersect(unitcircle,(0,0)--2*z1)[0]; + real t2=intersect(unitcircle,(0,0)--2*z2)[0]; + static int n=length(unitcircle); + if(direction) { + if (t1 >= t2) t1 -= n; + } else if(t2 >= t1) t2 -= n; + return shift(c)*scale(r)*subpath(unitcircle,t1,t2); +} + +// return an arc centered at c with radius r from angle1 to angle2 in degrees, +// drawing in the given direction. +path arc(pair c, real r, real angle1, real angle2, bool direction) +{ + return arc(c,c+r*dir(angle1),c+r*dir(angle2),direction); +} + +// return an arc centered at c with radius r > 0 from angle1 to angle2 in +// degrees, drawing counterclockwise if angle2 >= angle1 (otherwise clockwise). +path arc(pair c, real r, real angle1, real angle2) +{ + return arc(c,r,angle1,angle2,angle2 >= angle1 ? CCW : CW); +} diff --git a/Build/source/utils/asymptote/base/plain_arrows.asy b/Build/source/utils/asymptote/base/plain_arrows.asy new file mode 100644 index 00000000000..79ee403af3d --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_arrows.asy @@ -0,0 +1,649 @@ +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 default=0) +{ + real[][] Tl=transpose(intersections(left,base)); + real[][] Tr=transpose(intersections(right,base)); + return new real[] {Tl.length > 0 ? Tl[0][0] : default, + Tr.length > 0 ? Tr[0][0] : default}; +} + +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*factor,x)*r; + path right=rotate(angle*factor,x)*r; + real[] T=arrowbasepoints(base,left,right); + return subpath(left,0,T[0])--subpath(right,T[1],0)&cycle; +}; + +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); + path left=rotate(-angle,x)*r; + path right=rotate(angle,x)*r; + return subpath(left,t,0)--subpath(right,0,t); +}; + +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,1); + pair denom=point(right,T[1])-y; + real factor=denom != 0 ? length((point(left,T[0])-y)/denom) : 1; + path left=rotate(-angle*factor,x)*r; + path right=rotate(angle*factor,x)*r; + real[] T=arrowbasepoints(base,left,right,1); + left=subpath(left,0,T[0]); + right=subpath(right,T[1],0); + pair pl0=point(left,0), pl1=relpoint(left,1); + pair pr0=relpoint(right,0), pr1=relpoint(right,1); + pair M=(pl1+pr0)/2; + pair v=barb*unit(M-pl0); + pl1=pl1+v; pr0=pr0+v; + left=pl0{dir(-dir+degrees(M-pl0,false))}..pl1--M; + right=M--pr0..pr1{dir(dir+degrees(pr1-M,false))}; + return left--right&cycle; + }; + return a; +} +arrowhead HookHead=HookHead(); + +arrowhead TeXHead; +TeXHead.size=new real(pen p) + { + static real hcoef=2.1; // 84/40=abs(base-hint)/base_height + return hcoef*arrowtexfactor*linewidth(p); + }; +TeXHead.arcsize=TeXHead.size; + +TeXHead.head=new path(path g, position position=EndPoint, pen p=currentpen, + real size=0, real angle=arrowangle) { + static real wcoef=1/84; // 1/abs(base-hint) + static path texhead=scale(wcoef)* + ((0,20) .. controls (-75,75) and (-108,158) .. + (-108,166) .. controls (-108,175) and (-100,178) .. + (-93,178) .. controls (-82,178) and (-80,173) .. + (-77,168) .. controls (-62,134) and (-30,61) .. + (70,14) .. controls (82,8) and (84,7) .. + (84,0) .. controls (84,-7) and (82,-8) .. + (70,-14) .. controls (-30,-61) and (-62,-134) .. + (-77,-168) .. controls (-80,-173) and (-82,-178) .. + (-93,-178) .. controls (-100,-178) and (-108,-175).. + (-108,-166).. controls (-108,-158) and (-75,-75) .. + (0,-20)--cycle); + if(size == 0) size=TeXHead.size(p); + path gp=scale(size)*texhead; + bool relative=position.relative; + real position=position.position.x; + if(relative) position=reltime(g,position); + path r=subpath(g,position,0); + pair y=point(r,arctime(r,size)); + return shift(y)*rotate(degrees(-dir(r,arctime(r,0.5*size))))*gp; +}; +TeXHead.defaultfilltype=new filltype(pen p) {return Fill(p);}; + +private real position(position position, real size, path g, bool center) +{ + bool relative=position.relative; + real position=position.position.x; + if(relative) { + position *= arclength(g); + if(center) position += 0.5*size; + position=arctime(g,position); + } else if(center) + position=arctime(g,arclength(subpath(g,0,position))+0.5*size); + return position; +} + +void drawarrow(frame f, arrowhead arrowhead=DefaultHead, + path g, pen p=currentpen, real size=0, + real angle=arrowangle, + filltype filltype=null, + position position=EndPoint, bool forwards=true, + margin margin=NoMargin, bool center=false) +{ + if(size == 0) size=arrowhead.size(p); + if(filltype == null) filltype=arrowhead.defaultfilltype(p); + size=min(arrowsizelimit*arclength(g),size); + real position=position(position,size,g,center); + + g=margin(g,p).g; + int L=length(g); + if(!forwards) { + g=reverse(g); + position=L-position; + } + path r=subpath(g,position,0); + size=min(arrowsizelimit*arclength(r),size); + path head=arrowhead.head(g,position,p,size,angle); + bool endpoint=position > L-sqrtEpsilon; + if(cyclic(head) && (filltype == NoFill || endpoint)) { + if(position > 0) + draw(f,subpath(r,arctime(r,size),length(r)),p); + if(!endpoint) + draw(f,subpath(g,position,L),p); + } else draw(f,g,p); + filltype.fill(f,head,p+solid); +} + +void drawarrow2(frame f, arrowhead arrowhead=DefaultHead, + path g, pen p=currentpen, real size=0, + real angle=arrowangle, filltype filltype=null, + margin margin=NoMargin) +{ + if(size == 0) size=arrowhead.size(p); + if(filltype == null) filltype=arrowhead.defaultfilltype(p); + g=margin(g,p).g; + size=min(arrow2sizelimit*arclength(g),size); + + path r=reverse(g); + int L=length(g); + path head=arrowhead.head(g,L,p,size,angle); + path tail=arrowhead.head(r,L,p,size,angle); + if(cyclic(head)) + draw(f,subpath(r,arctime(r,size),L-arctime(g,size)),p); + else draw(f,g,p); + filltype.fill(f,head,p+solid); + filltype.fill(f,tail,p+solid); +} + +// Add to picture an estimate of the bounding box contribution of arrowhead +// using the local slope at endpoint and ignoring margin. +void addArrow(picture pic, arrowhead arrowhead, path g, pen p, real size, + real angle, filltype filltype, real position) +{ + if(filltype == null) filltype=arrowhead.defaultfilltype(p); + pair z=point(g,position); + path g=z-(size+linewidth(p))*dir(g,position)--z; + frame f; + filltype.fill(f,arrowhead.head(g,position,p,size,angle),p); + pic.addBox(z,z,min(f)-z,max(f)-z); +} + +picture arrow(arrowhead arrowhead=DefaultHead, + path g, pen p=currentpen, real size=0, + real angle=arrowangle, filltype filltype=null, + position position=EndPoint, bool forwards=true, + margin margin=NoMargin, bool center=false) +{ + if(size == 0) size=arrowhead.size(p); + picture pic; + pic.add(new void(frame f, transform t) { + drawarrow(f,arrowhead,t*g,p,size,angle,filltype,position,forwards,margin, + center); + }); + + pic.addPath(g,p); + + real position=position(position,size,g,center); + path G; + if(!forwards) { + G=reverse(g); + position=length(g)-position; + } else G=g; + addArrow(pic,arrowhead,G,p,size,angle,filltype,position); + + return pic; +} + +picture arrow2(arrowhead arrowhead=DefaultHead, + path g, pen p=currentpen, real size=0, + real angle=arrowangle, filltype filltype=null, + margin margin=NoMargin) +{ + if(size == 0) size=arrowhead.size(p); + picture pic; + pic.add(new void(frame f, transform t) { + drawarrow2(f,arrowhead,t*g,p,size,angle,filltype,margin); + }); + + pic.addPath(g,p); + + int L=length(g); + addArrow(pic,arrowhead,g,p,size,angle,filltype,L); + addArrow(pic,arrowhead,reverse(g),p,size,angle,filltype,L); + + return pic; +} + +void bar(picture pic, pair a, pair d, pen p=currentpen) +{ + picture opic; + Draw(opic,-0.5d--0.5d,p+solid); + add(pic,opic,a); +} + +picture bar(pair a, pair d, pen p=currentpen) +{ + picture pic; + bar(pic,a,d,p); + return pic; +} + +typedef bool arrowbar(picture, path, pen, margin); + +bool Blank(picture, path, pen, margin) +{ + return false; +} + +bool None(picture, path, pen, margin) +{ + return true; +} + +arrowbar BeginArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=BeginPoint) +{ + return new bool(picture pic, path g, pen p, margin margin) { + add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,forwards=false, + margin)); + return false; + }; +} + +arrowbar Arrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=EndPoint) +{ + return new bool(picture pic, path g, pen p, margin margin) { + add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,margin)); + return false; + }; +} + +arrowbar EndArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=EndPoint)=Arrow; + +arrowbar MidArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arrowangle, filltype filltype=null) +{ + return new bool(picture pic, path g, pen p, margin margin) { + add(pic,arrow(arrowhead,g,p,size,angle,filltype,MidPoint,margin, + center=true)); + return false; + }; +} + +arrowbar Arrows(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arrowangle, + filltype filltype=null) +{ + return new bool(picture pic, path g, pen p, margin margin) { + add(pic,arrow2(arrowhead,g,p,size,angle,filltype,margin)); + return false; + }; +} + +arrowbar BeginArcArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arcarrowangle, + filltype filltype=null, position position=BeginPoint) +{ + return new bool(picture pic, path g, pen p, margin margin) { + real size=size == 0 ? arrowhead.arcsize(p) : size; + add(pic,arrow(arrowhead,g,p,size,angle,filltype,position, + forwards=false,margin)); + return false; + }; +} + +arrowbar ArcArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arcarrowangle, + filltype filltype=null, position position=EndPoint) +{ + return new bool(picture pic, path g, pen p, margin margin) { + real size=size == 0 ? arrowhead.arcsize(p) : size; + add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,margin)); + return false; + }; +} + +arrowbar EndArcArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arcarrowangle, + filltype filltype=null, + position position=EndPoint)=ArcArrow; + +arrowbar MidArcArrow(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arcarrowangle, + filltype filltype=null) +{ + return new bool(picture pic, path g, pen p, margin margin) { + real size=size == 0 ? arrowhead.arcsize(p) : size; + add(pic,arrow(arrowhead,g,p,size,angle,filltype,MidPoint,margin, + center=true)); + return false; + }; +} + +arrowbar ArcArrows(arrowhead arrowhead=DefaultHead, + real size=0, real angle=arcarrowangle, + filltype filltype=null) +{ + return new bool(picture pic, path g, pen p, margin margin) { + real size=size == 0 ? arrowhead.arcsize(p) : size; + add(pic,arrow2(arrowhead,g,p,size,angle,filltype,margin)); + return false; + }; +} + +arrowbar BeginBar(real size=0) +{ + return new bool(picture pic, path g, pen p, margin margin) { + real size=size == 0 ? barsize(p) : size; + bar(pic,point(g,0),size*dir(g,0)*I,p); + return true; + }; +} + +arrowbar Bar(real size=0) +{ + return new bool(picture pic, path g, pen p, margin margin) { + int L=length(g); + real size=size == 0 ? barsize(p) : size; + bar(pic,point(g,L),size*dir(g,L)*I,p); + return true; + }; +} + +arrowbar EndBar(real size=0)=Bar; + +arrowbar Bars(real size=0) +{ + return new bool(picture pic, path g, pen p, margin margin) { + real size=size == 0 ? barsize(p) : size; + BeginBar(size)(pic,g,p,margin); + EndBar(size)(pic,g,p,margin); + return true; + }; +} + +arrowbar BeginArrow=BeginArrow(), +MidArrow=MidArrow(), +Arrow=Arrow(), +EndArrow=Arrow(), +Arrows=Arrows(), +BeginArcArrow=BeginArcArrow(), +MidArcArrow=MidArcArrow(), +ArcArrow=ArcArrow(), +EndArcArrow=ArcArrow(), +ArcArrows=ArcArrows(), +BeginBar=BeginBar(), +Bar=Bar(), +EndBar=Bar(), +Bars=Bars(); + +void draw(frame f, path g, pen p=currentpen, arrowbar arrow) +{ + picture pic; + if(arrow(pic,g,p,NoMargin)) + draw(f,g,p); + add(f,pic.fit()); +} + +void draw(picture pic=currentpicture, Label L=null, path g, + align align=NoAlign, pen p=currentpen, arrowbar arrow=None, + arrowbar bar=None, margin margin=NoMargin, Label legend=null, + marker marker=nomarker) +{ + // These if statements are ordered in such a way that the most common case + // (with just a path and a pen) executes the least bytecode. + if (marker == nomarker) + { + if (arrow == None && bar == None) + { + if (margin == NoMargin && size(nib(p)) == 0) + { + pic.addExactAbove( + new void(frame f, transform t, transform T, pair, pair) { + _draw(f,t*T*g,p); + }); + pic.addPath(g,p); + + // Jumping over else clauses takes time, so test if we can return + // here. + if (L == null && legend == null) + return; + } + else // With margin or polygonal pen. + { + _draw(pic, g, p, margin); + } + } + else /* arrow or bar */ + { + // Note we are using & instead of && as both arrow and bar need to be + // called. + if (arrow(pic, g, p, margin) & bar(pic, g, p, margin)) + _draw(pic, g, p, margin); + } + + if(L != null && L.s != "") { + L=L.copy(); + L.align(align); + L.p(p); + L.out(pic,g); + } + + if(legend != null && legend.s != "") { + legend.p(p); + pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above)); + } + } + else /* marker != nomarker */ + { + if(marker != nomarker && !marker.above) marker.mark(pic,g); + + // Note we are using & instead of && as both arrow and bar need to be + // called. + if ((arrow == None || arrow(pic, g, p, margin)) & + (bar == None || bar(pic, g, p, margin))) + { + _draw(pic, g, p, margin); + } + + if(L != null && L.s != "") { + L=L.copy(); + L.align(align); + L.p(p); + L.out(pic,g); + } + + if(legend != null && legend.s != "") { + legend.p(p); + pic.legend.push(Legend(legend.s,legend.p,p,marker.f,marker.above)); + } + + if(marker != nomarker && marker.above) marker.mark(pic,g); + } +} + +// Draw a fixed-size line about the user-coordinate 'origin'. +void draw(pair origin, picture pic=currentpicture, Label L=null, path g, + align align=NoAlign, pen p=currentpen, arrowbar arrow=None, + arrowbar bar=None, margin margin=NoMargin, Label legend=null, + marker marker=nomarker) +{ + picture opic; + draw(opic,L,g,align,p,arrow,bar,margin,legend,marker); + add(pic,opic,origin); +} + +void draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen, + Label legend=null, marker marker=nomarker) +{ + // This could be optimized to size and draw the entire array as a batch. + for(int i=0; i < g.length-1; ++i) + draw(pic,g[i],p,marker); + if(g.length > 0) draw(pic,g[g.length-1],p,legend,marker); +} + +void draw(picture pic=currentpicture, guide[] g, pen p=currentpen, + Label legend=null, marker marker=nomarker) +{ + draw(pic,(path[]) g,p,legend,marker); +} + +void draw(pair origin, picture pic=currentpicture, explicit path[] g, + pen p=currentpen, Label legend=null, marker marker=nomarker) +{ + picture opic; + draw(opic,g,p,legend,marker); + add(pic,opic,origin); +} + +void draw(pair origin, picture pic=currentpicture, guide[] g, pen p=currentpen, + Label legend=null, marker marker=nomarker) +{ + draw(origin,pic,(path[]) g,p,legend,marker); +} + +// Align an arrow pointing to b from the direction dir. The arrow is +// 'length' PostScript units long. +void arrow(picture pic=currentpicture, Label L=null, pair b, pair dir, + real length=arrowlength, align align=NoAlign, + pen p=currentpen, arrowbar arrow=Arrow, margin margin=EndMargin) +{ + if(L != null && L.s != "") { + L=L.copy(); + if(L.defaultposition) L.position(0); + L.align(L.align,dir); + L.p(p); + } + marginT margin=margin(b--b,p); // Extract margin.begin and margin.end + pair a=(margin.begin+length+margin.end)*unit(dir); + draw(b,pic,L,a--(0,0),align,p,arrow,margin); +} + +// Fit an array of pictures simultaneously using the sizing of picture all. +frame[] fit2(picture[] pictures, picture all) +{ + frame[] out; + if(!all.empty2()) { + transform t=all.calculateTransform(); + pair m=all.min(t); + pair M=all.max(t); + for(picture pic : pictures) { + frame f=pic.fit(t); + draw(f,m,nullpen); + draw(f,M,nullpen); + out.push(f); + } + } + return out; +} + +// Fit an array of pictures simultaneously using the size of the first picture. +// TODO: Remove unused arguments. +frame[] fit(string prefix="", picture[] pictures, string format="", + bool view=true, string options="", string script="", + projection P=currentprojection) +{ + if(pictures.length == 0) + return new frame[]; + + picture all; + size(all,pictures[0]); + for(picture pic : pictures) + add(all,pic); + + return fit2(pictures,all); +} + +// Pad a picture to a specified size +frame pad(picture pic=currentpicture, real xsize=pic.xsize, + real ysize=pic.ysize, filltype filltype=NoFill) +{ + picture P; + size(P,xsize,ysize,IgnoreAspect); + draw(P,(0,0),invisible+thin()); + draw(P,(xsize,ysize),invisible+thin()); + add(P,pic.fit(xsize,ysize),(xsize,ysize)/2); + frame f=P.fit(); + if(filltype != NoFill) { + frame F; + filltype.fill(F,box(min(f),max(f)),invisible); + prepend(f,F); + } + return f; +} diff --git a/Build/source/utils/asymptote/base/plain_bounds.asy b/Build/source/utils/asymptote/base/plain_bounds.asy new file mode 100644 index 00000000000..c4e24721773 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_bounds.asy @@ -0,0 +1,788 @@ +include plain_scaling; + +// After a transformation, produce new coordinate bounds. For paths that +// have been added, this is only an approximation since it takes the bounds of +// their transformed bounding box. +private void addTransformedCoords(coords2 dest, transform t, + coords2 point, coords2 min, coords2 max) +{ + dest.push(t, point, point); + + // Add in all 4 corner coords, to properly size rectangular pictures. + dest.push(t,min,min); + dest.push(t,min,max); + dest.push(t,max,min); + dest.push(t,max,max); +} + +// Adds another sizing restriction to the coordinates, but only if it is +// maximal, that is, if under some scaling, this coordinate could be the +// largest. +private void addIfMaximal(coord[] coords, real user, real truesize) { + // TODO: Test promoting coordinates for efficiency. + + for (coord c : coords) + if (user <= c.user && truesize <= c.truesize) + // Not maximal. + return; + + // The coordinate is not dominated by any existing extreme, so it is + // maximal and will be added, but first remove any coords it now dominates. + int i = 0; + while (i < coords.length) { + coord c = coords[i]; + if (c.user <= user && c.truesize <= truesize) + coords.delete(i); + else + ++i; + } + + // Add the coordinate to the extremes. + coords.push(coord.build(user, truesize)); +} + +private void addIfMaximal(coord[] dest, coord[] src) +{ + // This may be inefficient, as it rebuilds the coord struct when adding it. + for (coord c : src) + addIfMaximal(dest, c.user, c.truesize); +} + +// Same as addIfMaximal, but testing for minimal coords. +private void addIfMinimal(coord[] coords, real user, real truesize) { + for (coord c : coords) + if (user >= c.user && truesize >= c.truesize) + return; + + int i = 0; + while (i < coords.length) { + coord c = coords[i]; + if (c.user >= user && c.truesize >= truesize) + coords.delete(i); + else + ++i; + } + + coords.push(coord.build(user, truesize)); +} + +private void addIfMinimal(coord[] dest, coord[] src) +{ + for (coord c : src) + addIfMinimal(dest, c.user, c.truesize); +} + +// This stores a list of sizing bounds for picture data. If the object is +// frozen, then it cannot be modified further, and therefore can be safely +// passed by reference and stored in the sizing data for multiple pictures. +private struct freezableBounds { + restricted bool frozen = false; + void freeze() { + frozen = true; + } + + // Optional links to further (frozen) sizing data. + private freezableBounds[] links; + + // Links to (frozen) sizing data that is transformed when added here. + private static struct transformedBounds { + transform t; + freezableBounds link; + }; + private transformedBounds[] tlinks; + + // The sizing data. It cannot be modified once this object is frozen. + private coords2 point, min, max; + + // A bound represented by a path. Using the path instead of the bounding + // box means it will be accurate after a transformation by coordinates. + private path[] pathBounds; + + // A bound represented by a path and a pen. + // As often many paths use the same pen, we store an array of paths. + private static struct pathpen { + path[] g; pen p; + + void operator init(path g, pen p) { + this.g.push(g); + this.p = p; + } + } + private static pathpen operator *(transform t, pathpen pp) { + // Should the pen be transformed? + pathpen newpp; + for (path g : pp.g) + newpp.g.push(t*g); + newpp.p = pp.p; + return newpp; + } + + // WARNING: Due to crazy optimizations, if this array is changed between an + // empty and non-empty state, the assignment of a method to + // addPath(path,pen) must also change. + private pathpen[] pathpenBounds; + + // Once frozen, the sizing is immutable, and therefore we can compute and + // store the extremal coordinates. + public static struct extremes { + coord[] left, bottom, right, top; + + void operator init(coord[] left, coord[] bottom, + coord[] right, coord[] top) { + this.left = left; + this.bottom = bottom; + this.right = right; + this.top = top; + } + + } + private static void addMaxToExtremes(extremes e, pair user, pair truesize) { + addIfMaximal(e.right, user.x, truesize.x); + addIfMaximal(e.top, user.y, truesize.y); + } + private static void addMinToExtremes(extremes e, pair user, pair truesize) { + addIfMinimal(e.left, user.x, truesize.x); + addIfMinimal(e.bottom, user.y, truesize.y); + } + private static void addMaxToExtremes(extremes e, coords2 coords) { + addIfMaximal(e.right, coords.x); + addIfMaximal(e.top, coords.y); + } + private static void addMinToExtremes(extremes e, coords2 coords) { + addIfMinimal(e.left, coords.x); + addIfMinimal(e.bottom, coords.y); + } + + private extremes cachedExtremes = null; + + // Once frozen, getMutable returns a new object based on this one, which can + // be modified. + freezableBounds getMutable() { + assert(frozen); + var f = new freezableBounds; + f.links.push(this); + return f; + } + + freezableBounds transformed(transform t) { + // Freeze these bounds, as we are storing a reference to them. + freeze(); + + var tlink = new transformedBounds; + tlink.t = t; + tlink.link = this; + + var b = new freezableBounds; + b.tlinks.push(tlink); + + return b; + } + + void append(freezableBounds b) { + // Check that we can modify the object. + assert(!frozen); + + //TODO: If b is "small", ie. a single tlink or cliplink, just copy the + //link. + + // As we only reference b, we must freeze it to ensure it does not change. + b.freeze(); + links.push(b); + } + + void addPoint(pair user, pair truesize) { + assert(!frozen); + point.push(user, truesize); + } + + void addBox(pair userMin, pair userMax, pair trueMin, pair trueMax) { + assert(!frozen); + this.min.push(userMin, trueMin); + this.max.push(userMax, trueMax); + } + + void addPath(path g) { + // This, and other asserts have been removed to speed things up slightly. + //assert(!frozen); + this.pathBounds.push(g); + } + + void addPath(path[] g) { + //assert(!frozen); + this.pathBounds.append(g); + } + + // To squeeze out a bit more performance, this method is either assigned + // addPathToNonEmptyArray or addPathToEmptyArray depending on the state of + // the pathpenBounds array. + void addPath(path g, pen p); + + private void addPathToNonEmptyArray(path g, pen p) { + //assert(!frozen); + //assert(!pathpenBounds.empty()); + var pp = pathpenBounds[0]; + + // Test if the pens are equal or have the same bounds. + if (pp.p == p || (min(pp.p) == min(p) && max(pp.p) == max(p))) { + // If this path has the same pen as the last one, just add it to the + // array corresponding to that pen. + pp.g.push(g); + } + else { + // A different pen. Start a new bound and put it on the front. Put + // the old bound at the end of the array. + pathpenBounds[0] = pathpen(g,p); + pathpenBounds.push(pp); + } + } + void addPathToEmptyArray(path g, pen p) { + //assert(!frozen); + //assert(pathpenBounds.empty()); + + pathpenBounds.push(pathpen(g,p)); + addPath = addPathToNonEmptyArray; + } + + // Initial setting for addPath. + addPath = addPathToEmptyArray; + + // Transform the sizing info by t then add the result to the coords + // structure. + private void accumulateCoords(transform t, coords2 coords) { + for (var link : links) + link.accumulateCoords(t, coords); + + for (var tlink : tlinks) + tlink.link.accumulateCoords(t*tlink.t, coords); + + addTransformedCoords(coords, t, this.point, this.min, this.max); + + for (var g : pathBounds) { + g = t*g; + coords.push(min(g), (0,0)); + coords.push(max(g), (0,0)); + } + + for (var pp: pathpenBounds) { + pair pm = min(pp.p), pM = max(pp.p); + for (var g : pp.g) { + g = t*g; + coords.push(min(g), pm); + coords.push(max(g), pM); + } + } + } + + // Add all of the sizing info to the given coords structure. + private void accumulateCoords(coords2 coords) { + for (var link : links) + link.accumulateCoords(coords); + + for (var tlink : tlinks) + tlink.link.accumulateCoords(tlink.t, coords); + + coords.append(this.point); + coords.append(this.min); + coords.append(this.max); + + for (var g : pathBounds) { + coords.push(min(g), (0,0)); + coords.push(max(g), (0,0)); + } + + for (var pp: pathpenBounds) { + pair pm = min(pp.p), pM = max(pp.p); + for (var g : pp.g) { + coords.push(min(g), pm); + coords.push(max(g), pM); + } + } + } + + // Returns all of the coords that this sizing data represents. + private coords2 allCoords() { + coords2 coords; + accumulateCoords(coords); + return coords; + } + + private void addLocalsToExtremes(transform t, extremes e) { + coords2 coords; + addTransformedCoords(coords, t, this.point, this.min, this.max); + addMinToExtremes(e, coords); + addMaxToExtremes(e, coords); + + if (pathBounds.length > 0) { + addMinToExtremes(e, minAfterTransform(t, pathBounds), (0,0)); + addMaxToExtremes(e, maxAfterTransform(t, pathBounds), (0,0)); + } + + for (var pp : pathpenBounds) { + if (pp.g.length > 0) { + addMinToExtremes(e, minAfterTransform(t, pp.g), min(pp.p)); + addMaxToExtremes(e, maxAfterTransform(t, pp.g), max(pp.p)); + } + } + } + + private void addToExtremes(transform t, extremes e) { + for (var link : links) + link.addToExtremes(t, e); + + for (var tlink : tlinks) + tlink.link.addToExtremes(t*tlink.t, e); + + addLocalsToExtremes(t, e); + } + + private void addLocalsToExtremes(extremes e) { + addMinToExtremes(e, point); + addMaxToExtremes(e, point); + addMinToExtremes(e, min); + addMaxToExtremes(e, max); + + if (pathBounds.length > 0) { + addMinToExtremes(e, min(pathBounds), (0,0)); + addMaxToExtremes(e, max(pathBounds), (0,0)); + } + + for(var pp : pathpenBounds) { + pair m=min(pp.p); + pair M=max(pp.p); + for(path gg : pp.g) { + if (size(gg) > 0) { + addMinToExtremes(e,min(gg),m); + addMaxToExtremes(e,max(gg),M); + } + } + } + } + + private void addToExtremes(extremes e) { + for (var link : links) + link.addToExtremes(e); + + for (var tlink : tlinks) + tlink.link.addToExtremes(tlink.t, e); + + addLocalsToExtremes(e); + } + + private static void write(extremes e) { + static void write(coord[] coords) { + for (coord c : coords) + write(" " + (string)c.user + " u + " + (string)c.truesize); + } + write("left:"); + write(e.left); + write("bottom:"); + write(e.bottom); + write("right:"); + write(e.right); + write("top:"); + write(e.top); + } + + // Returns the extremal coordinates of the sizing data. + public extremes extremes() { + if (cachedExtremes == null) { + freeze(); + + extremes e; + addToExtremes(e); + cachedExtremes = e; + } + + return cachedExtremes; + } + + // Helper functions for computing the usersize bounds. usermin and usermax + // would be easily computable from extremes, except that the picture + // interface actually allows calls that manually change the usermin and + // usermax values. Therefore, we have to compute these values separately. + private static struct userbounds { + bool areSet=false; + pair min; + pair max; + } + private static struct boundsAccumulator { + pair[] mins; + pair[] maxs; + + void push(pair m, pair M) { + mins.push(m); + maxs.push(M); + } + + void push(userbounds b) { + if (b.areSet) + push(b.min, b.max); + } + + void push(transform t, userbounds b) { + if (b.areSet) { + pair[] box = { t*(b.min.x,b.max.y), t*b.max, + t*b.min, t*(b.max.x,b.min.y) }; + for (var z : box) + push(z,z); + } + } + + void pushUserCoords(coords2 min, coords2 max) { + int n = min.x.length; + assert(min.y.length == n); + assert(max.x.length == n); + assert(max.y.length == n); + + for (int i = 0; i < n; ++i) + push((min.x[i].user, min.y[i].user), + (max.x[i].user, max.y[i].user)); + } + + userbounds collapse() { + userbounds b; + if (mins.length > 0) { + b.areSet = true; + b.min = minbound(mins); + b.max = maxbound(maxs); + } + else { + b.areSet = false; + } + return b; + } + } + + // The user bounds already calculated for this data. + private userbounds storedUserBounds = null; + + private void accumulateUserBounds(boundsAccumulator acc) + { + if (storedUserBounds != null) { + assert(frozen); + acc.push(storedUserBounds); + } else { + acc.pushUserCoords(point, point); + acc.pushUserCoords(min, max); + if (pathBounds.length > 0) + acc.push(min(pathBounds), max(pathBounds)); + for (var pp : pathpenBounds) + if(size(pp.g) > 0) + acc.push(min(pp.g), max(pp.g)); + for (var link : links) + link.accumulateUserBounds(acc); + + // Transforms are handled as they were in the old system. + for (var tlink : tlinks) { + boundsAccumulator tacc; + tlink.link.accumulateUserBounds(tacc); + acc.push(tlink.t, tacc.collapse()); + } + } + } + + private void computeUserBounds() { + freeze(); + boundsAccumulator acc; + accumulateUserBounds(acc); + storedUserBounds = acc.collapse(); + } + + private userbounds userBounds() { + if (storedUserBounds == null) + computeUserBounds(); + + assert(storedUserBounds != null); + return storedUserBounds; + } + + // userMin/userMax returns the minimal/maximal userspace coordinate of the + // sizing data. As coordinates for objects such as labels can have + // significant truesize dimensions, this userMin/userMax values may not + // correspond closely to the end of the screen, and are of limited use. + // userSetx and userSety determine if there is sizing data in order to even + // have userMin/userMax defined. + public bool userBoundsAreSet() { + return userBounds().areSet; + } + + public pair userMin() { + return userBounds().min; + } + public pair userMax() { + return userBounds().max; + } + + // To override the true userMin and userMax bounds, first compute the + // userBounds as they should be at this point, then change the values. + public void alterUserBound(string which, real val) { + // We are changing the bounds data, so it cannot be frozen yet. After the + // user bounds are set, however, the sizing data cannot change, so it will + // be frozen. + assert(!frozen); + computeUserBounds(); + assert(frozen); + + var b = storedUserBounds; + if (which == "minx") + b.min = (val, b.min.y); + else if (which == "miny") + b.min = (b.min.x, val); + else if (which == "maxx") + b.max = (val, b.max.y); + else { + assert(which == "maxy"); + b.max = (b.max.x, val); + } + } + + // A temporary measure. Stuffs all of the data from the links and paths + // into the coords. + private void flatten() { + assert(!frozen); + + // First, compute the user bounds, taking into account any manual + // alterations. + computeUserBounds(); + + // Calculate all coordinates. + coords2 coords = allCoords(); + + // Erase all the old data. + point.erase(); + min.erase(); + max.erase(); + pathBounds.delete(); + pathpenBounds.delete(); + addPath = addPathToEmptyArray; + links.delete(); + tlinks.delete(); + + // Put all of the coordinates into point. + point = coords; + } + + void xclip(real Min, real Max) { + assert(!frozen); + flatten(); + point.xclip(Min,Max); + min.xclip(Min,Max); + max.xclip(Min,Max); + + // Cap the userBounds. + userbounds b = storedUserBounds; + b.min = (max(Min, b.min.x), b.min.y); + b.max = (min(Max, b.max.x), b.max.y); + } + + void yclip(real Min, real Max) { + assert(!frozen); + flatten(); + point.yclip(Min,Max); + min.yclip(Min,Max); + max.yclip(Min,Max); + + // Cap the userBounds. + userbounds b = storedUserBounds; + b.min = (b.min.x, max(Min, b.min.y)); + b.max = (b.max.x, min(Max, b.max.y)); + } + + // Calculate the min for the final frame, given the coordinate transform. + pair min(transform t) { + extremes e = extremes(); + if (e.left.length == 0) + return 0; + + pair a=t*(1,1)-t*(0,0), b=t*(0,0); + scaling xs=scaling.build(a.x,b.x); + scaling ys=scaling.build(a.y,b.y); + + return (min(infinity, xs, e.left), min(infinity, ys, e.bottom)); + } + + // Calculate the max for the final frame, given the coordinate transform. + pair max(transform t) { + extremes e = extremes(); + if (e.right.length == 0) + return 0; + + pair a=t*(1,1)-t*(0,0), b=t*(0,0); + scaling xs=scaling.build(a.x,b.x); + scaling ys=scaling.build(a.y,b.y); + + return (max(-infinity, xs, e.right), max(-infinity, ys, e.top)); + } + + // Returns the transform for turning user-space pairs into true-space pairs. + transform scaling(real xsize, real ysize, + real xunitsize, real yunitsize, + bool keepAspect, bool warn) { + if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0) + return identity(); + + // Get the extremal coordinates. + extremes e = extremes(); + + real sx; + if(xunitsize == 0) { + if(xsize != 0) sx=calculateScaling("x",e.left,e.right,xsize,warn); + } else sx=xunitsize; + + /* Possible alternative code : + real sx = xunitsize != 0 ? xunitsize : + xsize != 0 ? calculateScaling("x", Coords.x, xsize, warn) : + 0; */ + + real sy; + if(yunitsize == 0) { + if(ysize != 0) sy=calculateScaling("y",e.bottom,e.top,ysize,warn); + } else sy=yunitsize; + + if(sx == 0) { + sx=sy; + if(sx == 0) + return identity(); + } else if(sy == 0) sy=sx; + + + if(keepAspect && (xunitsize == 0 || yunitsize == 0)) + return scale(min(sx,sy)); + else + return scale(sx,sy); + } +} + +struct bounds { + private var base = new freezableBounds; + + // We should probably put this back into picture. + bool exact = true; + + // Called just before modifying the sizing data. It ensures base is + // non-frozen. + // Note that this is manually inlined for speed reasons in a couple often + // called methods below. + private void makeMutable() { + if (base.frozen) + base = base.getMutable(); + //assert(!base.frozen); // Disabled for speed reasons. + } + + void erase() { + // Just discard the old bounds. + base = new freezableBounds; + + // We don't reset the 'exact' field, for backward compatibility. + } + + bounds copy() { + // Freeze the underlying bounds and make a shallow copy. + base.freeze(); + + var b = new bounds; + b.base = this.base; + b.exact = this.exact; + return b; + } + + bounds transformed(transform t) { + var b = new bounds; + b.base = base.transformed(t); + b.exact = this.exact; + return b; + } + + void append(bounds b) { + makeMutable(); + base.append(b.base); + } + + void append(transform t, bounds b) { + // makeMutable will be called by append. + if (t == identity()) + append(b); + else + append(b.transformed(t)); + } + + void addPoint(pair user, pair truesize) { + makeMutable(); + base.addPoint(user, truesize); + } + + void addBox(pair userMin, pair userMax, pair trueMin, pair trueMax) { + makeMutable(); + base.addBox(userMin, userMax, trueMin, trueMax); + } + + void addPath(path g) { + //makeMutable(); // Manually inlined here for speed reasons. + if (base.frozen) + base = base.getMutable(); + base.addPath(g); + } + + void addPath(path[] g) { + //makeMutable(); // Manually inlined here for speed reasons. + if (base.frozen) + base = base.getMutable(); + base.addPath(g); + } + + void addPath(path g, pen p) { + //makeMutable(); // Manually inlined here for speed reasons. + if (base.frozen) + base = base.getMutable(); + base.addPath(g, p); + } + + public bool userBoundsAreSet() { + return base.userBoundsAreSet(); + } + public pair userMin() { + return base.userMin(); + } + public pair userMax() { + return base.userMax(); + } + public void alterUserBound(string which, real val) { + makeMutable(); + base.alterUserBound(which, val); + } + + void xclip(real Min, real Max) { + makeMutable(); + base.xclip(Min,Max); + } + + void yclip(real Min, real Max) { + makeMutable(); + base.yclip(Min,Max); + } + + void clip(pair Min, pair Max) { + // TODO: If the user bounds have been manually altered, they may be + // incorrect after the clip. + xclip(Min.x,Max.x); + yclip(Min.y,Max.y); + } + + pair min(transform t) { + return base.min(t); + } + + pair max(transform t) { + return base.max(t); + } + + transform scaling(real xsize, real ysize, + real xunitsize, real yunitsize, + bool keepAspect, bool warn) { + return base.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn); + } +} + +bounds operator *(transform t, bounds b) { + return b.transformed(t); +} diff --git a/Build/source/utils/asymptote/base/plain_boxes.asy b/Build/source/utils/asymptote/base/plain_boxes.asy new file mode 100644 index 00000000000..50501a40897 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_boxes.asy @@ -0,0 +1,138 @@ +// Draw and/or fill a box on frame dest using the dimensions of frame src. +path box(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + pair z=(xmargin,ymargin); + int sign=filltype == NoFill ? 1 : -1; + pair h=0.5*sign*(max(p)-min(p)); + path g=box(min(src)-h-z,max(src)+h+z); + frame F; + if(above == false) { + filltype.fill(F,g,p); + prepend(dest,F); + } else filltype.fill(dest,g,p); + return g; +} + +path roundbox(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + pair m=min(src); + pair M=max(src); + pair bound=M-m; + real a=bound.x+2*xmargin; + real b=bound.y+2*ymargin; + real ds=0; + real dw=min(a,b)*0.3; + path g=shift(m-(xmargin,ymargin))*((0,dw)--(0,b-dw){up}..{right} + (dw,b)--(a-dw,b){right}..{down} + (a,b-dw)--(a,dw){down}..{left} + (a-dw,0)--(dw,0){left}..{up}cycle); + + frame F; + if(above == false) { + filltype.fill(F,g,p); + prepend(dest,F); + } else filltype.fill(dest,g,p); + return g; +} + +path ellipse(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + pair m=min(src); + pair M=max(src); + pair D=M-m; + static real factor=0.5*sqrt(2); + int sign=filltype == NoFill ? 1 : -1; + pair h=0.5*sign*(max(p)-min(p)); + path g=ellipse(0.5*(M+m),factor*D.x+h.x+xmargin,factor*D.y+h.y+ymargin); + frame F; + if(above == false) { + filltype.fill(F,g,p); + prepend(dest,F); + } else filltype.fill(dest,g,p); + return g; +} + +path box(frame f, Label L, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + add(f,L); + return box(f,xmargin,ymargin,p,filltype,above); +} + +path roundbox(frame f, Label L, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + add(f,L); + return roundbox(f,xmargin,ymargin,p,filltype,above); +} + +path ellipse(frame f, Label L, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + add(f,L); + return ellipse(f,xmargin,ymargin,p,filltype,above); +} + +typedef path envelope(frame dest, frame src=dest, real xmargin=0, + real ymargin=xmargin, pen p=currentpen, + filltype filltype=NoFill, bool above=true); + +object object(Label L, envelope e, real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill, bool above=true) +{ + object F; + F.L=L.copy(); + Label L0=L.copy(); + L0.position(0); + L0.p(p); + add(F.f,L0); + F.g=e(F.f,xmargin,ymargin,p,filltype,above); + return F; +} + +object draw(picture pic=currentpicture, Label L, envelope e, + real xmargin=0, real ymargin=xmargin, pen p=currentpen, + filltype filltype=NoFill, bool above=true) +{ + object F=object(L,e,xmargin,ymargin,p,filltype,above); + pic.add(new void (frame f, transform t) { + frame d; + add(d,t,F.L); + e(f,d,xmargin,ymargin,p,filltype,above); + add(f,d); + },true); + pic.addBox(L.position,L.position,min(F.f),max(F.f)); + return F; +} + +object draw(picture pic=currentpicture, Label L, envelope e, pair position, + real xmargin=0, real ymargin=xmargin, pen p=currentpen, + filltype filltype=NoFill, bool above=true) +{ + return draw(pic,Label(L,position),e,xmargin,ymargin,p,filltype,above); +} + +pair point(object F, pair dir, transform t=identity()) +{ + pair m=min(F.g); + pair M=max(F.g); + pair c=0.5*(m+M); + pair z=t*F.L.position; + real[] T=intersect(F.g,c--2*(m+realmult(rectify(dir),M-m))-c); + if(T.length == 0) return z; + return z+point(F.g,T[0]); +} + +frame bbox(picture pic=currentpicture, + real xmargin=0, real ymargin=xmargin, + pen p=currentpen, filltype filltype=NoFill) +{ + real penwidth=linewidth(p); + frame f=pic.fit(max(pic.xsize-2*(xmargin+penwidth),0), + max(pic.ysize-2*(ymargin+penwidth),0)); + box(f,xmargin,ymargin,p,filltype,above=false); + return f; +} diff --git a/Build/source/utils/asymptote/base/plain_constants.asy b/Build/source/utils/asymptote/base/plain_constants.asy new file mode 100644 index 00000000000..136b3540218 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_constants.asy @@ -0,0 +1,169 @@ +restricted int undefined=(intMax % 2 == 1) ? intMax : intMax-1; + +restricted real inches=72; +restricted real inch=inches; +restricted real cm=inches/2.54; +restricted real mm=0.1cm; +restricted real bp=1; // A PostScript point. +restricted real pt=72.0/72.27; // A TeX pt; smaller than a PostScript bp. +restricted pair I=(0,1); + +restricted pair right=(1,0); +restricted pair left=(-1,0); +restricted pair up=(0,1); +restricted pair down=(0,-1); + +restricted pair E=(1,0); +restricted pair N=(0,1); +restricted pair W=(-1,0); +restricted pair S=(0,-1); + +restricted pair NE=unit(N+E); +restricted pair NW=unit(N+W); +restricted pair SW=unit(S+W); +restricted pair SE=unit(S+E); + +restricted pair ENE=unit(E+NE); +restricted pair NNE=unit(N+NE); +restricted pair NNW=unit(N+NW); +restricted pair WNW=unit(W+NW); +restricted pair WSW=unit(W+SW); +restricted pair SSW=unit(S+SW); +restricted pair SSE=unit(S+SE); +restricted pair ESE=unit(E+SE); + +restricted real sqrtEpsilon=sqrt(realEpsilon); +restricted pair Align=sqrtEpsilon*NE; +restricted int mantissaBits=ceil(-log(realEpsilon)/log(2))+1; + +restricted transform identity; +restricted transform zeroTransform=(0,0,0,0,0,0); + +int min(... int[] a) {return min(a);} +int max(... int[] a) {return max(a);} + +real min(... real[] a) {return min(a);} +real max(... real[] a) {return max(a);} + +bool finite(real x) +{ + return abs(x) < infinity; +} + +bool finite(pair z) +{ + return abs(z.x) < infinity && abs(z.y) < infinity; +} + +bool finite(triple v) +{ + return abs(v.x) < infinity && abs(v.y) < infinity && abs(v.z) < infinity; +} + +restricted file stdin=input(); +restricted file stdout=output(); + +void none(file file) {} +void endl(file file) {write(file,'\n',flush);} +void newl(file file) {write(file,'\n');} +void DOSendl(file file) {write(file,'\r\n',flush);} +void DOSnewl(file file) {write(file,'\r\n');} +void tab(file file) {write(file,'\t');} +void comma(file file) {write(file,',');} +typedef void suffix(file); + +// Used by interactive write to warn that the outputted type is the resolution +// of an overloaded name. +void overloadedMessage(file file) { + write(file,' <overloaded>'); + endl(file); +} + +void write(suffix suffix=endl) {suffix(stdout);} +void write(file file, suffix suffix=none) {suffix(file);} + +path box(pair a, pair b) +{ + return a--(b.x,a.y)--b--(a.x,b.y)--cycle; +} + +restricted path unitsquare=box((0,0),(1,1)); + +restricted path unitcircle=E..N..W..S..cycle; +restricted real circleprecision=0.0006; + +restricted transform invert=reflect((0,0),(1,0)); + +restricted pen defaultpen; + +// A type that takes on one of the values true, false, or default. +struct bool3 { + bool value; + bool set; +} + +void write(file file, string s="", bool3 b, suffix suffix=none) +{ + if(b.set) write(b.value,suffix); + else write("default",suffix); +} + +void write(string s="", bool3 b, suffix suffix=endl) +{ + write(stdout,s,b,suffix); +} + +restricted bool3 default; + +bool operator cast(bool3 b) +{ + return b.set && b.value; +} + +bool3 operator cast(bool b) +{ + bool3 B; + B.value=b; + B.set=true; + return B; +} + +bool operator == (bool3 a, bool3 b) +{ + return a.set == b.set && (!a.set || (a.value == b.value)); +} + +bool operator != (bool3 a, bool3 b) +{ + return a.set != b.set || (a.set && (a.value != b.value)); +} + +bool operator == (bool3 a, bool b) +{ + return a.set && a.value == b; +} + +bool operator != (bool3 a, bool b) +{ + return !a.set || a.value != b; +} + +bool operator == (bool a, bool3 b) +{ + return b.set && b.value == a; +} + +bool operator != (bool a, bool3 b) +{ + return !b.set || b.value != a; +} + +bool[] operator cast(bool3[] b) +{ + return sequence(new bool(int i) {return b[i];},b.length); +} + +bool3[] operator cast(bool[] b) +{ + return sequence(new bool3(int i) {return b[i];},b.length); +} diff --git a/Build/source/utils/asymptote/base/plain_debugger.asy b/Build/source/utils/asymptote/base/plain_debugger.asy new file mode 100644 index 00000000000..8568ee0bd8d --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/plain_filldraw.asy b/Build/source/utils/asymptote/base/plain_filldraw.asy new file mode 100644 index 00000000000..026f2cee6c5 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_filldraw.asy @@ -0,0 +1,248 @@ +// Draw path g on frame f with user-constructed pen p. +void makedraw(frame f, path g, pen p, int depth=mantissaBits) +{ + if(depth == 0) return; + --depth; + + path n=nib(p); + for(int i=0; i < size(g); ++i) + fill(f,shift(point(g,i))*n,p); + + static real epsilon=1000*realEpsilon; + int L=length(g); + real stop=L-epsilon; + int N=length(n); + pair first=point(n,0); + pair n0=first; + + for(int i=0; i < N; ++i) { + pair n1=point(n,i+1); + pair dir=unit(n1-n0); + real t=dirtime(g,-dir)-epsilon; + if(straight(g,(int) t)) t=ceil(t); + if(t > epsilon && t < stop) { + makedraw(f,subpath(g,0,t),p,depth); + makedraw(f,subpath(g,t,L),p,depth); + return; + } + real t=dirtime(g,dir); + if(straight(g,(int) t)) t=ceil(t); + if(t > epsilon && t < stop) { + makedraw(f,subpath(g,0,t),p,depth); + makedraw(f,subpath(g,t,L),p,depth); + return; + } + n0=n1; + } + + n0=first; + for(int i=0; i < N; ++i) { + pair n1=point(n,i+1); + fill(f,shift(n0)*g--shift(n1)*reverse(g)--cycle,p); + n0=n1; + } +} + +void draw(frame f, path g, pen p=currentpen) +{ + if(size(nib(p)) == 0) _draw(f,g,p); + else { + begingroup(f); + makedraw(f,g,p); + endgroup(f); + } +} + +void draw(frame f, explicit path[] g, pen p=currentpen) +{ + for(int i=0; i < g.length; ++i) draw(f,g[i],p); +} + +void draw(frame f, guide[] g, pen p=currentpen) +{ + for(int i=0; i < g.length; ++i) draw(f,g[i],p); +} + +void filldraw(frame f, path[] g, pen fillpen=currentpen, + pen drawpen=currentpen) +{ + begingroup(f); + fill(f,g,fillpen); + draw(f,g,drawpen); + endgroup(f); +} + +path[] complement(frame f, path[] g) +{ + static pair margin=(0.5,0.5); + return box(minbound(min(f),min(g))-margin,maxbound(max(f),max(g))+margin)^^g; +} + +void unfill(frame f, path[] g, bool copy=true) +{ + clip(f,complement(f,g),evenodd,copy); +} + +void filloutside(frame f, path[] g, pen p=currentpen, bool copy=true) +{ + fill(f,complement(f,g),p+evenodd,copy); +} + +struct filltype +{ + typedef void fill2(frame f, path[] g, pen fillpen); + fill2 fill2; + pen fillpen; + pen drawpen; + + int type; + static int Fill=1; + static int FillDraw=2; + static int Draw=3; + static int NoFill=4; + static int UnFill=5; + + void operator init(int type=0, pen fillpen=nullpen, pen drawpen=nullpen, + fill2 fill2) { + this.type=type; + this.fillpen=fillpen; + this.drawpen=drawpen; + this.fill2=fill2; + } + void fill(frame f, path[] g, pen p) {fill2(f,g,p);} +} + +path[] margin(path[] g, real xmargin, real ymargin) +{ + if(xmargin != 0 || ymargin != 0) { + pair M=max(g); + pair m=min(g); + real width=M.x-m.x; + real height=M.y-m.y; + real xfactor=width > 0 ? (width+2xmargin)/width : 1; + real yfactor=height > 0 ? (height+2ymargin)/height : 1; + g=scale(xfactor,yfactor)*g; + g=shift(0.5*(M+m)-0.5*(max(g)+min(g)))*g; + } + return g; +} + +filltype Fill(real xmargin=0, real ymargin=xmargin, pen p=nullpen) +{ + return filltype(filltype.Fill,p,new void(frame f, path[] g, pen fillpen) { + if(p != nullpen) fillpen=p; + if(fillpen == nullpen) fillpen=currentpen; + fill(f,margin(g,xmargin,ymargin),fillpen); + }); +} + +filltype FillDraw(real xmargin=0, real ymargin=xmargin, + pen fillpen=nullpen, pen drawpen=nullpen) +{ + return filltype(filltype.FillDraw,fillpen,drawpen, + new void(frame f, path[] g, pen Drawpen) { + if(drawpen != nullpen) Drawpen=drawpen; + pen Fillpen=fillpen == nullpen ? Drawpen : fillpen; + if(Fillpen == nullpen) Fillpen=currentpen; + if(Drawpen == nullpen) Drawpen=Fillpen; + if(cyclic(g[0])) + filldraw(f,margin(g,xmargin,ymargin),Fillpen,Drawpen); + else + draw(f,margin(g,xmargin,ymargin),Drawpen); + }); +} + +filltype Draw(real xmargin=0, real ymargin=xmargin, pen p=nullpen) +{ + return filltype(filltype.Draw,drawpen=p, + new void(frame f, path[] g, pen drawpen) { + pen drawpen=p == nullpen ? drawpen : p; + if(drawpen == nullpen) drawpen=currentpen; + draw(f,margin(g,xmargin,ymargin),drawpen); + }); +} + +filltype NoFill=filltype(filltype.NoFill,new void(frame f, path[] g, pen p) { + draw(f,g,p); + }); + + +filltype UnFill(real xmargin=0, real ymargin=xmargin) +{ + return filltype(filltype.UnFill,new void(frame f, path[] g, pen) { + unfill(f,margin(g,xmargin,ymargin)); + }); +} + +filltype FillDraw=FillDraw(), Fill=Fill(), Draw=Draw(), UnFill=UnFill(); + +// Fill varying radially from penc at the center of the bounding box to +// penr at the edge. +filltype RadialShade(pen penc, pen penr) +{ + return filltype(new void(frame f, path[] g, pen) { + pair c=(min(g)+max(g))/2; + radialshade(f,g,penc,c,0,penr,c,abs(max(g)-min(g))/2); + }); +} + +filltype RadialShadeDraw(real xmargin=0, real ymargin=xmargin, + pen penc, pen penr, pen drawpen=nullpen) +{ + return filltype(new void(frame f, path[] g, pen Drawpen) { + if(drawpen != nullpen) Drawpen=drawpen; + if(Drawpen == nullpen) Drawpen=penc; + pair c=(min(g)+max(g))/2; + if(cyclic(g[0])) + radialshade(f,margin(g,xmargin,ymargin),penc,c,0,penr,c, + abs(max(g)-min(g))/2); + draw(f,margin(g,xmargin,ymargin),Drawpen); + }); +} + +// Fill the region in frame dest underneath frame src and return the +// boundary of src. +path fill(frame dest, frame src, filltype filltype=NoFill, + real xmargin=0, real ymargin=xmargin) +{ + pair z=(xmargin,ymargin); + path g=box(min(src)-z,max(src)+z); + filltype.fill(dest,g,nullpen); + return g; +} + +// Add frame dest to frame src with optional grouping and background fill. +void add(frame dest, frame src, bool group, filltype filltype=NoFill, + bool above=true) +{ + if(above) { + if(filltype != NoFill) fill(dest,src,filltype); + if(group) begingroup(dest); + add(dest,src); + if(group) endgroup(dest); + } else { + if(group) { + frame f; + endgroup(f); + prepend(dest,f); + } + prepend(dest,src); + if(group) { + frame f; + begingroup(f); + prepend(dest,f); + } + if(filltype != NoFill) { + frame f; + fill(f,src,filltype); + prepend(dest,f); + } + } +} + +void add(frame dest, frame src, filltype filltype, + bool above=filltype.type != filltype.UnFill) +{ + if(filltype != NoFill) fill(dest,src,filltype); + (above ? add : prepend)(dest,src); +} diff --git a/Build/source/utils/asymptote/base/plain_margins.asy b/Build/source/utils/asymptote/base/plain_margins.asy new file mode 100644 index 00000000000..fbd0163050c --- /dev/null +++ b/Build/source/utils/asymptote/base/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=begin) { + 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=begin) +{ + 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=begin) +{ + 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=begin) +{ + 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=begin) +{ + 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/Build/source/utils/asymptote/base/plain_markers.asy b/Build/source/utils/asymptote/base/plain_markers.asy new file mode 100644 index 00000000000..250e1701eb3 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_markers.asy @@ -0,0 +1,401 @@ +real legendlinelength=50; +real legendhskip=1.2; +real legendvskip=legendhskip; +real legendmargin=10; +real legendmaxrelativewidth=1; + +// Return a unit polygon with n sides. +path polygon(int n) +{ + guide g; + for(int i=0; i < n; ++i) g=g--expi(2pi*(i+0.5)/n-0.5*pi); + return g--cycle; +} + +// Return a unit n-point cyclic cross, with optional inner radius r and +// end rounding. +path cross(int n, bool round=true, real r=0) +{ + assert(n > 1); + real r=min(r,1); + real theta=pi/n; + real s=sin(theta); + real c=cos(theta); + pair z=(c,s); + transform mirror=reflect(0,z); + pair p1=(r,0); + path elementary; + if(round) { + pair e1=p1+z*max(1-r*(s+c),0); + elementary=p1--e1..(c,s)..mirror*e1--mirror*p1; + } else { + pair p2=p1+z*(max(sqrt(1-(r*s)^2)-r*c),0); + elementary=p1--p2--mirror*p2--mirror*p1; + } + + guide g; + real step=360/n; + for(int i=0; i < n; ++i) + g=g--rotate(i*step-90)*elementary; + + return g--cycle; +} + +path[] plus=(-1,0)--(1,0)^^(0,-1)--(0,1); + +typedef void markroutine(picture pic=currentpicture, frame f, path g); + +// On picture pic, add frame f about every node of path g. +void marknodes(picture pic=currentpicture, frame f, path g) { + for(int i=0; i < size(g); ++i) + add(pic,f,point(g,i)); +} + +// On picture pic, add n copies of frame f to path g, evenly spaced in +// arclength. +// If rotated=true, the frame will be rotated by the angle of the tangent +// to the path at the points where the frame will be added. +// If centered is true, center the frames within n evenly spaced arclength +// intervals. +markroutine markuniform(bool centered=false, int n, bool rotated=false) { + return new void(picture pic=currentpicture, frame f, path g) { + if(n <= 0) return; + void add(real x) { + real t=reltime(g,x); + add(pic,rotated ? rotate(degrees(dir(g,t)))*f : f,point(g,t)); + } + if(centered) { + real width=1/n; + for(int i=0; i < n; ++i) add((i+0.5)*width); + } else { + if(n == 1) add(0.5); + else { + real width=1/(n-1); + for(int i=0; i < n; ++i) + add(i*width); + } + } + }; +} + +// On picture pic, add frame f at points z(t) for n evenly spaced values of +// t in [a,b]. +markroutine markuniform(pair z(real t), real a, real b, int n) +{ + return new void(picture pic=currentpicture, frame f, path) { + real width=b-a; + for(int i=0; i <= n; ++i) { + add(pic,f,z(a+i/n*width)); + } + }; +} + +struct marker { + frame f; + bool above=true; + markroutine markroutine=marknodes; + void mark(picture pic=currentpicture, path g) { + markroutine(pic,f,g); + }; +} + +marker marker(frame f=newframe, markroutine markroutine=marknodes, + bool above=true) +{ + marker m=new marker; + m.f=f; + m.above=above; + m.markroutine=markroutine; + return m; +} + +marker marker(path[] g, markroutine markroutine=marknodes, pen p=currentpen, + filltype filltype=NoFill, bool above=true) +{ + frame f; + filltype.fill(f,g,p); + return marker(f,markroutine,above); +} + +// On picture pic, add path g with opacity thinning about every node. +marker markthin(path g, pen p=currentpen, + real thin(real fraction)=new real(real x) {return x^2;}, + filltype filltype=NoFill) { + marker M=new marker; + M.above=true; + filltype.fill(M.f,g,p); + real factor=1/abs(size(M.f)); + M.markroutine=new void(picture pic=currentpicture, frame, path G) { + transform t=pic.calculateTransform(); + int n=size(G); + for(int i=0; i < n; ++i) { + pair z=point(G,i); + frame f; + real fraction=1; + if(i > 0) fraction=min(fraction,abs(t*(z-point(G,i-1)))*factor); + if(i < n-1) fraction=min(fraction,abs(t*(point(G,i+1)-z))*factor); + filltype.fill(f,g,p+opacity(thin(fraction))); + add(pic,f,point(G,i)); + } + }; + return M; +} + +marker nomarker; + +real circlescale=0.85; + +path[] MarkPath={scale(circlescale)*unitcircle, + polygon(3),polygon(4),polygon(5),invert*polygon(3), + cross(4),cross(6)}; + +marker[] Mark=sequence(new marker(int i) {return marker(MarkPath[i]);}, + MarkPath.length); + +marker[] MarkFill=sequence(new marker(int i) {return marker(MarkPath[i],Fill);}, + MarkPath.length-2); + +marker Mark(int n) +{ + n=n % (Mark.length+MarkFill.length); + if(n < Mark.length) return Mark[n]; + else return MarkFill[n-Mark.length]; +} + +picture legenditem(Legend legenditem, real linelength) +{ + picture pic; + pair z1=(0,0); + pair z2=z1+(linelength,0); + if(!legenditem.above && !empty(legenditem.mark)) + marknodes(pic,legenditem.mark,interp(z1,z2,0.5)); + if(linelength > 0) + Draw(pic,z1--z2,legenditem.p); + if(legenditem.above && !empty(legenditem.mark)) + marknodes(pic,legenditem.mark,interp(z1,z2,0.5)); + if(legenditem.plabel != invisible) + label(pic,legenditem.label,z2,E,legenditem.plabel); + else + label(pic,legenditem.label,z2,E,currentpen); + return pic; +} + +picture legend(Legend[] Legend, int perline=1, real linelength, + real hskip, real vskip, real maxwidth=0, real maxheight=0, + bool hstretch=false, bool vstretch=false) +{ + if(maxwidth <= 0) hstretch=false; + if(maxheight <= 0) vstretch=false; + if(Legend.length <= 1) vstretch=hstretch=false; + + picture inset; + size(inset,0,0,IgnoreAspect); + + if(Legend.length == 0) + return inset; + + // Check for legend entries with lines: + bool bLineEntriesAvailable=false; + for(int i=0; i < Legend.length; ++i) { + if(Legend[i].p != invisible) { + bLineEntriesAvailable=true; + break; + } + } + + real markersize=0; + for(int i=0; i < Legend.length; ++i) + markersize=max(markersize,size(Legend[i].mark).x); + + // If no legend has a line, set the line length to zero + if(!bLineEntriesAvailable) + linelength=0; + + linelength=max(linelength,markersize*(linelength == 0 ? 1 : 2)); + + // Get the maximum dimensions per legend entry; + // calculate line length for a one-line legend + real heightPerEntry=0; + real widthPerEntry=0; + real totalwidth=0; + for(int i=0; i < Legend.length; ++i) { + picture pic=legenditem(Legend[i],linelength); + pair lambda=size(pic); + heightPerEntry=max(heightPerEntry,lambda.y); + widthPerEntry=max(widthPerEntry,lambda.x); + if(Legend[i].p != invisible) + totalwidth += lambda.x; + else { + // Legend entries without leading line need less space in one-line legends + picture pic=legenditem(Legend[i],0); + totalwidth += size(pic).x; + } + } + // Does everything fit into one line? + if(((perline < 1) || (perline >= Legend.length)) && + (maxwidth >= totalwidth+(totalwidth/Legend.length)* + (Legend.length-1)*(hskip-1))) { + // One-line legend + real currPosX=0; + real itemDistance; + if(hstretch) + itemDistance=(maxwidth-totalwidth)/(Legend.length-1); + else + itemDistance=(totalwidth/Legend.length)*(hskip-1); + for(int i=0; i < Legend.length; ++i) { + picture pic=legenditem(Legend[i], + Legend[i].p == invisible ? 0 : linelength); + add(inset,pic,(currPosX,0)); + currPosX += size(pic).x+itemDistance; + } + } else { + // multiline legend + if(maxwidth > 0) { + int maxperline=floor(maxwidth/(widthPerEntry*hskip)); + if((perline < 1) || (perline > maxperline)) + perline=maxperline; + } + if(perline < 1) // This means: maxwidth < widthPerEntry + perline=1; + + if(perline <= 1) hstretch=false; + if(hstretch) hskip=(maxwidth/widthPerEntry-perline)/(perline-1)+1; + if(vstretch) { + int rows=ceil(Legend.length/perline); + vskip=(maxheight/heightPerEntry-rows)/(rows-1)+1; + } + + if(hstretch && (perline == 1)) { + Draw(inset,(0,0)--(maxwidth,0),invisible()); + for(int i=0; i < Legend.length; ++i) + add(inset,legenditem(Legend[i],linelength), + (0.5*(maxwidth-widthPerEntry), + -quotient(i,perline)*heightPerEntry*vskip)); + } else + for(int i=0; i < Legend.length; ++i) + add(inset,legenditem(Legend[i],linelength), + ((i%perline)*widthPerEntry*hskip, + -quotient(i,perline)*heightPerEntry*vskip)); + } + + return inset; +} + +frame legend(picture pic=currentpicture, int perline=1, + real xmargin=legendmargin, real ymargin=xmargin, + real linelength=legendlinelength, + real hskip=legendhskip, real vskip=legendvskip, + real maxwidth=perline == 0 ? + legendmaxrelativewidth*size(pic).x : 0, real maxheight=0, + bool hstretch=false, bool vstretch=false, pen p=currentpen) +{ + frame F; + if(pic.legend.length == 0) return F; + F=legend(pic.legend,perline,linelength,hskip,vskip, + max(maxwidth-2xmargin,0), + max(maxheight-2ymargin,0), + hstretch,vstretch).fit(); + box(F,xmargin,ymargin,p); + return F; +} + +pair[] pairs(real[] x, real[] y) +{ + if(x.length != y.length) abort("arrays have different lengths"); + return sequence(new pair(int i) {return (x[i],y[i]);},x.length); +} + +filltype dotfilltype = Fill; + +void dot(frame f, pair z, pen p=currentpen, filltype filltype=dotfilltype) +{ + if(filltype == Fill) + draw(f,z,dotsize(p)+p); + else { + real s=0.5*(dotsize(p)-linewidth(p)); + if(s <= 0) return; + path g=shift(z)*scale(s)*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=dotfilltype) +{ + 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=dotfilltype) +{ + 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=dotfilltype) +{ + 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=dotfilltype) +{ + 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=dotfilltype) +{ + 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=dotfilltype) +{ + dot(pic,L,L.position,p,filltype); +} + +// A dot in a frame. +frame dotframe(pen p=currentpen, filltype filltype=dotfilltype) +{ + frame f; + dot(f,(0,0),p,filltype); + return f; +} + +frame dotframe=dotframe(); + +marker dot(pen p=currentpen, filltype filltype=dotfilltype) +{ + return marker(dotframe(p,filltype)); +} + +marker dot=dot(); + diff --git a/Build/source/utils/asymptote/base/plain_paths.asy b/Build/source/utils/asymptote/base/plain_paths.asy new file mode 100644 index 00000000000..8bb5250db5d --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_paths.asy @@ -0,0 +1,397 @@ +path nullpath; + +typedef guide interpolate(... guide[]); + +// These numbers identify the side of a specifier in an operator spec or +// operator curl expression: +// a{out} .. {in}b +restricted int JOIN_OUT=0; +restricted int JOIN_IN=1; + +// Define a.. tension t ..b to be equivalent to +// a.. tension t and t ..b +// and likewise with controls. +tensionSpecifier operator tension(real t, bool atLeast) +{ + return operator tension(t,t,atLeast); +} + +guide operator controls(pair z) +{ + return operator controls(z,z); +} + +guide[] operator cast(pair[] z) +{ + return sequence(new guide(int i) {return z[i];},z.length); +} + +path[] operator cast(pair[] z) +{ + return sequence(new path(int i) {return z[i];},z.length); +} + +path[] operator cast(guide[] g) +{ + return sequence(new path(int i) {return g[i];},g.length); +} + +guide[] operator cast(path[] g) +{ + return sequence(new guide(int i) {return g[i];},g.length); +} + +path[] operator cast(path p) +{ + return new path[] {p}; +} + +path[] operator cast(guide g) +{ + return new path[] {(path) g}; +} + +path[] operator ^^ (path p, path q) +{ + return new path[] {p,q}; +} + +path[] operator ^^ (path p, explicit path[] q) +{ + return concat(new path[] {p},q); +} + +path[] operator ^^ (explicit path[] p, path q) +{ + return concat(p,new path[] {q}); +} + +path[] operator ^^ (explicit path[] p, explicit path[] q) +{ + return concat(p,q); +} + +path[] operator * (transform t, explicit path[] p) +{ + return sequence(new path(int i) {return t*p[i];},p.length); +} + +pair[] operator * (transform t, pair[] z) +{ + return sequence(new pair(int i) {return t*z[i];},z.length); +} + +void write(file file, string s="", explicit path[] x, suffix suffix=none) +{ + write(file,s); + if(x.length > 0) write(file,x[0]); + for(int i=1; i < x.length; ++i) { + write(file,endl); + write(file," ^^"); + write(file,x[i]); + } + write(file,suffix); +} + +void write(string s="", explicit path[] x, suffix suffix=endl) +{ + write(stdout,s,x,suffix); +} + +void write(file file, string s="", explicit guide[] x, suffix suffix=none) +{ + write(file,s); + if(x.length > 0) write(file,x[0]); + for(int i=1; i < x.length; ++i) { + write(file,endl); + write(file," ^^"); + write(file,x[i]); + } + write(file,suffix); +} + +void write(string s="", explicit guide[] x, suffix suffix=endl) +{ + write(stdout,s,x,suffix); +} + +interpolate operator ..(tensionSpecifier t) +{ + return new guide(... guide[] a) { + if(a.length == 0) return nullpath; + guide g=a[0]; + for(int i=1; i < a.length; ++i) + g=g..t..a[i]; + return g; + }; +} + +interpolate operator ::=operator ..(operator tension(1,true)); +interpolate operator ---=operator ..(operator tension(infinity,true)); + +// return an arbitrary intersection point of paths p and q +pair intersectionpoint(path p, path q, real fuzz=-1) +{ + real[] t=intersect(p,q,fuzz); + if(t.length == 0) abort("paths do not intersect"); + return point(p,t[0]); +} + +// return an array containing all intersection points of the paths p and q +pair[] intersectionpoints(path p, path q, real fuzz=-1) +{ + real[][] t=intersections(p,q,fuzz); + return sequence(new pair(int i) {return point(p,t[i][0]);},t.length); +} + +pair[] intersectionpoints(explicit path[] p, explicit path[] q, real fuzz=-1) +{ + pair[] z; + for(int i=0; i < p.length; ++i) + for(int j=0; j < q.length; ++j) + z.append(intersectionpoints(p[i],q[j],fuzz)); + return z; +} + +struct slice { + path before,after; +} + +slice cut(path p, path knife, int n) +{ + slice s; + real[][] T=intersections(p,knife); + if(T.length == 0) {s.before=p; s.after=nullpath; return s;} + T.cyclic=true; + real t=T[n][0]; + s.before=subpath(p,0,t); + s.after=subpath(p,t,length(p)); + return s; +} + +slice firstcut(path p, path knife) +{ + return cut(p,knife,0); +} + +slice lastcut(path p, path knife) +{ + return cut(p,knife,-1); +} + +pair dir(path p) +{ + return dir(p,length(p)); +} + +pair dir(path p, path q) +{ + return unit(dir(p)+dir(q)); +} + +// return the point on path p at arclength L +pair arcpoint(path p, real L) +{ + return point(p,arctime(p,L)); +} + +// return the direction on path p at arclength L +pair arcdir(path p, real L) +{ + return dir(p,arctime(p,L)); +} + +// return the time on path p at the relative fraction l of its arclength +real reltime(path p, real l) +{ + return arctime(p,l*arclength(p)); +} + +// return the point on path p at the relative fraction l of its arclength +pair relpoint(path p, real l) +{ + return point(p,reltime(p,l)); +} + +// return the direction of path p at the relative fraction l of its arclength +pair reldir(path p, real l) +{ + return dir(p,reltime(p,l)); +} + +// return the initial point of path p +pair beginpoint(path p) +{ + return point(p,0); +} + +// return the point on path p at half of its arclength +pair midpoint(path p) +{ + return relpoint(p,0.5); +} + +// return the final point of path p +pair endpoint(path p) +{ + return point(p,length(p)); +} + +path operator &(path p, cycleToken tok) +{ + int n=length(p); + if(n < 0) return nullpath; + if(n == 0) return p--cycle; + if(cyclic(p)) return p; + return straight(p,n-1) ? subpath(p,0,n-1)--cycle : + subpath(p,0,n-1)..controls postcontrol(p,n-1) and precontrol(p,n)..cycle; +} + +// return a cyclic path enclosing a region bounded by a list of two or more +// consecutively intersecting paths +path buildcycle(... path[] p) +{ + int n=p.length; + if(n < 2) return nullpath; + real[] ta=new real[n]; + real[] tb=new real[n]; + if(n == 2) { + real[][] t=intersections(p[0],p[1]); + if(t.length < 2) + return nullpath; + int k=t.length-1; + ta[0]=t[0][0]; tb[0]=t[k][0]; + ta[1]=t[k][1]; tb[1]=t[0][1]; + } else { + int j=n-1; + for(int i=0; i < n; ++i) { + real[][] t=intersections(p[i],p[j]); + if(t.length == 0) + return nullpath; + ta[i]=t[0][0]; tb[j]=t[0][1]; + j=i; + } + } + + pair c; + for(int i=0; i < n ; ++i) + c += point(p[i],ta[i]); + c /= n; + + path G; + for(int i=0; i < n ; ++i) { + real Ta=ta[i]; + real Tb=tb[i]; + if(cyclic(p[i])) { + int L=length(p[i]); + real t=Tb-L; + if(abs(c-point(p[i],0.5(Ta+t))) < + abs(c-point(p[i],0.5(Ta+Tb)))) Tb=t; + while(Tb < Ta) Tb += L; + } + G=G&subpath(p[i],Ta,Tb); + } + return G&cycle; +} + +// return 1 if p strictly contains q, +// -1 if q strictly contains p, +// 0 otherwise. +int inside(path p, path q, pen fillrule=currentpen) +{ + if(intersect(p,q).length > 0) return 0; + if(cyclic(p) && inside(p,point(q,0),fillrule)) return 1; + if(cyclic(q) && inside(q,point(p,0),fillrule)) return -1; + return 0; +} + +// Return an arbitrary point strictly inside a cyclic path p according to +// the specified fill rule. +pair inside(path p, pen fillrule=currentpen) +{ + if(!cyclic(p)) abort("path is not cyclic"); + int n=length(p); + for(int i=0; i < n; ++i) { + pair z=point(p,i); + pair dir=dir(p,i); + if(dir == 0) continue; + real[] T=intersections(p,z,z+I*dir); + // Check midpoints of line segments formed between the + // corresponding intersection points and z. + for(int j=0; j < T.length; ++j) { + if(T[j] != i) { + pair w=point(p,T[j]); + pair m=0.5*(z+w); + if(interior(windingnumber(p,m),fillrule)) return m; + } + } + } + // cannot find an interior point: path is degenerate + return point(p,0); +} + +// Return all intersection times of path g with the vertical line through (x,0). +real[] times(path p, real x) +{ + return intersections(p,(x,0),(x,1)); +} + +// Return all intersection times of path g with the horizontal line through +// (0,z.y). +real[] times(path p, explicit pair z) +{ + return intersections(p,(0,z.y),(1,z.y)); +} + +path randompath(int n, bool cumulate=true, interpolate join=operator ..) +{ + guide g; + pair w; + for(int i=0; i <= n; ++i) { + pair z=(unitrand()-0.5,unitrand()-0.5); + if(cumulate) w += z; + else w=z; + g=join(g,w); + } + return g; +} + +path[] strokepath(path g, pen p=currentpen) +{ + path[] G=_strokepath(g,p); + if(G.length == 0) return G; + pair center(path g) {return 0.5*(min(g)+max(g));} + pair center(path[] g) {return 0.5*(min(g)+max(g));} + return shift(center(g)-center(G))*G; +} + +real braceinnerangle=radians(60); +real braceouterangle=radians(70); +real bracemidangle=radians(0); +real bracedefaultratio=0.14; +path brace(pair a, pair b, real amplitude=bracedefaultratio*length(b-a)) +{ + real length=length(b-a); + real sign=sgn(amplitude); + real hamplitude=0.5*amplitude; + real hlength=0.5*length; + path brace; + if(abs(amplitude) < bracedefaultratio*length) { + real slope=2*bracedefaultratio; + real controldist=(abs(hamplitude))/slope; + brace=(0,0){expi(sign*braceouterangle)}:: + {expi(sign*bracemidangle)}(controldist,hamplitude):: + {expi(sign*bracemidangle)}(hlength-controldist,hamplitude):: + {expi(sign*braceinnerangle)}(hlength,amplitude) {expi(-sign*braceinnerangle)}:: + {expi(-sign*bracemidangle)}(hlength+controldist,hamplitude):: + {expi(-sign*bracemidangle)}(length-controldist,hamplitude):: + {expi(-sign*braceouterangle)}(length,0); + } else { + brace=(0,0){expi(sign*braceouterangle)}:: + {expi(sign*bracemidangle)}(0.25*length,hamplitude):: + {expi(sign*braceinnerangle)}(hlength,amplitude){expi(-sign*braceinnerangle)}:: + {expi(-sign*bracemidangle)}(0.75*length,hamplitude):: + {expi(-sign*braceouterangle)}(length,0); + } + return shift(a)*rotate(degrees(b-a,warn=false))*brace; +} diff --git a/Build/source/utils/asymptote/base/plain_pens.asy b/Build/source/utils/asymptote/base/plain_pens.asy new file mode 100644 index 00000000000..b8465696581 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_pens.asy @@ -0,0 +1,368 @@ +real labelmargin=0.3; +real dotfactor=6; + +pen solid=linetype(new real[]); +pen dotted=linetype(new real[] {0,4}); +pen dashed=linetype(new real[] {8,8}); +pen longdashed=linetype(new real[] {24,8}); +pen dashdotted=linetype(new real[] {8,8,0,8}); +pen longdashdotted=linetype(new real[] {24,8,0,8}); + +pen linetype(string pattern, real offset=0, bool scale=true, bool adjust=true) +{ + return linetype((real[]) split(pattern),offset,scale,adjust); +} + +void defaultpen(real w) {defaultpen(linewidth(w));} +pen operator +(pen p, real w) {return p+linewidth(w);} +pen operator +(real w, pen p) {return linewidth(w)+p;} + +pen Dotted(pen p=currentpen) {return linetype(new real[] {0,3})+2*linewidth(p);} +pen Dotted=Dotted(); + +restricted pen squarecap=linecap(0); +restricted pen roundcap=linecap(1); +restricted pen extendcap=linecap(2); + +restricted pen miterjoin=linejoin(0); +restricted pen roundjoin=linejoin(1); +restricted pen beveljoin=linejoin(2); + +restricted pen zerowinding=fillrule(0); +restricted pen evenodd=fillrule(1); + +bool interior(int windingnumber, pen fillrule) +{ + return windingnumber != undefined && + (fillrule(fillrule) == 1 ? windingnumber % 2 == 1 : windingnumber != 0); +} + +restricted pen nobasealign=basealign(0); +restricted pen basealign=basealign(1); + +pen invisible=invisible(); +pen thin() {return settings.thin ? linewidth(0) : defaultpen;} +pen thick(pen p=currentpen) {return linewidth(linewidth(p));} +pen nullpen=linewidth(0)+invisible; + +pen black=gray(0); +pen white=gray(1); +pen gray=gray(0.5); + +pen red=rgb(1,0,0); +pen green=rgb(0,1,0); +pen blue=rgb(0,0,1); + +pen Cyan=cmyk(1,0,0,0); +pen Magenta=cmyk(0,1,0,0); +pen Yellow=cmyk(0,0,1,0); +pen Black=cmyk(0,0,0,1); + +pen cyan=rgb(0,1,1); +pen magenta=rgb(1,0,1); +pen yellow=rgb(1,1,0); + +pen palered=rgb(1,0.75,0.75); +pen palegreen=rgb(0.75,1,0.75); +pen paleblue=rgb(0.75,0.75,1); +pen palecyan=rgb(0.75,1,1); +pen palemagenta=rgb(1,0.75,1); +pen paleyellow=rgb(1,1,0.75); +pen palegray=gray(0.95); + +pen lightred=rgb(1,0.5,0.5); +pen lightgreen=rgb(0.5,1,0.5); +pen lightblue=rgb(0.5,0.5,1); +pen lightcyan=rgb(0.5,1,1); +pen lightmagenta=rgb(1,0.5,1); +pen lightyellow=rgb(1,1,0.5); +pen lightgray=gray(0.9); + +pen mediumred=rgb(1,0.25,0.25); +pen mediumgreen=rgb(0.25,1,0.25); +pen mediumblue=rgb(0.25,0.25,1); +pen mediumcyan=rgb(0.25,1,1); +pen mediummagenta=rgb(1,0.25,1); +pen mediumyellow=rgb(1,1,0.25); +pen mediumgray=gray(0.75); + +pen heavyred=rgb(0.75,0,0); +pen heavygreen=rgb(0,0.75,0); +pen heavyblue=rgb(0,0,0.75); +pen heavycyan=rgb(0,0.75,0.75); +pen heavymagenta=rgb(0.75,0,0.75); +pen lightolive=rgb(0.75,0.75,0); +pen heavygray=gray(0.25); + +pen deepred=rgb(0.5,0,0); +pen deepgreen=rgb(0,0.5,0); +pen deepblue=rgb(0,0,0.5); +pen deepcyan=rgb(0,0.5,0.5); +pen deepmagenta=rgb(0.5,0,0.5); +pen deepyellow=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 olive=deepyellow; +pen darkbrown=darkred; +pen pink=palemagenta; +pen palegrey=palegray; +pen lightgrey=lightgray; +pen mediumgrey=mediumgray; +pen grey=gray; +pen heavygrey=heavygray; +pen deepgrey=deepgray; +pen darkgrey=darkgray; + +// Options for handling label overwriting +restricted int Allow=0; +restricted int Suppress=1; +restricted int SuppressQuiet=2; +restricted int Move=3; +restricted int MoveQuiet=4; + +pen[] colorPen={red,blue,green,magenta,cyan,orange,purple,brown, + deepblue,deepgreen,chartreuse,fuchsia,lightred, + lightblue,black,pink,yellow,gray}; + +colorPen.cyclic=true; + +pen[] monoPen={solid,dashed,dotted,longdashed,dashdotted, + longdashdotted}; +monoPen.cyclic=true; + +pen Pen(int n) +{ + return (settings.gray || settings.bw) ? monoPen[n] : colorPen[n]; +} + +pen Pentype(int n) +{ + return (settings.gray || settings.bw) ? monoPen[n] : monoPen[n]+colorPen[n]; +} + +real dotsize(pen p=currentpen) +{ + return dotfactor*linewidth(p); +} + +pen fontsize(real size) +{ + return fontsize(size,1.2*size); +} + +real labelmargin(pen p=currentpen) +{ + return labelmargin*fontsize(p); +} + +void write(file file=stdout, string s="", pen[] p) +{ + for(int i=0; i < p.length; ++i) + write(file,s,p[i],endl); +} + +void usetypescript(string s, string encoding="") +{ + string s="\usetypescript["+s+"]"; + if(encoding != "") s +="["+encoding+"]"; + texpreamble(s); +} + +pen font(string name, string options="") +{ + // Work around misalignment in ConTeXt switchtobodyfont if font is not found. + return fontcommand(settings.tex == "context" ? + "\switchtobodyfont["+name+ + (options == "" ? "" : ","+options)+ + "]\removeunwantedspaces" : + "\font\ASYfont="+name+"\ASYfont"); +} + +pen font(string name, real size, string options="") +{ + string s=(string) (size/pt)+"pt"; + if(settings.tex == "context") + return fontsize(size)+font(name+","+s,options); + return fontsize(size)+font(name+" at "+s); +} + +pen font(string encoding, string family, string series, string shape) +{ + return fontcommand("\usefont{"+encoding+"}{"+family+"}{"+series+"}{"+shape+ + "}"); +} + +pen AvantGarde(string series="m", string shape="n") +{ + return font("OT1","pag",series,shape); +} +pen Bookman(string series="m", string shape="n") +{ + return font("OT1","pbk",series,shape); +} +pen Courier(string series="m", string shape="n") +{ + return font("OT1","pcr",series,shape); +} +pen Helvetica(string series="m", string shape="n") +{ + return font("OT1","phv",series,shape); +} +pen NewCenturySchoolBook(string series="m", string shape="n") +{ + return font("OT1","pnc",series,shape); +} +pen Palatino(string series="m", string shape="n") +{ + return font("OT1","ppl",series,shape); +} +pen TimesRoman(string series="m", string shape="n") +{ + return font("OT1","ptm",series,shape); +} +pen ZapfChancery(string series="m", string shape="n") +{ + return font("OT1","pzc",series,shape); +} +pen Symbol(string series="m", string shape="n") +{ + return font("OT1","psy",series,shape); +} +pen ZapfDingbats(string series="m", string shape="n") +{ + return font("OT1","pzd",series,shape); +} + +pen squarepen=makepen(shift(-0.5,-0.5)*unitsquare); + +struct hsv { + real h; + real v; + real s; + void operator init(real h, real s, real v) { + this.h=h; + this.s=s; + this.v=v; + } + void operator init(pen p) { + real[] c=colors(rgb(p)); + real r=c[0]; + real g=c[1]; + real b=c[2]; + real M=max(r,g,b); + real m=min(r,g,b); + if(M == m) this.h=0; + else { + real denom=1/(M-m); + if(M == r) { + this.h=60*(g-b)*denom; + if(g < b) h += 360; + } else if(M == g) { + this.h=60*(b-r)*denom+120; + } else + this.h=60*(r-g)*denom+240; + } + this.s=M == 0 ? 0 : 1-m/M; + this.v=M; + } + // return an rgb pen corresponding to h in [0,360) and s and v in [0,1]. + pen rgb() { + real H=(h % 360)/60; + int i=floor(H) % 6; + real f=H-i; + real[] V={v,v*(1-s),v*(1-(i % 2 == 0 ? 1-f : f)*s)}; + int[] a={0,2,1,1,2,0}; + int[] b={2,0,0,2,1,1}; + int[] c={1,1,2,0,0,2}; + return rgb(V[a[i]],V[b[i]],V[c[i]]); + } +} + +pen operator cast(hsv hsv) +{ + return hsv.rgb(); +} + +hsv operator cast(pen p) +{ + return hsv(p); +} + +real[] rgba(pen p) +{ + real[] a=colors(rgb(p)); + a.push(opacity(p)); + return a; +} + +pen rgba(real[] a) +{ + return rgb(a[0],a[1],a[2])+opacity(a[3]); +} + +// Return a pen corresponding to a given 6-character RGB hexadecimal string. +pen rgb(string s) +{ + int offset=substr(s,0,1) == '#' ? 1 : 0; + real value(string s, int i) {return hex(substr(s,2i+offset,2))/255;} + return rgb(value(s,0),value(s,1),value(s,2)); +} + +pen RGB(int r, int g, int b) +{ + return rgb(r/255,g/255,b/255); +} + +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/Build/source/utils/asymptote/base/plain_picture.asy b/Build/source/utils/asymptote/base/plain_picture.asy new file mode 100644 index 00000000000..c0c189ee691 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_picture.asy @@ -0,0 +1,1687 @@ +// Pre picture <<<1 +import plain_scaling; +import plain_bounds; + +include plain_prethree; + +// This variable is required by asymptote.sty. +pair viewportsize=0; // Horizontal and vertical viewport limits. + +restricted bool Aspect=true; +restricted bool IgnoreAspect=false; + +struct coords3 { + coord[] x,y,z; + void erase() { + x.delete(); + y.delete(); + z.delete(); + } + // Only a shallow copy of the individual elements of x and y + // is needed since, once entered, they are never modified. + coords3 copy() { + coords3 c=new coords3; + c.x=copy(x); + c.y=copy(y); + c.z=copy(z); + return c; + } + void append(coords3 c) { + x.append(c.x); + y.append(c.y); + z.append(c.z); + } + void push(triple user, triple truesize) { + x.push(coord.build(user.x,truesize.x)); + y.push(coord.build(user.y,truesize.y)); + z.push(coord.build(user.z,truesize.z)); + } + void push(coord cx, coord cy, coord cz) { + x.push(cx); + y.push(cy); + z.push(cz); + } + void push(transform3 t, coords3 c1, coords3 c2, coords3 c3) { + for(int i=0; i < c1.x.length; ++i) { + coord cx=c1.x[i], cy=c2.y[i], cz=c3.z[i]; + triple tinf=shiftless(t)*(0,0,0); + triple z=t*(cx.user,cy.user,cz.user); + triple w=(cx.truesize,cy.truesize,cz.truesize); + w=length(w)*unit(shiftless(t)*w); + coord Cx,Cy,Cz; + Cx.user=z.x; + Cy.user=z.y; + Cz.user=z.z; + Cx.truesize=w.x; + Cy.truesize=w.y; + Cz.truesize=w.z; + push(Cx,Cy,Cz); + } + } +} + +// scaleT and Legend <<< +typedef real scalefcn(real x); + +struct scaleT { + scalefcn T,Tinv; + bool logarithmic; + bool automin,automax; + void operator init(scalefcn T, scalefcn Tinv, bool logarithmic=false, + bool automin=false, bool automax=false) { + this.T=T; + this.Tinv=Tinv; + this.logarithmic=logarithmic; + this.automin=automin; + this.automax=automax; + } + scaleT copy() { + scaleT dest=scaleT(T,Tinv,logarithmic,automin,automax); + return dest; + } +}; + +scaleT operator init() +{ + scaleT S=scaleT(identity,identity); + return S; +} + +typedef void boundRoutine(); + +struct autoscaleT { + scaleT scale; + scaleT postscale; + real tickMin=-infinity, tickMax=infinity; + boundRoutine[] bound; // Optional routines to recompute the bounding box. + bool automin=false, automax=false; + bool automin() {return automin && scale.automin;} + bool automax() {return automax && scale.automax;} + + real T(real x) {return postscale.T(scale.T(x));} + scalefcn T() {return scale.logarithmic ? postscale.T : T;} + real Tinv(real x) {return scale.Tinv(postscale.Tinv(x));} + + autoscaleT copy() { + autoscaleT dest=new autoscaleT; + dest.scale=scale.copy(); + dest.postscale=postscale.copy(); + dest.tickMin=tickMin; + dest.tickMax=tickMax; + dest.bound=copy(bound); + dest.automin=(bool) automin; + dest.automax=(bool) automax; + return dest; + } +} + +struct ScaleT { + bool set; + autoscaleT x; + autoscaleT y; + autoscaleT z; + + ScaleT copy() { + ScaleT dest=new ScaleT; + dest.set=set; + dest.x=x.copy(); + dest.y=y.copy(); + dest.z=z.copy(); + return dest; + } +}; + +struct Legend { + string label; + pen plabel; + pen p; + frame mark; + bool above; + void operator init(string label, pen plabel=currentpen, pen p=nullpen, + frame mark=newframe, bool above=true) { + this.label=label; + this.plabel=plabel; + this.p=(p == nullpen) ? plabel : p; + this.mark=mark; + this.above=above; + } +} + +// >>> + +// Frame Alignment was here + +triple min3(pen p) +{ + return linewidth(p)*(-0.5,-0.5,-0.5); +} + +triple max3(pen p) +{ + return linewidth(p)*(0.5,0.5,0.5); +} + +// A function that draws an object to frame pic, given that the transform +// from user coordinates to true-size coordinates is t. +typedef void drawer(frame f, transform t); + +// A generalization of drawer that includes the final frame's bounds. +// TODO: Add documentation as to what T is. +typedef void drawerBound(frame f, transform t, transform T, pair lb, pair rt); + +struct node { + drawerBound d; + string key; + void operator init(drawerBound d, string key=xasyKEY()) { + this.d=d; + this.key=key; + } +} + +// PairOrTriple <<<1 +// This struct is used to represent a userMin/userMax which serves as both a +// pair and a triple depending on the context. +struct pairOrTriple { + real x,y,z; + void init() { x = y = z = 0; } +}; +void copyPairOrTriple(pairOrTriple dest, pairOrTriple src) +{ + dest.x = src.x; + dest.y = src.y; + dest.z = src.z; +} +pair operator cast (pairOrTriple a) { + return (a.x, a.y); +}; +triple operator cast (pairOrTriple a) { + return (a.x, a.y, a.z); +} +void write(pairOrTriple a) { + write((triple) a); +} + +struct picture { // <<<1 + // Nodes <<<2 + // Three-dimensional version of drawer and drawerBound: + typedef void drawer3(frame f, transform3 t, picture pic, projection P); + typedef void drawerBound3(frame f, transform3 t, transform3 T, + picture pic, projection P, triple lb, triple rt); + + struct node3 { + drawerBound3 d; + string key; + void operator init(drawerBound3 d, string key=xasyKEY()) { + this.d=d; + this.key=key; + } + } + + // The functions to do the deferred drawing. + node[] nodes; + node3[] nodes3; + + bool uptodate=true; + + struct bounds3 { + coords3 point,min,max; + bool exact=true; // An accurate picture bounds is provided by the user. + void erase() { + point.erase(); + min.erase(); + max.erase(); + } + bounds3 copy() { + bounds3 b=new bounds3; + b.point=point.copy(); + b.min=min.copy(); + b.max=max.copy(); + b.exact=exact; + return b; + } + } + + bounds bounds; + bounds3 bounds3; + + // Other Fields <<<2 + // Transform to be applied to this picture. + transform T; + transform3 T3; + + // The internal representation of the 3D user bounds. + private pairOrTriple umin, umax; + private bool usetx, usety, usetz; + + ScaleT scale; // Needed by graph + Legend[] legend; + + pair[] clipmax; // Used by beginclip/endclip + pair[] clipmin; + + // The maximum sizes in the x, y, and z directions; zero means no restriction. + real xsize=0, ysize=0; + + real xsize3=0, ysize3=0, zsize3=0; + + // Fixed unitsizes in the x y, and z directions; zero means use + // xsize, ysize, and zsize. + real xunitsize=0, yunitsize=0, zunitsize=0; + + // If true, the x and y directions must be scaled by the same amount. + bool keepAspect=true; + + // A fixed scaling transform. + bool fixed; + transform fixedscaling; + + // Init and erase <<<2 + void init() { + umin.init(); + umax.init(); + usetx=usety=usetz=false; + T3=identity(4); + } + init(); + + // Erase the current picture, retaining bounds. + void clear() { + nodes.delete(); + nodes3.delete(); + legend.delete(); + } + + // Erase the current picture, retaining any size specification. + void erase() { + clear(); + bounds.erase(); + bounds3.erase(); + T=identity(); + scale=new ScaleT; + init(); + } + + // Empty <<<2 + bool empty2() { + return nodes.length == 0; + } + + bool empty3() { + return nodes3.length == 0; + } + + bool empty() { + return empty2() && empty3(); + } + + // User min/max <<<2 + pair userMin2() {return bounds.userMin(); } + pair userMax2() {return bounds.userMax(); } + + bool userSetx2() { return bounds.userBoundsAreSet(); } + bool userSety2() { return bounds.userBoundsAreSet(); } + + triple userMin3() { return umin; } + triple userMax3() { return umax; } + + bool userSetx3() { return usetx; } + bool userSety3() { return usety; } + bool userSetz3() { return usetz; } + + private typedef real binop(real, real); + + // Helper functions for finding the minimum/maximum of two data, one of + // which may not be defined. + private static real merge(real x1, bool set1, real x2, bool set2, binop m) + { + return set1 ? (set2 ? m(x1,x2) : x1) : x2; + } + private pairOrTriple userExtreme(pair u2(), triple u3(), binop m) + { + bool setx2 = userSetx2(); + bool sety2 = userSety2(); + bool setx3 = userSetx3(); + bool sety3 = userSety3(); + + pair p; + if (setx2 || sety2) + p = u2(); + triple t = u3(); + + pairOrTriple r; + r.x = merge(p.x, setx2, t.x, setx3, m); + r.y = merge(p.y, sety2, t.y, sety3, m); + r.z = t.z; + + return r; + } + + // The combination of 2D and 3D data. + pairOrTriple userMin() { + return userExtreme(userMin2, userMin3, min); + } + pairOrTriple userMax() { + return userExtreme(userMax2, userMax3, max); + } + + bool userSetx() { return userSetx2() || userSetx3(); } + bool userSety() { return userSety2() || userSety3(); } + bool userSetz() = userSetz3; + + // Functions for setting the user bounds. + void userMinx3(real x) { + umin.x=x; + usetx=true; + } + + void userMiny3(real y) { + umin.y=y; + usety=true; + } + + void userMinz3(real z) { + umin.z=z; + usetz=true; + } + + void userMaxx3(real x) { + umax.x=x; + usetx=true; + } + + void userMaxy3(real y) { + umax.y=y; + usety=true; + } + + void userMaxz3(real z) { + umax.z=z; + usetz=true; + } + + void userMinx2(real x) { bounds.alterUserBound("minx", x); } + void userMinx(real x) { userMinx2(x); userMinx3(x); } + void userMiny2(real y) { bounds.alterUserBound("miny", y); } + void userMiny(real y) { userMiny2(y); userMiny3(y); } + void userMaxx2(real x) { bounds.alterUserBound("maxx", x); } + void userMaxx(real x) { userMaxx2(x); userMaxx3(x); } + void userMaxy2(real y) { bounds.alterUserBound("maxy", y); } + void userMaxy(real y) { userMaxy2(y); userMaxy3(y); } + void userMinz(real z) = userMinz3; + void userMaxz(real z) = userMaxz3; + + void userCorners3(triple c000, triple c001, triple c010, triple c011, + triple c100, triple c101, triple c110, triple c111) { + umin.x = min(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x); + umin.y = min(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y); + umin.z = min(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z); + umax.x = max(c000.x,c001.x,c010.x,c011.x,c100.x,c101.x,c110.x,c111.x); + umax.y = max(c000.y,c001.y,c010.y,c011.y,c100.y,c101.y,c110.y,c111.y); + umax.z = max(c000.z,c001.z,c010.z,c011.z,c100.z,c101.z,c110.z,c111.z); + } + + // Cache the current user-space bounding box x coodinates + void userBoxX3(real min, real max, binop m=min, binop M=max) { + if (usetx) { + umin.x=m(umin.x,min); + umax.x=M(umax.x,max); + } else { + umin.x=min; + umax.x=max; + usetx=true; + } + } + + // Cache the current user-space bounding box y coodinates + void userBoxY3(real min, real max, binop m=min, binop M=max) { + if (usety) { + umin.y=m(umin.y,min); + umax.y=M(umax.y,max); + } else { + umin.y=min; + umax.y=max; + usety=true; + } + } + + // Cache the current user-space bounding box z coodinates + void userBoxZ3(real min, real max, binop m=min, binop M=max) { + if (usetz) { + umin.z=m(umin.z,min); + umax.z=M(umax.z,max); + } else { + umin.z=min; + umax.z=max; + usetz=true; + } + } + + // Cache the current user-space bounding box + void userBox3(triple min, triple max) { + userBoxX3(min.x,max.x); + userBoxY3(min.y,max.y); + userBoxZ3(min.z,max.z); + } + + // Add drawer <<<2 + void add(drawerBound d, bool exact=false, bool above=true) { + uptodate=false; + if(!exact) bounds.exact=false; + if(above) + nodes.push(node(d)); + else + nodes.insert(0,node(d)); + } + + // Faster implementation of most common case. + void addExactAbove(drawerBound d) { + uptodate=false; + nodes.push(node(d)); + } + + void add(drawer d, bool exact=false, bool above=true) { + add(new void(frame f, transform t, transform T, pair, pair) { + d(f,t*T); + },exact,above); + } + + void add(drawerBound3 d, bool exact=false, bool above=true) { + uptodate=false; + if(!exact) bounds.exact=false; + if(above) + nodes3.push(node3(d)); + else + nodes3.insert(0,node3(d)); + } + + void add(drawer3 d, bool exact=false, bool above=true) { + add(new void(frame f, transform3 t, transform3 T, picture pic, + projection P, triple, triple) { + d(f,t*T,pic,P); + },exact,above); + } + + // Clip <<<2 + void clip(pair min, pair max, drawer d, bool exact=false) { + bounds.clip(min, max); + this.add(d,exact); + } + + void clip(pair min, pair max, drawerBound d, bool exact=false) { + bounds.clip(min, max); + this.add(d,exact); + } + + // Add sizing <<<2 + // Add a point to the sizing. + void addPoint(pair user, pair truesize=0) { + bounds.addPoint(user,truesize); + //userBox(user,user); + } + + // Add a point to the sizing, accounting also for the size of the pen. + void addPoint(pair user, pair truesize=0, pen p) { + addPoint(user,truesize+min(p)); + addPoint(user,truesize+max(p)); + } + + void addPoint(triple user, triple truesize=(0,0,0)) { + bounds3.point.push(user,truesize); + userBox3(user,user); + } + + void addPoint(triple user, triple truesize=(0,0,0), pen p) { + addPoint(user,truesize+min3(p)); + addPoint(user,truesize+max3(p)); + } + + // Add a box to the sizing. + void addBox(pair userMin, pair userMax, pair trueMin=0, pair trueMax=0) { + bounds.addBox(userMin, userMax, trueMin, trueMax); + } + + void addBox(triple userMin, triple userMax, triple trueMin=(0,0,0), + triple trueMax=(0,0,0)) { + bounds3.min.push(userMin,trueMin); + bounds3.max.push(userMax,trueMax); + userBox3(userMin,userMax); + } + + // For speed reason, we unravel the addPath routines from bounds. This + // avoids an extra function call. + from bounds unravel addPath; + + // Size commands <<<2 + void size(real x, real y=x, bool keepAspect=this.keepAspect) { + if(!empty()) uptodate=false; + xsize=x; + ysize=y; + this.keepAspect=keepAspect; + } + + void size3(real x, real y=x, real z=y, bool keepAspect=this.keepAspect) { + if(!empty3()) uptodate=false; + xsize3=x; + ysize3=y; + zsize3=z; + this.keepAspect=keepAspect; + } + + void unitsize(real x, real y=x, real z=y) { + uptodate=false; + xunitsize=x; + yunitsize=y; + zunitsize=z; + } + + // min/max of picture <<<2 + // Calculate the min for the final frame, given the coordinate transform. + pair min(transform t) { + return bounds.min(t); + } + + // Calculate the max for the final frame, given the coordinate transform. + pair max(transform t) { + return bounds.max(t); + } + + // Calculate the min for the final frame, given the coordinate transform. + triple min(transform3 t) { + if(bounds3.min.x.length == 0 && bounds3.point.x.length == 0 && + bounds3.max.x.length == 0) return (0,0,0); + triple a=t*(1,1,1)-t*(0,0,0), b=t*(0,0,0); + scaling xs=scaling.build(a.x,b.x); + scaling ys=scaling.build(a.y,b.y); + scaling zs=scaling.build(a.z,b.z); + return (min(min(min(infinity,xs,bounds3.point.x),xs,bounds3.min.x), + xs,bounds3.max.x), + min(min(min(infinity,ys,bounds3.point.y),ys,bounds3.min.y), + ys,bounds3.max.y), + min(min(min(infinity,zs,bounds3.point.z),zs,bounds3.min.z), + zs,bounds3.max.z)); + } + + // Calculate the max for the final frame, given the coordinate transform. + triple max(transform3 t) { + if(bounds3.min.x.length == 0 && bounds3.point.x.length == 0 && + bounds3.max.x.length == 0) return (0,0,0); + triple a=t*(1,1,1)-t*(0,0,0), b=t*(0,0,0); + scaling xs=scaling.build(a.x,b.x); + scaling ys=scaling.build(a.y,b.y); + scaling zs=scaling.build(a.z,b.z); + return (max(max(max(-infinity,xs,bounds3.point.x),xs,bounds3.min.x), + xs,bounds3.max.x), + max(max(max(-infinity,ys,bounds3.point.y),ys,bounds3.min.y), + ys,bounds3.max.y), + max(max(max(-infinity,zs,bounds3.point.z),zs,bounds3.min.z), + zs,bounds3.max.z)); + } + + void append(coords3 point, coords3 min, coords3 max, transform3 t, + bounds3 bounds) + { + // Add the coord info to this picture. + if(t == identity4) { + point.append(bounds.point); + min.append(bounds.min); + max.append(bounds.max); + } else { + point.push(t,bounds.point,bounds.point,bounds.point); + // Add in all 8 corner points, to properly size cuboid pictures. + point.push(t,bounds.min,bounds.min,bounds.min); + point.push(t,bounds.min,bounds.min,bounds.max); + point.push(t,bounds.min,bounds.max,bounds.min); + point.push(t,bounds.min,bounds.max,bounds.max); + point.push(t,bounds.max,bounds.min,bounds.min); + point.push(t,bounds.max,bounds.min,bounds.max); + point.push(t,bounds.max,bounds.max,bounds.min); + point.push(t,bounds.max,bounds.max,bounds.max); + } + } + + // Scaling and Fit <<<2 + // Returns the transform for turning user-space pairs into true-space pairs. + transform scaling(real xsize, real ysize, bool keepAspect=true, + bool warn=true) { + bounds b = (T == identity()) ? this.bounds : T * this.bounds; + + return b.scaling(xsize, ysize, xunitsize, yunitsize, keepAspect, warn); + } + + transform scaling(bool warn=true) { + return scaling(xsize,ysize,keepAspect,warn); + } + + // Returns the transform for turning user-space pairs into true-space triples. + transform3 scaling(real xsize, real ysize, real zsize, bool keepAspect=true, + bool warn=true) { + if(xsize == 0 && xunitsize == 0 && ysize == 0 && yunitsize == 0 + && zsize == 0 && zunitsize == 0) + return identity(4); + + coords3 Coords; + + append(Coords,Coords,Coords,T3,bounds3); + + real sx; + if(xunitsize == 0) { + if(xsize != 0) sx=calculateScaling("x",Coords.x,xsize,warn); + } else sx=xunitsize; + + real sy; + if(yunitsize == 0) { + if(ysize != 0) sy=calculateScaling("y",Coords.y,ysize,warn); + } else sy=yunitsize; + + real sz; + if(zunitsize == 0) { + if(zsize != 0) sz=calculateScaling("z",Coords.z,zsize,warn); + } else sz=zunitsize; + + if(sx == 0) { + sx=max(sy,sz); + if(sx == 0) + return identity(4); + } + if(sy == 0) sy=max(sz,sx); + if(sz == 0) sz=max(sx,sy); + + if(keepAspect && (xunitsize == 0 || yunitsize == 0 || zunitsize == 0)) + return scale3(min(sx,sy,sz)); + else + return scale(sx,sy,sz); + } + + transform3 scaling3(bool warn=true) { + return scaling(xsize3,ysize3,zsize3,keepAspect,warn); + } + + frame fit(transform t, transform T0=T, pair m, pair M) { + frame f; + for(node n : nodes) { + xasyKEY(n.key); + n.d(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(node3 n : nodes3) { + xasyKEY(n.key); + n.d(f,t,T0,pic,P,m,M); + } + return f; + } + + // Returns a rigid version of the picture using t to transform user coords + // into truesize coords. + frame fit(transform t) { + return fit(t,min(t),max(t)); + } + + frame fit3(transform3 t, picture pic, projection P) { + return fit3(t,pic,P,min(t),max(t)); + } + + // Add drawer wrappers <<<2 + void add(void d(picture, transform), bool exact=false) { + add(new void(frame f, transform t) { + picture opic=new picture; + d(opic,t); + add(f,opic.fit(identity())); + },exact); + } + + void add(void d(picture, transform3), bool exact=false, bool above=true) { + add(new void(frame f, transform3 t, picture pic2, projection P) { + picture opic=new picture; + d(opic,t); + add(f,opic.fit3(identity4,pic2,P)); + },exact,above); + } + + void add(void d(picture, transform3, transform3, triple, triple), + bool exact=false, bool above=true) { + add(new void(frame f, transform3 t, transform3 T, picture pic2, + projection P, triple lb, triple rt) { + picture opic=new picture; + d(opic,t,T,lb,rt); + add(f,opic.fit3(identity4,pic2,P)); + },exact,above); + } + + // More scaling <<<2 + frame scaled() { + frame f=fit(fixedscaling); + pair d=size(f); + static real epsilon=100*realEpsilon; + if(d.x > xsize*(1+epsilon)) + warning("xlimit","frame exceeds xlimit: "+(string) d.x+" > "+ + (string) xsize); + if(d.y > ysize*(1+epsilon)) + warning("ylimit","frame exceeds ylimit: "+(string) d.y+" > "+ + (string) ysize); + return f; + } + + // Calculate additional scaling required if only an approximate picture + // size estimate is available. + transform scale(frame f, real xsize=this.xsize, real ysize=this.ysize, + bool keepaspect=this.keepAspect) { + if(bounds.exact) return identity(); + pair m=min(f); + pair M=max(f); + real width=M.x-m.x; + real height=M.y-m.y; + real xgrow=xsize == 0 || width == 0 ? 1 : xsize/width; + real ygrow=ysize == 0 || height == 0 ? 1 : ysize/height; + if(keepAspect) { + real[] grow; + if(xsize > 0) grow.push(xgrow); + if(ysize > 0) grow.push(ygrow); + return scale(grow.length == 0 ? 1 : min(grow)); + } else return scale(xgrow,ygrow); + + } + + // Calculate additional scaling required if only an approximate picture + // size estimate is available. + transform3 scale3(frame f, real xsize3=this.xsize3, + real ysize3=this.ysize3, real zsize3=this.zsize3, + bool keepaspect=this.keepAspect) { + if(bounds3.exact) return identity(4); + triple m=min3(f); + triple M=max3(f); + real width=M.x-m.x; + real height=M.y-m.y; + real depth=M.z-m.z; + real xgrow=xsize3 == 0 || width == 0 ? 1 : xsize3/width; + real ygrow=ysize3 == 0 || height == 0 ? 1 : ysize3/height; + real zgrow=zsize3 == 0 || depth == 0 ? 1 : zsize3/depth; + if(keepAspect) { + real[] grow; + if(xsize3 > 0) grow.push(xgrow); + if(ysize3 > 0) grow.push(ygrow); + if(zsize3 > 0) grow.push(zgrow); + return scale3(grow.length == 0 ? 1 : min(grow)); + } else return scale(xgrow,ygrow,zgrow); + } + + // calculateTransform with scaling <<<2 + // Return the transform that would be used to fit the picture to a frame + transform calculateTransform(real xsize, real ysize, bool keepAspect=true, + bool warn=true) { + transform t=scaling(xsize,ysize,keepAspect,warn); + return scale(fit(t),xsize,ysize,keepAspect)*t; + } + + transform calculateTransform(bool warn=true) { + if(fixed) return fixedscaling; + return calculateTransform(xsize,ysize,keepAspect,warn); + } + + transform3 calculateTransform3(real xsize=xsize3, real ysize=ysize3, + real zsize=zsize3, + bool keepAspect=true, bool warn=true, + projection P=currentprojection) { + transform3 t=scaling(xsize,ysize,zsize,keepAspect,warn); + return scale3(fit3(t,null,P),keepAspect)*t; + } + + // min/max with xsize and ysize <<<2 + // NOTE: These are probably very slow as implemented. + pair min(real xsize=this.xsize, real ysize=this.ysize, + bool keepAspect=this.keepAspect, bool warn=true) { + return min(calculateTransform(xsize,ysize,keepAspect,warn)); + } + + pair max(real xsize=this.xsize, real ysize=this.ysize, + bool keepAspect=this.keepAspect, bool warn=true) { + return max(calculateTransform(xsize,ysize,keepAspect,warn)); + } + + triple min3(real xsize=this.xsize3, real ysize=this.ysize3, + real zsize=this.zsize3, bool keepAspect=this.keepAspect, + bool warn=true, projection P) { + return min(calculateTransform3(xsize,ysize,zsize,keepAspect,warn,P)); + } + + triple max3(real xsize=this.xsize3, real ysize=this.ysize3, + real zsize=this.zsize3, bool keepAspect=this.keepAspect, + bool warn=true, projection P) { + return max(calculateTransform3(xsize,ysize,zsize,keepAspect,warn,P)); + } + + // More Fitting <<<2 + // Returns the 2D picture fit to the requested size. + frame fit2(real xsize=this.xsize, real ysize=this.ysize, + bool keepAspect=this.keepAspect) { + if(fixed) return scaled(); + if(empty2()) + return newframe; + + transform t=scaling(xsize,ysize,keepAspect); + frame f=fit(t); + transform s=scale(f,xsize,ysize,keepAspect); + if(s == identity()) return f; + return fit(s*t); + } + + static frame fitter(string,picture,string,real,real,bool,bool,string,string, + light,projection); + frame fit(string prefix="", string format="", + real xsize=this.xsize, real ysize=this.ysize, + bool keepAspect=this.keepAspect, bool view=false, + string options="", string script="", light light=currentlight, + projection P=currentprojection) { + return fitter == null ? fit2(xsize,ysize,keepAspect) : + fitter(prefix,this,format,xsize,ysize,keepAspect,view,options,script, + light,P); + } + + // Fit a 3D picture. + frame fit3(projection P=currentprojection) { + if(settings.render == 0) return fit(P); + if(fixed) return scaled(); + if(empty3()) return newframe; + transform3 t=scaling(xsize3,ysize3,zsize3,keepAspect); + frame f=fit3(t,null,P); + transform3 s=scale3(f,xsize3,ysize3,zsize3,keepAspect); + if(s == identity4) return f; + return fit3(s*t,null,P); + } + + // In case only an approximate picture size estimate is available, return the + // fitted frame slightly scaled (including labels and true size distances) + // so that it precisely meets the given size specification. + frame scale(real xsize=this.xsize, real ysize=this.ysize, + bool keepAspect=this.keepAspect) { + frame f=fit(xsize,ysize,keepAspect); + transform s=scale(f,xsize,ysize,keepAspect); + if(s == identity()) return f; + return s*f; + } + + // Copying <<<2 + + // Copies enough information to yield the same userMin/userMax. + void userCopy2(picture pic) { + userMinx2(pic.userMin2().x); + userMiny2(pic.userMin2().y); + userMaxx2(pic.userMax2().x); + userMaxy2(pic.userMax2().y); + } + + void userCopy3(picture pic) { + copyPairOrTriple(umin, pic.umin); + copyPairOrTriple(umax, pic.umax); + usetx=pic.usetx; + usety=pic.usety; + usetz=pic.usetz; + } + + void userCopy(picture pic) { + userCopy2(pic); + userCopy3(pic); + } + + // Copies the drawing information, but not the sizing information into a new + // picture. Fitting this picture will not scale as the original picture would. + picture drawcopy() { + picture dest=new picture; + dest.nodes=copy(nodes); + dest.nodes3=copy(nodes3); + dest.T=T; + dest.T3=T3; + + // TODO: User bounds are sizing info, which probably shouldn't be part of + // a draw copy. Should we move this down to copy()? + dest.userCopy3(this); + + dest.scale=scale.copy(); + dest.legend=copy(legend); + + return dest; + } + + // A deep copy of this picture. Modifying the copied picture will not affect + // the original. + picture copy() { + picture dest=drawcopy(); + + dest.uptodate=uptodate; + dest.bounds=bounds.copy(); + dest.bounds3=bounds3.copy(); + + dest.xsize=xsize; dest.ysize=ysize; + dest.xsize3=xsize; dest.ysize3=ysize3; dest.zsize3=zsize3; + dest.keepAspect=keepAspect; + dest.xunitsize=xunitsize; dest.yunitsize=yunitsize; + dest.zunitsize=zunitsize; + dest.fixed=fixed; dest.fixedscaling=fixedscaling; + + return dest; + } + + // Helper function for defining transformed pictures. Do not call it + // directly. + picture transformed(transform t) { + picture dest=drawcopy(); + + // Replace nodes with a single drawer that realizes the transform. + node[] oldnodes = dest.nodes; + void drawAll(frame f, transform tt, transform T, pair lb, pair rt) { + transform Tt = T*t; + for (node n : oldnodes) { + xasyKEY(n.key); + n.d(f,tt,Tt,lb,rt); + } + } + dest.nodes = new node[] {node(drawAll)}; + + dest.uptodate=uptodate; + dest.bounds=bounds.transformed(t); + dest.bounds3=bounds3.copy(); + + dest.bounds.exact=false; + + dest.xsize=xsize; dest.ysize=ysize; + dest.xsize3=xsize; dest.ysize3=ysize3; dest.zsize3=zsize3; + dest.keepAspect=keepAspect; + dest.xunitsize=xunitsize; dest.yunitsize=yunitsize; + dest.zunitsize=zunitsize; + dest.fixed=fixed; dest.fixedscaling=fixedscaling; + + return dest; + } + + // Add Picture <<<2 + // Add a picture to this picture, such that the user coordinates will be + // scaled identically when fitted + void add(picture src, bool group=true, filltype filltype=NoFill, + bool above=true) { + // Copy the picture. Only the drawing function closures are needed, so we + // only copy them. This needs to be a deep copy, as src could later have + // objects added to it that should not be included in this picture. + + if(src == this) abort("cannot add picture to itself"); + + uptodate=false; + + picture srcCopy=src.drawcopy(); + // Draw by drawing the copied picture. + if(srcCopy.nodes.length > 0) { + nodes.push(node(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(node3(new void(frame f, transform3 t, transform3 T3, + picture pic, projection P, triple m, triple M) + { + add(f,srcCopy.fit3(t,T3*srcCopy.T3,pic,P,m,M),group,above); + })); + } + + legend.append(src.legend); + + if(src.usetx) userBoxX3(src.umin.x,src.umax.x); + if(src.usety) userBoxY3(src.umin.y,src.umax.y); + if(src.usetz) userBoxZ3(src.umin.z,src.umax.z); + + bounds.append(srcCopy.T, src.bounds); + //append(bounds.point,bounds.min,bounds.max,srcCopy.T,src.bounds); + append(bounds3.point,bounds3.min,bounds3.max,srcCopy.T3,src.bounds3); + + //if(!src.bounds.exact) bounds.exact=false; + if(!src.bounds3.exact) bounds3.exact=false; + } +} + +// Post Struct <<<1 +picture operator * (transform t, picture orig) +{ + return orig.transformed(t); +} + +picture operator * (transform3 t, picture orig) +{ + picture pic=orig.copy(); + pic.T3=t*pic.T3; + triple umin=pic.userMin3(), umax=pic.userMax3(); + pic.userCorners3(t*umin, + t*(umin.x,umin.y,umax.z), + t*(umin.x,umax.y,umin.z), + t*(umin.x,umax.y,umax.z), + t*(umax.x,umin.y,umin.z), + t*(umax.x,umin.y,umax.z), + t*(umax.x,umax.y,umin.z), + t*umax); + pic.bounds3.exact=false; + return pic; +} + +picture currentpicture; + +void size(picture pic=currentpicture, real x, real y=x, + bool keepAspect=pic.keepAspect) +{ + pic.size(x,y,keepAspect); +} + +void size(picture pic=currentpicture, transform t) +{ + if(pic.empty3()) { + pair z=size(pic.fit(t)); + pic.size(z.x,z.y); + } +} + +void size3(picture pic=currentpicture, real x, real y=x, real z=y, + bool keepAspect=pic.keepAspect) +{ + pic.size3(x,y,z,keepAspect); +} + +void unitsize(picture pic=currentpicture, real x, real y=x, real z=y) +{ + pic.unitsize(x,y,z); +} + +void size(picture pic=currentpicture, real xsize, real ysize, + pair min, pair max) +{ + pair size=max-min; + pic.unitsize(size.x != 0 ? xsize/size.x : 0, + size.y != 0 ? ysize/size.y : 0); +} + +void size(picture dest, picture src) +{ + dest.size(src.xsize,src.ysize,src.keepAspect); + dest.size3(src.xsize3,src.ysize3,src.zsize3,src.keepAspect); + dest.unitsize(src.xunitsize,src.yunitsize,src.zunitsize); +} + +pair min(picture pic, bool user=false) +{ + transform t=pic.calculateTransform(); + pair z=pic.min(t); + return user ? inverse(t)*z : z; +} + +pair max(picture pic, bool user=false) +{ + transform t=pic.calculateTransform(); + pair z=pic.max(t); + return user ? inverse(t)*z : z; +} + +pair size(picture pic, bool user=false) +{ + transform t=pic.calculateTransform(); + pair M=pic.max(t); + pair m=pic.min(t); + if(!user) return M-m; + t=inverse(t); + return t*M-t*m; +} + +// Frame Alignment <<< +pair rectify(pair dir) +{ + real scale=max(abs(dir.x),abs(dir.y)); + if(scale != 0) dir *= 0.5/scale; + dir += (0.5,0.5); + return dir; +} + +pair point(frame f, pair dir) +{ + pair m=min(f); + pair M=max(f); + return m+realmult(rectify(dir),M-m); +} + +path[] align(path[] g, transform t=identity(), pair position, + pair align, pen p=currentpen) +{ + if(g.length == 0) return g; + pair m=min(g); + pair M=max(g); + pair dir=rectify(inverse(t)*-align); + if(basealign(p) == 1) + dir -= (0,m.y/(M.y-m.y)); + pair a=m+realmult(dir,M-m); + return shift(position+align*labelmargin(p))*t*shift(-a)*g; +} + +// Returns a transform for aligning frame f in the direction align +transform shift(frame f, pair align) +{ + return shift(align-point(f,-align)); +} + +// Returns a copy of frame f aligned in the direction align +frame align(frame f, pair align) +{ + return shift(f,align)*f; +} +// >>> + +pair point(picture pic=currentpicture, pair dir, bool user=true) +{ + pair umin = pic.userMin2(); + pair umax = pic.userMax2(); + + pair z=umin+realmult(rectify(dir),umax-umin); + return user ? z : pic.calculateTransform()*z; +} + +pair truepoint(picture pic=currentpicture, pair dir, bool user=true) +{ + transform t=pic.calculateTransform(); + pair m=pic.min(t); + pair M=pic.max(t); + pair z=m+realmult(rectify(dir),M-m); + return user ? inverse(t)*z : z; +} + +// Transform coordinate in [0,1]x[0,1] to current user coordinates. +pair relative(picture pic=currentpicture, pair z) +{ + return pic.userMin2()+realmult(z,pic.userMax2()-pic.userMin2()); +} + +void add(picture pic=currentpicture, drawer d, bool exact=false) +{ + pic.add(d,exact); +} + +typedef void drawer3(frame f, transform3 t, picture pic, projection P); +void add(picture pic=currentpicture, drawer3 d, bool exact=false) +{ + pic.add(d,exact); +} + +void add(picture pic=currentpicture, void d(picture,transform), + bool exact=false) +{ + pic.add(d,exact); +} + +void add(picture pic=currentpicture, void d(picture,transform3), + bool exact=false) +{ + pic.add(d,exact); +} + +void begingroup(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform) { + begingroup(f); + },true); +} + +void endgroup(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform) { + endgroup(f); + },true); +} + +void Draw(picture pic=currentpicture, path g, pen p=currentpen) +{ + pic.add(new void(frame f, transform t) { + draw(f,t*g,p); + },true); + pic.addPath(g,p); +} + +// Default arguments have been removed to increase speed. +void _draw(picture pic, path g, pen p, margin margin) +{ + if (size(nib(p)) == 0 && margin==NoMargin) { + // Inline the drawerBound wrapper for speed. + pic.addExactAbove(new void(frame f, transform t, transform T, pair, pair) { + _draw(f,t*T*g,p); + }); + } else { + pic.add(new void(frame f, transform t) { + draw(f,margin(t*g,p).g,p); + },true); + } + pic.addPath(g,p); +} + +void Draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen) +{ + // Could optimize this by adding one drawer. + for(int i=0; i < g.length; ++i) Draw(pic,g[i],p); +} + +void fill(picture pic=currentpicture, path[] g, pen p=currentpen, + bool copy=true) +{ + if(copy) + g=copy(g); + pic.add(new void(frame f, transform t) { + fill(f,t*g,p,false); + },true); + pic.addPath(g); +} + +void drawstrokepath(picture pic=currentpicture, path g, pen strokepen, + pen p=currentpen) +{ + pic.add(new void(frame f, transform t) { + draw(f,strokepath(t*g,strokepen),p); + },true); + pic.addPath(g,p); +} + +void latticeshade(picture pic=currentpicture, path[] g, bool stroke=false, + pen fillrule=currentpen, pen[][] p, bool copy=true) +{ + if(copy) { + g=copy(g); + p=copy(p); + } + pic.add(new void(frame f, transform t) { + latticeshade(f,t*g,stroke,fillrule,p,t,false); + },true); + pic.addPath(g); +} + +void axialshade(picture pic=currentpicture, path[] g, bool stroke=false, + pen pena, pair a, bool extenda=true, + pen penb, pair b, bool extendb=true, bool copy=true) +{ + if(copy) + g=copy(g); + pic.add(new void(frame f, transform t) { + axialshade(f,t*g,stroke,pena,t*a,extenda,penb,t*b,extendb,false); + },true); + pic.addPath(g); +} + +void radialshade(picture pic=currentpicture, path[] g, bool stroke=false, + pen pena, pair a, real ra, bool extenda=true, + pen penb, pair b, real rb, bool extendb=true, 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,extenda,penb,B,RB,extendb,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=new path[], + pair[][] z=new pair[][], bool copy=true) +{ + bool compact=b.length == 0 || b[0] == nullpath; + if(copy) { + g=copy(g); + p=copy(p); + if(!compact) 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]; + path[] G=t*g; + if(compact) + tensorshade(f,G,stroke,fillrule,p,Z,false); + else + tensorshade(f,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, pair[] z=new pair[]) +{ + tensorshade(f,g,stroke,fillrule,new pen[][] {p},b, + z.length > 0 ? new pair[][] {z} : new pair[][]); +} + +void tensorshade(picture pic=currentpicture, path[] g, bool stroke=false, + pen fillrule=currentpen, pen[] p, + path b=nullpath, pair[] z=new pair[]) +{ + tensorshade(pic,g,stroke,fillrule,new pen[][] {p},b, + z.length > 0 ? new pair[][] {z} : new pair[][]); +} + +// Smoothly shade the regions between consecutive paths of a sequence using a +// given array of pens: +void draw(picture pic=currentpicture, path[] g, pen fillrule=currentpen, + pen[] p) +{ + path[] G; + pen[][] P; + string differentlengths="arrays have different lengths"; + if(g.length != p.length) abort(differentlengths); + for(int i=0; i < g.length-1; ++i) { + path g0=g[i]; + path g1=g[i+1]; + if(length(g0) != length(g1)) abort(differentlengths); + for(int j=0; j < length(g0); ++j) { + G.push(subpath(g0,j,j+1)--reverse(subpath(g1,j,j+1))--cycle); + P.push(new pen[] {p[i],p[i],p[i+1],p[i+1]}); + } + } + tensorshade(pic,G,fillrule,P); +} + +void functionshade(picture pic=currentpicture, path[] g, bool stroke=false, + pen fillrule=currentpen, string shader, bool copy=true) +{ + if(copy) + g=copy(g); + pic.add(new void(frame f, transform t) { + functionshade(f,t*g,stroke,fillrule,shader); + },true); + pic.addPath(g); +} + +void filldraw(picture pic=currentpicture, path[] g, pen fillpen=currentpen, + pen drawpen=currentpen) +{ + begingroup(pic); + fill(pic,g,fillpen); + Draw(pic,g,drawpen); + endgroup(pic); +} + +void clip(picture pic=currentpicture, path[] g, bool stroke=false, + pen fillrule=currentpen, bool copy=true) +{ + if(copy) + g=copy(g); + pic.clip(min(g), max(g), + new void(frame f, transform t) { + clip(f,t*g,stroke,fillrule,false); + }, + true); +} + +void beginclip(picture pic=currentpicture, path[] g, bool stroke=false, + pen fillrule=currentpen, bool copy=true) +{ + if(copy) + g=copy(g); + + pic.clipmin.push(min(g)); + pic.clipmax.push(max(g)); + + pic.add(new void(frame f, transform t) { + beginclip(f,t*g,stroke,fillrule,false); + },true); +} + +void endclip(picture pic=currentpicture) +{ + pair min,max; + if (pic.clipmin.length > 0 && pic.clipmax.length > 0) + { + min = pic.clipmin.pop(); + max = pic.clipmax.pop(); + } + else + { + // We should probably abort here, since the PostScript output will be + // garbage. + warning("endclip", "endclip without beginclip"); + min = pic.userMin2(); + max = pic.userMax2(); + } + + pic.clip(min, max, + new void(frame f, transform) { + endclip(f); + }, + true); +} + +void unfill(picture pic=currentpicture, path[] g, bool copy=true) +{ + if(copy) + g=copy(g); + pic.add(new void(frame f, transform t) { + unfill(f,t*g,false); + },true); +} + +void filloutside(picture pic=currentpicture, path[] g, pen p=currentpen, + bool copy=true) +{ + if(copy) + g=copy(g); + pic.add(new void(frame f, transform t) { + filloutside(f,t*g,p,false); + },true); + pic.addPath(g); +} + +// Use a fixed scaling to map user coordinates in box(min,max) to the +// desired picture size. +transform fixedscaling(picture pic=currentpicture, pair min, pair max, + pen p=nullpen, bool warn=false) +{ + Draw(pic,min,p+invisible); + Draw(pic,max,p+invisible); + pic.fixed=true; + return pic.fixedscaling=pic.calculateTransform(pic.xsize,pic.ysize, + pic.keepAspect); +} + +// Add frame src to frame dest about position with optional grouping. +void add(frame dest, frame src, pair position, bool group=false, + filltype filltype=NoFill, bool above=true) +{ + add(dest,shift(position)*src,group,filltype,above); +} + +// Add frame src to picture dest about position with optional grouping. +void add(picture dest=currentpicture, frame src, pair position=0, + bool group=true, filltype filltype=NoFill, bool above=true) +{ + if(is3D(src)) { + dest.add(new void(frame f, transform3, picture, projection) { + add(f,src); // always add about 3D origin (ignore position) + },true); + dest.addBox((0,0,0),(0,0,0),min3(src),max3(src)); + } else { + dest.add(new void(frame f, transform t) { + add(f,shift(t*position)*src,group,filltype,above); + },true); + dest.addBox(position,position,min(src),max(src)); + } +} + +// Like add(picture,frame,pair) but extend picture to accommodate frame. +void attach(picture dest=currentpicture, frame src, pair position=0, + bool group=true, filltype filltype=NoFill, bool above=true) +{ + transform t=dest.calculateTransform(); + add(dest,src,position,group,filltype,above); + pair s=size(dest.fit(t)); + size(dest,dest.xsize != 0 ? s.x : 0,dest.ysize != 0 ? s.y : 0); +} + +// Like add(picture,frame,pair) but align frame in direction align. +void add(picture dest=currentpicture, frame src, pair position, pair align, + bool group=true, filltype filltype=NoFill, bool above=true) +{ + add(dest,align(src,align),position,group,filltype,above); +} + +// Like add(frame,frame,pair) but align frame in direction align. +void add(frame dest, frame src, pair position, pair align, + bool group=true, filltype filltype=NoFill, bool above=true) +{ + add(dest,align(src,align),position,group,filltype,above); +} + +// Like add(picture,frame,pair,pair) but extend picture to accommodate frame; +void attach(picture dest=currentpicture, frame src, pair position, + pair align, bool group=true, filltype filltype=NoFill, + bool above=true) +{ + attach(dest,align(src,align),position,group,filltype,above); +} + +// Add a picture to another such that user coordinates in both will be scaled +// identically in the shipout. +void add(picture dest, picture src, bool group=true, filltype filltype=NoFill, + bool above=true) +{ + dest.add(src,group,filltype,above); +} + +void add(picture src, bool group=true, filltype filltype=NoFill, + bool above=true) +{ + currentpicture.add(src,group,filltype,above); +} + +// Fit the picture src using the identity transformation (so user +// coordinates and truesize coordinates agree) and add it about the point +// position to picture dest. +void add(picture dest, picture src, pair position, bool group=true, + filltype filltype=NoFill, bool above=true) +{ + add(dest,src.fit(identity()),position,group,filltype,above); +} + +void add(picture src, pair position, bool group=true, filltype filltype=NoFill, + bool above=true) +{ + add(currentpicture,src,position,group,filltype,above); +} + +// Fill a region about the user-coordinate 'origin'. +void fill(pair origin, picture pic=currentpicture, path[] g, pen p=currentpen) +{ + picture opic; + fill(opic,g,p); + add(pic,opic,origin); +} + +void postscript(picture pic=currentpicture, string s) +{ + pic.add(new void(frame f, transform) { + postscript(f,s); + },true); +} + +void postscript(picture pic=currentpicture, string s, pair min, pair max) +{ + pic.add(new void(frame f, transform t) { + postscript(f,s,t*min,t*max); + },true); +} + +void tex(picture pic=currentpicture, string s) +{ + // Force TeX string s to be evaluated immediately (in case it is a macro). + frame g; + tex(g,s); + size(g); + pic.add(new void(frame f, transform) { + tex(f,s); + },true); +} + +void tex(picture pic=currentpicture, string s, pair min, pair max) +{ + frame g; + tex(g,s); + size(g); + pic.add(new void(frame f, transform t) { + tex(f,s,t*min,t*max); + },true); +} + +void layer(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform) { + layer(f); + },true); +} + +void erase(picture pic=currentpicture) +{ + pic.uptodate=false; + pic.erase(); +} + +void begin(picture pic=currentpicture, string name, string id="", + bool visible=true) +{ + if(!latex() || !pdf()) return; + settings.twice=true; + if(id == "") id=string(++ocgindex); + tex(pic,"\begin{ocg}{"+name+"}{"+id+"}{"+(visible ? "1" : "0")+"}"); + layer(pic); +} + +void end(picture pic=currentpicture) +{ + if(!latex() || !pdf()) return; + tex(pic,"\end{ocg}"); + layer(pic); +} + +// For users of the LaTeX babel package. +void deactivatequote(picture pic=currentpicture) +{ + tex(pic,"\catcode`\"=12"); +} + +void activatequote(picture pic=currentpicture) +{ + tex(pic,"\catcode`\"=13"); +} diff --git a/Build/source/utils/asymptote/base/plain_prethree.asy b/Build/source/utils/asymptote/base/plain_prethree.asy new file mode 100644 index 00000000000..968ae2943a6 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_prethree.asy @@ -0,0 +1,216 @@ +// Critical definitions for transform3 needed by projection and picture. + +pair viewportmargin=settings.viewportmargin; + +typedef real[][] transform3; +restricted transform3 identity4=identity(4); + +// A uniform 3D scaling. +transform3 scale3(real s) +{ + transform3 t=identity(4); + t[0][0]=t[1][1]=t[2][2]=s; + return t; +} + +// Simultaneous 3D scalings in the x, y, and z directions. +transform3 scale(real x, real y, real z) +{ + transform3 t=identity(4); + t[0][0]=x; + t[1][1]=y; + t[2][2]=z; + return t; +} + +transform3 shiftless(transform3 t) +{ + transform3 T=copy(t); + T[0][3]=T[1][3]=T[2][3]=0; + return T; +} + +real camerafactor=2; // Factor used for camera adjustment. + +struct transformation { + transform3 modelview; // For orientation and positioning + transform3 projection; // For 3D to 2D projection + bool infinity; + void operator init(transform3 modelview) { + this.modelview=modelview; + this.projection=identity4; + infinity=true; + } + void operator init(transform3 modelview, transform3 projection) { + this.modelview=modelview; + this.projection=projection; + infinity=false; + } + transform3 compute() { + return infinity ? modelview : projection*modelview; + } + transformation copy() { + transformation T=new transformation; + T.modelview=copy(modelview); + T.projection=copy(projection); + T.infinity=infinity; + return T; + } +} + +struct projection { + transform3 t; // projection*modelview (cached) + bool infinity; + bool absolute=false; + triple camera; // Position of camera. + triple up; // A vector that should be projected to direction (0,1). + triple target; // Point where camera is looking at. + triple normal; // Normal vector from target to projection plane. + pair viewportshift; // Fractional viewport shift. + real zoom=1; // Zoom factor. + real angle; // Lens angle (for perspective projection). + bool showtarget=true; // Expand bounding volume to include target? + typedef transformation projector(triple camera, triple up, triple target); + projector projector; + bool autoadjust=true; // Adjust camera to lie outside bounding volume? + bool center=false; // Center target within bounding volume? + int ninterpolate; // Used for projecting nurbs to 2D Bezier curves. + bool bboxonly=true; // Typeset label bounding box only. + + transformation T; + + void calculate() { + T=projector(camera,up,target); + t=T.compute(); + infinity=T.infinity; + ninterpolate=infinity ? 1 : 16; + } + + triple vector() { + return camera-target; + } + + void operator init(triple camera, triple up=(0,0,1), triple target=(0,0,0), + triple normal=camera-target, + real zoom=1, real angle=0, pair viewportshift=0, + bool showtarget=true, bool autoadjust=true, + bool center=false, projector projector) { + this.camera=camera; + this.up=up; + this.target=target; + this.normal=normal; + this.zoom=zoom; + this.angle=angle; + this.viewportshift=viewportshift; + this.showtarget=showtarget; + this.autoadjust=autoadjust; + this.center=center; + this.projector=projector; + calculate(); + } + + projection copy() { + projection P=new projection; + P.t=t; + P.infinity=infinity; + P.absolute=absolute; + P.camera=camera; + P.up=up; + P.target=target; + P.normal=normal; + P.zoom=zoom; + P.angle=angle; + P.viewportshift=viewportshift; + P.showtarget=showtarget; + P.autoadjust=autoadjust; + P.center=center; + P.projector=projector; + P.ninterpolate=ninterpolate; + P.bboxonly=bboxonly; + P.T=T.copy(); + return P; + } + + // Return the maximum distance of box(m,M) from target. + real distance(triple m, triple M) { + triple[] c={m,(m.x,m.y,M.z),(m.x,M.y,m.z),(m.x,M.y,M.z), + (M.x,m.y,m.z),(M.x,m.y,M.z),(M.x,M.y,m.z),M}; + return max(abs(c-target)); + } + + + // This is redefined here to make projection as self-contained as possible. + static private real sqrtEpsilon = sqrt(realEpsilon); + + // Move the camera so that the box(m,M) rotated about target will always + // lie in front of the clipping plane. + bool adjust(triple m, triple M) { + triple v=camera-target; + real d=distance(m,M); + static real lambda=camerafactor*(1-sqrtEpsilon); + if(lambda*d >= abs(v)) { + camera=target+camerafactor*d*unit(v); + calculate(); + return true; + } + return false; + } +} + +projection currentprojection; + +struct light { + real[][] diffuse; + real[][] specular; + pen background=nullpen; // Background color of the 3D canvas. + real specularfactor; + triple[] position; // Only directional lights are currently implemented. + + transform3 T=identity(4); // Transform to apply to normal vectors. + + bool on() {return position.length > 0;} + + void operator init(pen[] diffuse, + pen[] specular=diffuse, pen background=nullpen, + real specularfactor=1, + triple[] position) { + int n=diffuse.length; + assert(specular.length == n && position.length == n); + + this.diffuse=new real[n][]; + this.specular=new real[n][]; + this.background=background; + this.position=new triple[n]; + for(int i=0; i < position.length; ++i) { + this.diffuse[i]=rgba(diffuse[i]); + this.specular[i]=rgba(specular[i]); + this.position[i]=unit(position[i]); + } + this.specularfactor=specularfactor; + } + + void operator init(pen diffuse=white, pen specular=diffuse, + pen background=nullpen, real specularfactor=1 ...triple[] position) { + int n=position.length; + operator init(array(n,diffuse),array(n,specular), + background,specularfactor,position); + } + + void operator init(pen diffuse=white, pen specular=diffuse, + pen background=nullpen, real x, real y, real z) { + operator init(diffuse,specular,background,(x,y,z)); + } + + void operator init(explicit light light) { + diffuse=copy(light.diffuse); + specular=copy(light.specular); + background=light.background; + specularfactor=light.specularfactor; + position=copy(light.position); + } + + real[] background() {return rgba(background == nullpen ? white : background);} +} + +light currentlight; + diff --git a/Build/source/utils/asymptote/base/plain_scaling.asy b/Build/source/utils/asymptote/base/plain_scaling.asy new file mode 100644 index 00000000000..5bed3338e9b --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_scaling.asy @@ -0,0 +1,258 @@ +real expansionfactor=sqrt(2); + +// A coordinate in "flex space." A linear combination of user and true-size +// coordinates. +struct coord { + real user,truesize; + + // Build a coord. + static coord build(real user, real truesize) { + coord c=new coord; + c.user=user; + c.truesize=truesize; + return c; + } + + // Deep copy of coordinate. Users may add coords to the picture, but then + // modify the struct. To prevent this from yielding unexpected results, deep + // copying is used. + coord copy() { + return build(user, truesize); + } + + void clip(real min, real max) { + user=min(max(user,min),max); + truesize=0; + } +} + +bool operator <= (coord a, coord b) +{ + return a.user <= b.user && a.truesize <= b.truesize; +} + +bool operator >= (coord a, coord b) +{ + return a.user >= b.user && a.truesize >= b.truesize; +} + +// Find the maximal elements of the input array, using the partial ordering +// given. +coord[] maxcoords(coord[] in, bool operator <= (coord,coord)) +{ + // As operator <= is defined in the parameter list, it has a special + // meaning in the body of the function. + + coord best; + coord[] c; + + int n=in.length; + + if(n == 0) + return c; + + int first=0; + // Add the first coord without checking restrictions (as there are none). + best=in[first]; + c.push(best); + + static int NONE=-1; + + int dominator(coord x) + { + // This assumes it has already been checked against the best. + for(int i=1; i < c.length; ++i) + if(x <= c[i]) + return i; + return NONE; + } + + void promote(int i) + { + // Swap with the top + coord x=c[i]; + c[i]=best; + best=c[0]=x; + } + + void addmaximal(coord x) + { + coord[] newc; + + // Check if it beats any others. + for(int i=0; i < c.length; ++i) { + coord y=c[i]; + if(!(y <= x)) + newc.push(y); + } + newc.push(x); + c=newc; + best=c[0]; + } + + void add(coord x) + { + if(x <= best) + return; + else { + int i=dominator(x); + if(i == NONE) + addmaximal(x); + else + promote(i); + } + } + + for(int i=1; i < n; ++i) + add(in[i]); + + return c; +} + +struct coords2 { + coord[] x,y; + void erase() { + x.delete(); + y.delete(); + } + // Only a shallow copy of the individual elements of x and y + // is needed since, once entered, they are never modified. + coords2 copy() { + coords2 c=new coords2; + c.x=copy(x); + c.y=copy(y); + return c; + } + void append(coords2 c) { + x.append(c.x); + y.append(c.y); + } + void push(pair user, pair truesize) { + x.push(coord.build(user.x,truesize.x)); + y.push(coord.build(user.y,truesize.y)); + } + void push(coord cx, coord cy) { + x.push(cx); + y.push(cy); + } + void push(transform t, coords2 c1, coords2 c2) { + for(int i=0; i < c1.x.length; ++i) { + coord cx=c1.x[i], cy=c2.y[i]; + pair tinf=shiftless(t)*(0,0); + pair z=t*(cx.user,cy.user); + pair w=(cx.truesize,cy.truesize); + w=length(w)*unit(shiftless(t)*w); + coord Cx,Cy; + Cx.user=z.x; + Cy.user=z.y; + Cx.truesize=w.x; + Cy.truesize=w.y; + push(Cx,Cy); + } + } + void xclip(real min, real max) { + for(int i=0; i < x.length; ++i) + x[i].clip(min,max); + } + void yclip(real min, real max) { + for(int i=0; i < y.length; ++i) + y[i].clip(min,max); + } +} + +// The scaling in one dimension: x --> a*x + b +struct scaling { + real a,b; + static scaling build(real a, real b) { + scaling s=new scaling; + s.a=a; s.b=b; + return s; + } + real scale(real x) { + return a*x+b; + } + real scale(coord c) { + return scale(c.user) + c.truesize; + } +} + +// Calculate the minimum point in scaling the coords. +real min(real m, scaling s, coord[] c) { + for(int i=0; i < c.length; ++i) + if(s.scale(c[i]) < m) + m=s.scale(c[i]); + return m; +} + +// Calculate the maximum point in scaling the coords. +real max(real M, scaling s, coord[] c) { + for(int i=0; i < c.length; ++i) + if(s.scale(c[i]) > M) + M=s.scale(c[i]); + return M; +} + +import simplex; + +/* + Calculate the sizing constants for the given array and maximum size. + Solve 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. +*/ +real calculateScaling(string dir, coord[] m, coord[] M, real size, + bool warn=true) { + real[][] A; + real[] b; + real[] c=new real[] {-1,0,0}; + + void addMinCoord(coord c) { + // (a*user + b) + truesize >= 0: + A.push(new real[] {c.user,1,-1}); + b.push(-c.truesize); + } + void addMaxCoord(coord c) { + // (a*user + b) + truesize <= size: + A.push(new real[] {-c.user,-1,1}); + b.push(c.truesize-size); + } + + for (int i=0; i < m.length; ++i) + addMinCoord(m[i]); + for (int i=0; i < M.length; ++i) + addMaxCoord(M[i]); + + int[] s=array(A.length,1); + simplex S=simplex(c,A,s,b); + + if(S.case == S.OPTIMAL) { + return S.x[0]; + } else if(S.case == S.UNBOUNDED) { + if(warn) warning("unbounded",dir+" scaling in picture unbounded"); + return 0; + } else { + if(!warn) return 1; + + bool userzero(coord[] coords) { + for(var coord : coords) + if(coord.user != 0) return false; + return true; + } + + if((userzero(m) && userzero(M)) || size >= infinity) return 1; + + warning("cannotfit","cannot fit picture to "+dir+"size "+(string) size + +"...enlarging..."); + + return calculateScaling(dir,m,M,expansionfactor*size,warn); + } +} + +real calculateScaling(string dir, coord[] coords, real size, bool warn=true) +{ + coord[] m=maxcoords(coords,operator >=); + coord[] M=maxcoords(coords,operator <=); + + return calculateScaling(dir, m, M, size, warn); +} diff --git a/Build/source/utils/asymptote/base/plain_shipout.asy b/Build/source/utils/asymptote/base/plain_shipout.asy new file mode 100644 index 00000000000..388a03a557b --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_shipout.asy @@ -0,0 +1,154 @@ +// Default file prefix used for inline LaTeX mode +string defaultfilename; + +file _outpipe; +if(settings.xasy) + _outpipe=output(mode="pipe"); + +string[] file3; + +string outprefix(string prefix=defaultfilename) { + return stripextension(prefix != "" ? prefix : outname()); +} + +string outformat(string format="") +{ + if(format == "") format=settings.outformat; + if(format == "") format=nativeformat(); + return format; +} + +frame currentpatterns; + +frame Portrait(frame f) {return f;}; +frame Landscape(frame f) {return rotate(90)*f;}; +frame UpsideDown(frame f) {return rotate(180)*f;}; +frame Seascape(frame f) {return rotate(-90)*f;}; +typedef frame orientation(frame); +orientation orientation=Portrait; + +// Forward references to functions defined in module three. +object embed3(string, frame, string, string, string, light, projection); +string Embed(string name, string text="", string options="", real width=0, + real height=0); + +bool prconly(string format="") +{ + return outformat(format) == "prc"; +} + +bool prc0(string format="") +{ + return settings.prc && (outformat(format) == "pdf" || prconly() || 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; +} + +void deconstruct(picture pic=currentpicture) +{ + frame f; + transform t=pic.calculateTransform(); + if(currentpicture.fitter == null) + f=pic.fit(t); + else + f=pic.fit(); + deconstruct(f,currentpatterns,t); +} + +bool implicitshipout=false; + +void shipout(string prefix=defaultfilename, frame f, + string format="", bool wait=false, bool view=true, + string options="", string script="", + light light=currentlight, projection P=currentprojection, + transform t=identity) +{ + if(is3D(f)) { + f=enclose(prefix,embed3(prefix,f,format,options,script,light,P)); + if(settings.render != 0 && !prc(format)) { + return; + } + } + + bool defaultprefix=prefix==defaultfilename; + + if(settings.xasy || (!implicitshipout && defaultprefix)) { + if(defaultprefix) { + currentpicture.clear(); + 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,t); +} + +void shipout(string prefix=defaultfilename, picture pic=currentpicture, + orientation orientation=orientation, + string format="", bool wait=false, bool view=true, + string options="", string script="", + light light=currentlight, projection P=currentprojection) +{ + pic.uptodate=true; + if(!uptodate()) { + bool inlinetex=settings.inlinetex; + bool prc=prc(format); + bool empty3=pic.empty3(); + if(prc && !empty3) { + if(settings.render == 0) { + string image=outprefix(prefix)+"+"+(string) file3.length; + if(settings.inlineimage) image += "_0"; + settings.inlinetex=false; + settings.prc=false; + shipout(image,pic,orientation,nativeformat(),view=false,light,P); + settings.prc=true; + } + settings.inlinetex=settings.inlineimage; + } + frame f; + transform t=pic.calculateTransform(); + if(currentpicture.fitter == null) { + pen background=currentlight.background; + if(settings.outformat == "html" && background == nullpen) + background=white; + if(background != nullpen) + f=bbox(pic,nullpen,Fill(background)); + else + f=pic.fit(t); + } + else + f=pic.fit(prefix,format,view=view,options,script,light,P); + + if(!prconly() && (!pic.empty2() || settings.render == 0 || prc || empty3)) + shipout(prefix,orientation(f),format,wait,view,t); + settings.inlinetex=inlinetex; + } +} + +void newpage(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform) { + newpage(f); + },true); +} diff --git a/Build/source/utils/asymptote/base/plain_strings.asy b/Build/source/utils/asymptote/base/plain_strings.asy new file mode 100644 index 00000000000..a5158614931 --- /dev/null +++ b/Build/source/utils/asymptote/base/plain_strings.asy @@ -0,0 +1,258 @@ +string defaultformat(int n, string trailingzero="", bool fixed=false, + bool signed=true) +{ + return "$%"+trailingzero+"."+string(n)+(fixed ? "f" : "g")+"$"; +} + +string defaultformat=defaultformat(4); +string defaultseparator="\!\times\!"; + +string ask(string prompt) +{ + write(stdout,prompt); + return stdin; +} + +string getstring(string name="", string default="", string prompt="", + bool store=true) +{ + string[] history=history(name,1); + if(history.length > 0) default=history[0]; + if(prompt == "") prompt=name+"? [%s] "; + prompt=replace(prompt,new string[][] {{"%s",default}}); + string s=readline(prompt,name); + if(s == "") s=default; + else saveline(name,s,store); + return s; +} + +int getint(string name="", int default=0, string prompt="", bool store=true) +{ + return (int) getstring(name,(string) default,prompt,store); +} + +real getreal(string name="", real default=0, string prompt="", bool store=true) +{ + return (real) getstring(name,(string) default,prompt,store); +} + +pair getpair(string name="", pair default=0, string prompt="", bool store=true) +{ + return (pair) getstring(name,(string) default,prompt,store); +} + +triple gettriple(string name="", triple default=(0,0,0), string prompt="", + bool store=true) +{ + return (triple) getstring(name,(string) default,prompt,store); +} + +// returns a string with all occurrences of string 'before' in string 's' +// changed to string 'after'. +string replace(string s, string before, string after) +{ + return replace(s,new string[][] {{before,after}}); +} + +// Like texify but don't convert embedded TeX commands: \${} +string TeXify(string s) +{ + static string[][] t={{"&","\&"},{"%","\%"},{"_","\_"},{"#","\#"},{"<","$<$"}, + {">","$>$"},{"|","$|$"},{"^","$\hat{\ }$"}, + {"~","$\tilde{\ }$"},{" ","\phantom{ }"}}; + return replace(s,t); +} + +private string[][] trans1={{'\\',"\backslash "}, + {"$","\$"},{"{","\{"},{"}","\}"}}; +private string[][] trans2={{"\backslash ","$\backslash$"}}; + +// Convert string to TeX +string texify(string s) +{ + return TeXify(replace(replace(s,trans1),trans2)); +} + +// Convert string to TeX, preserving newlines +string verbatim(string s) +{ + bool space=substr(s,0,1) == '\n'; + static string[][] t={{'\n',"\\"}}; + t.append(trans1); + s=TeXify(replace(replace(s,t),trans2)); + return space ? "\ "+s : s; +} + +// Split a string into an array of substrings delimited by delimiter +// If delimiter is an empty string, use space delimiter but discard empty +// substrings. TODO: Move to C++ code. +string[] split(string s, string delimiter="") +{ + bool prune=false; + if(delimiter == "") { + prune=true; + delimiter=" "; + } + + string[] S; + int last=0; + int i; + int N=length(delimiter); + int n=length(s); + while((i=find(s,delimiter,last)) >= 0) { + if(i > last || (i == last && !prune)) + S.push(substr(s,last,i-last)); + last=i+N; + } + if(n > last || (n == last && !prune)) + S.push(substr(s,last,n-last)); + return S; +} + +// Returns an array of strings obtained by splitting s into individual +// characters. TODO: Move to C++ code. +string[] array(string s) +{ + int len=length(s); + string[] S=new string[len]; + for(int i=0; i < len; ++i) + S[i]=substr(s,i,1); + return S; +} + +// Concatenate an array of strings into a single string. +// TODO: Move to C++ code. +string operator +(...string[] a) +{ + string S; + for(string s : a) + S += s; + return S; +} + +int system(string s) +{ + return system(split(s)); +} + +int[] operator ecast(string[] a) +{ + return sequence(new int(int i) {return (int) a[i];},a.length); +} + +real[] operator ecast(string[] a) +{ + return sequence(new real(int i) {return (real) a[i];},a.length); +} + +// Read contents of file as a string. +string file(string s) +{ + file f=input(s); + string s; + while(!eof(f)) { + s += f+'\n'; + } + return s; +} + +string italic(string s) +{ + return s != "" ? "{\it "+s+"}" : s; +} + +string baseline(string s, string template="\strut") +{ + return s != "" && settings.tex != "none" ? "\vphantom{"+template+"}"+s : s; +} + +string math(string s) +{ + return s != "" ? "$"+s+"$" : s; +} + +private void notimplemented(string text) +{ + abort(text+" is not implemented for the '"+settings.tex+"' TeX engine"); +} + +string jobname(string name) +{ + int pos=rfind(name,"-"); + return pos >= 0 ? "\ASYprefix\jobname"+substr(name,pos) : name; +} + +string graphic(string name, string options="") +{ + if(latex()) { + if(options != "") options="["+options+"]"; + string includegraphics="\includegraphics"+options; + return includegraphics+"{"+(settings.inlinetex ? jobname(name) : name)+"}"; + } + if(settings.tex != "context") + notimplemented("graphic"); + return "\externalfigure["+name+"]["+options+"]"; +} + +string graphicscale(real x) +{ + return string(settings.tex == "context" ? 1000*x : x); +} + +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 format(string format=defaultformat, bool forcemath=false, real x, + string locale="") +{ + return format(format,forcemath,defaultseparator,x,locale); +} + +string phantom(string s) +{ + return settings.tex != "none" ? "\phantom{"+s+"}" : ""; +} + +string[] spinner=new string[] {'|','/','-','\\'}; +spinner.cyclic=true; + +void progress(bool3 init=default) +{ + static int count=-1; + static int lastseconds=-1; + if(init == true) { + lastseconds=0; + write(stdout,' ',flush); + } else + if(init == default) { + int seconds=seconds(); + if(seconds > lastseconds) { + lastseconds=seconds; + write(stdout,'\b'+spinner[++count],flush); + } + } else + write(stdout,'\b',flush); +} + +restricted int ocgindex=0; diff --git a/Build/source/utils/asymptote/base/pstoedit.asy b/Build/source/utils/asymptote/base/pstoedit.asy new file mode 100644 index 00000000000..7baccd3ed69 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/rational.asy b/Build/source/utils/asymptote/base/rational.asy new file mode 100644 index 00000000000..a120c141232 --- /dev/null +++ b/Build/source/utils/asymptote/base/rational.asy @@ -0,0 +1,275 @@ +// Asymptote module implementing rational arithmetic. + +int gcd(int m, int n) +{ + if(m < n) { + int temp=m; + m=n; + n=temp; + } + while(n != 0) { + int r=m % n; + m=n; + n=r; + } + return m; +} + +int lcm(int m, int n) +{ + return m#gcd(m,n)*n; +} + +struct rational { + int p=0,q=1; + void reduce() { + int d=gcd(p,q); + if(abs(d) > 1) { + p #= d; + q #= d; + } + if(q <= 0) { + if(q == 0) abort("Division by zero"); + p=-p; + q=-q; + } + } + void operator init(int p=0, int q=1, bool reduce=true) { + this.p=p; + this.q=q; + if(reduce) reduce(); + } +} + +rational operator cast(int p) { + return rational(p,false); +} + +rational[] operator cast(int[] a) { + return sequence(new rational(int i) {return a[i];},a.length); +} + +rational[][] operator cast(int[][] a) { + return sequence(new rational[](int i) {return a[i];},a.length); +} + +real operator ecast(rational r) { + return r.p/r.q; +} + +rational operator -(rational r) +{ + return rational(-r.p,r.q,false); +} + +rational operator +(rational r, rational s) +{ + return rational(r.p*s.q+s.p*r.q,r.q*s.q); +} + +rational operator -(rational r, rational s) +{ + return rational(r.p*s.q-s.p*r.q,r.q*s.q); +} + +rational operator *(rational r, rational s) +{ + return rational(r.p*s.p,r.q*s.q); +} + +rational operator /(rational r, rational s) +{ + return rational(r.p*s.q,r.q*s.p); +} + +bool operator ==(rational r, rational s) +{ + return r.p == s.p && r.q == s.q; +} + +bool operator !=(rational r, rational s) +{ + return r.p != s.p || r.q != s.q; +} + +bool operator <(rational r, rational s) +{ + return r.p*s.q-s.p*r.q < 0; +} + +bool operator >(rational r, rational s) +{ + return r.p*s.q-s.p*r.q > 0; +} + +bool operator <=(rational r, rational s) +{ + return r.p*s.q-s.p*r.q <= 0; +} + +bool operator >=(rational r, rational s) +{ + return r.p*s.q-s.p*r.q >= 0; +} + +bool[] operator ==(rational[] r, rational s) +{ + return sequence(new bool(int i) {return r[i] == s;},r.length); +} + +bool operator ==(rational[] r, rational[] s) +{ + if(r.length != s.length) + abort(" operation attempted on arrays of different lengths: "+ + string(r.length)+" != "+string(s.length)); + return all(sequence(new bool(int i) {return r[i] == s[i];},r.length)); +} + +bool operator ==(rational[][] r, rational[][] s) +{ + if(r.length != s.length) + abort(" operation attempted on arrays of different lengths: "+ + string(r.length)+" != "+string(s.length)); + return all(sequence(new bool(int i) {return r[i] == s[i];},r.length)); +} + +bool[] operator <(rational[] r, rational s) +{ + return sequence(new bool(int i) {return r[i] < s;},r.length); +} + +bool[] operator >(rational[] r, rational s) +{ + return sequence(new bool(int i) {return r[i] > s;},r.length); +} + +bool[] operator <=(rational[] r, rational s) +{ + return sequence(new bool(int i) {return r[i] <= s;},r.length); +} + +bool[] operator >=(rational[] r, rational s) +{ + return sequence(new bool(int i) {return r[i] >= s;},r.length); +} + +rational min(rational a, rational b) +{ + return a <= b ? a : b; +} + +rational max(rational a, rational b) +{ + return a >= b ? a : b; +} + +string string(rational r) +{ + return r.q == 1 ? string(r.p) : string(r.p)+"/"+string(r.q); +} + +string texstring(rational r) +{ + if(r.q == 1) return string(r.p); + string s; + if(r.p < 0) s="-"; + return s+"\frac{"+string(abs(r.p))+"}{"+string(r.q)+"}"; +} + + +void write(file fout, string s="", rational r, suffix suffix=none) +{ + write(fout,s+string(r),suffix); +} + +void write(string s="", rational r, suffix suffix=endl) +{ + write(stdout,s,r,suffix); +} + +void write(file fout=stdout, string s="", rational[] a, suffix suffix=none) +{ + if(s != "") + write(fout,s,endl); + for(int i=0; i < a.length; ++i) { + write(fout,i,none); + write(fout,':\t',a[i],endl); + } + write(fout,suffix); +} + +void write(file fout=stdout, string s="", rational[][] a, suffix suffix=none) +{ + if(s != "") + write(fout,s); + for(int i=0; i < a.length; ++i) { + rational[] ai=a[i]; + for(int j=0; j < ai.length; ++j) { + write(fout,ai[j],tab); + } + write(fout,endl); + } + write(fout,suffix); +} + +bool rectangular(rational[][] 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; +} + +rational sum(rational[] a) +{ + rational sum; + for(rational r:a) + sum += r; + return sum; +} + +rational max(rational[] a) +{ + rational M=a[0]; + for(rational r:a) + M=max(M,r); + return M; +} + +rational abs(rational r) +{ + return rational(abs(r.p),r.q,false); +} + +rational[] operator -(rational[] r) +{ + return sequence(new rational(int i) {return -r[i];},r.length); +} + +rational[][] rationalidentity(int n) +{ + return sequence(new rational[](int i) {return sequence(new rational(int j) {return j == i ? 1 : 0;},n);},n); +} + +/* +rational r=rational(1,3)+rational(1,4); +write(r == rational(1,12)); +write(r); +real x=r; +write(x); + +rational r=3; +write(r); + +write(gcd(-8,12)); +write(rational(-36,-14)); + +int[][] a=new int[][] {{1,2},{3,4}}; +rational[][] r=a; +write(r); + +*/ + diff --git a/Build/source/utils/asymptote/base/rationalSimplex.asy b/Build/source/utils/asymptote/base/rationalSimplex.asy new file mode 100644 index 00000000000..3a376ba2ddb --- /dev/null +++ b/Build/source/utils/asymptote/base/rationalSimplex.asy @@ -0,0 +1,423 @@ +// Rational simplex solver written by John C. Bowman and Pouria Ramazi, 2018. +import rational; + +void simplexInit(rational[] c, rational[][] A, int[] s=new int[], + rational[] b, int count) {} +void simplexTableau(rational[][] E, int[] Bindices, int I=-1, int J=-1) {} +void simplexPhase1(rational[] c, rational[][] A, rational[] b, + int[] Bindices) {} +void simplexPhase2() {} + +void simplexWrite(rational[][] E, int[] Bindices, int, int) +{ + int m=E.length-1; + int n=E[0].length-1; + + write(E[m][0],tab); + for(int j=1; j <= n; ++j) + write(E[m][j],tab); + write(); + + for(int i=0; i < m; ++i) { + write(E[i][0],tab); + for(int j=1; j <= n; ++j) { + write(E[i][j],tab); + } + write(); + } + write(); +}; + +struct simplex { + static int OPTIMAL=0; + static int UNBOUNDED=1; + static int INFEASIBLE=2; + + int case; + rational[] x; + rational[] xStandard; + rational cost; + rational[] d; + bool dual=false; + + int m,n; + int J; + + // Row reduce based on pivot E[I][J] + void rowreduce(rational[][] E, int N, int I, int J) { + rational[] EI=E[I]; + rational v=EI[J]; + for(int j=0; j < J; ++j) EI[j] /= v; + EI[J]=1; + for(int j=J+1; j <= N; ++j) EI[j] /= v; + + for(int i=0; i < I; ++i) { + rational[] Ei=E[i]; + rational EiJ=Ei[J]; + for(int j=0; j < J; ++j) + Ei[j] -= EI[j]*EiJ; + Ei[J]=0; + for(int j=J+1; j <= N; ++j) + Ei[j] -= EI[j]*EiJ; + } + for(int i=I+1; i <= m; ++i) { + rational[] Ei=E[i]; + rational EiJ=Ei[J]; + for(int j=0; j < J; ++j) + Ei[j] -= EI[j]*EiJ; + Ei[J]=0; + for(int j=J+1; j <= N; ++j) + Ei[j] -= EI[j]*EiJ; + } + } + + int iterate(rational[][] E, int N, int[] Bindices) { + while(true) { + // Bland's rule: first negative entry in reduced cost (bottom) row enters + rational[] Em=E[m]; + for(J=1; J <= N; ++J) + if(Em[J] < 0) break; + + if(J > N) + break; + + int I=-1; + rational t; + for(int i=0; i < m; ++i) { + rational u=E[i][J]; + if(u > 0) { + t=E[i][0]/u; + I=i; + break; + } + } + for(int i=I+1; i < m; ++i) { + rational u=E[i][J]; + if(u > 0) { + rational r=E[i][0]/u; + if(r <= t && (r < t || Bindices[i] < Bindices[I])) { + t=r; I=i; + } // Bland's rule: exiting variable has smallest minimizing subscript + } + } + if(I == -1) + return UNBOUNDED; // Can only happen in Phase 2. + + simplexTableau(E,Bindices,I,J); + + // Generate new tableau + Bindices[I]=J; + rowreduce(E,N,I,J); + } + return OPTIMAL; + } + + int iterateDual(rational[][] E, int N, int[] Bindices) { + while(true) { + // Bland's rule: negative variable with smallest subscript exits + int I; + for(I=0; I < m; ++I) { + if(E[I][0] < 0) break; + } + + if(I == m) + break; + + for(int i=I+1; i < m; ++i) { + if(E[i][0] < 0 && Bindices[i] < Bindices[I]) + I=i; + } + + rational[] Em=E[m]; + rational[] EI=E[I]; + int J=0; + rational t; + for(int j=1; j <= N; ++j) { + rational u=EI[j]; + if(u < 0) { + t=-Em[j]/u; + J=j; + break; + } + } + for(int j=J+1; j <= N; ++j) { + rational u=EI[j]; + if(u < 0) { + rational r=-Em[j]/u; + if(r <= t && (r < t || j < J)) { + t=r; J=j; + } // Bland's rule: smallest minimizing subscript enters + } + } + if(J == 0) + return INFEASIBLE; // Can only happen in Phase 2. + + simplexTableau(E,Bindices,I,J); + + // Generate new tableau + Bindices[I]=J; + rowreduce(E,N,I,J); + } + return OPTIMAL; + } + + // Try to find a solution x to Ax=b that minimizes the cost c^T x, + // where A is an m x n matrix, x is a vector of n non-negative numbers, + // b is a vector of length m, and c is a vector of length n. + // Can set phase1=false if the last m columns of A form the identity matrix. + void operator init(rational[] c, rational[][] A, rational[] b, + bool phase1=true) { + // Phase 1 + m=A.length; + if(m == 0) {case=INFEASIBLE; return;} + n=A[0].length; + if(n == 0) {case=INFEASIBLE; return;} + + rational[][] E=new rational[m+1][n+1]; + rational[] Em=E[m]; + + for(int j=1; j <= n; ++j) + Em[j]=0; + + for(int i=0; i < m; ++i) { + rational[] Ai=A[i]; + rational[] Ei=E[i]; + if(b[i] >= 0 || dual) { + for(int j=1; j <= n; ++j) { + rational Aij=Ai[j-1]; + Ei[j]=Aij; + Em[j] -= Aij; + } + } else { + for(int j=1; j <= n; ++j) { + rational Aij=-Ai[j-1]; + Ei[j]=Aij; + Em[j] -= Aij; + } + } + } + + void basicValues() { + rational sum=0; + for(int i=0; i < m; ++i) { + rational B=dual ? b[i] : abs(b[i]); + E[i][0]=B; + sum -= B; + } + Em[0]=sum; + } + + int[] Bindices; + + if(phase1) { + Bindices=new int[m]; + int p=0; + + // Check for redundant basis vectors. + bool checkBasis(int j) { + for(int i=0; i < m; ++i) { + rational[] Ei=E[i]; + if(i != p ? Ei[j] != 0 : Ei[j] <= 0) return false; + } + return true; + } + + int checkTableau() { + for(int j=1; j <= n; ++j) + if(checkBasis(j)) return j; + return 0; + } + + int k=0; + while(p < m) { + int j=checkTableau(); + if(j > 0) + Bindices[p]=j; + else { // Add an artificial variable + Bindices[p]=n+1+k; + for(int i=0; i < p; ++i) + E[i].push(0); + E[p].push(1); + for(int i=p+1; i < m; ++i) + E[i].push(0); + E[m].push(0); + ++k; + } + ++p; + } + + basicValues(); + + simplexPhase1(c,A,b,Bindices); + + iterate(E,n+k,Bindices); + + if(Em[0] != 0) { + simplexTableau(E,Bindices); + case=INFEASIBLE; + return; + } + } else { + Bindices=sequence(new int(int x){return x;},m)+n-m+1; + basicValues(); + } + + rational[] cB=phase1 ? new rational[m] : c[n-m:n]; + rational[][] D=phase1 ? new rational[m+1][n+1] : E; + if(phase1) { + bool output=true; + // Drive artificial variables out of basis. + for(int i=0; i < m; ++i) { + int k=Bindices[i]; + if(k > n) { + rational[] Ei=E[i]; + int j; + for(j=1; j <= n; ++j) + if(Ei[j] != 0) break; + if(j > n) continue; + output=false; + simplexTableau(E,Bindices,i,j); + Bindices[i]=j; + rowreduce(E,n,i,j); + } + } + if(output) simplexTableau(E,Bindices); + int ip=0; // reduced i + for(int i=0; i < m; ++i) { + int k=Bindices[i]; + if(k > n) continue; + Bindices[ip]=k; + cB[ip]=c[k-1]; + rational[] Dip=D[ip]; + rational[] Ei=E[i]; + for(int j=1; j <= n; ++j) + Dip[j]=Ei[j]; + Dip[0]=Ei[0]; + ++ip; + } + + rational[] Dip=D[ip]; + rational[] Em=E[m]; + for(int j=1; j <= n; ++j) + Dip[j]=Em[j]; + Dip[0]=Em[0]; + + if(m > ip) { + Bindices.delete(ip,m-1); + D.delete(ip,m-1); + m=ip; + } + if(!output) simplexTableau(D,Bindices); + } + + rational[] Dm=D[m]; + for(int j=1; j <= n; ++j) { + rational sum=0; + for(int k=0; k < m; ++k) + sum += cB[k]*D[k][j]; + Dm[j]=c[j-1]-sum; + } + + rational sum=0; + for(int k=0; k < m; ++k) + sum += cB[k]*D[k][0]; + Dm[0]=-sum; + + simplexPhase2(); + + case=(dual ? iterateDual : iterate)(D,n,Bindices); + simplexTableau(D,Bindices); + + if(case != INFEASIBLE) { + x=new rational[n]; + for(int j=0; j < n; ++j) + x[j]=0; + + for(int k=0; k < m; ++k) + x[Bindices[k]-1]=D[k][0]; + } + + if(case == UNBOUNDED) { + d=new rational[n]; + for(int j=0; j < n; ++j) + d[j]=0; + d[J-1]=1; + for(int k=0; k < m; ++k) + d[Bindices[k]-1]=-D[k][J]; + } + + if(case != OPTIMAL) + return; + + cost=-Dm[0]; + } + + // Try to find a solution x to sgn(Ax-b)=sgn(s) that minimizes the cost + // c^T x, where A is an m x n matrix, x is a vector of n non-negative + // numbers, b is a vector of length m, and c is a vector of length n. + void operator init(rational[] c, rational[][] A, int[] s, rational[] b) { + int m=A.length; + if(m == 0) {case=INFEASIBLE; return;} + int n=A[0].length; + if(n == 0) {case=INFEASIBLE; return;} + + int count=0; + for(int i=0; i < m; ++i) + if(s[i] != 0) ++count; + + rational[][] a=new rational[m][n+count]; + + for(int i=0; i < m; ++i) { + rational[] ai=a[i]; + rational[] Ai=A[i]; + for(int j=0; j < n; ++j) { + ai[j]=Ai[j]; + } + } + + int k=0; + + bool phase1=false; + dual=count == m && all(c >= 0); + + for(int i=0; i < m; ++i) { + rational[] ai=a[i]; + for(int j=0; j < k; ++j) + ai[n+j]=0; + int si=s[i]; + if(k < count) + ai[n+k]=-si; + for(int j=k+1; j < count; ++j) + ai[n+j]=0; + if(si == 0) phase1=true; + else { + ++k; + rational bi=b[i]; + if(bi == 0) { + if(si == 1) { + s[i]=-1; + for(int j=0; j < n+count; ++j) + ai[j]=-ai[j]; + } + } else if(dual && si == 1) { + b[i]=-bi; + s[i]=-1; + for(int j=0; j < n+count; ++j) + ai[j]=-ai[j]; + } else if(si*bi > 0) + phase1=true; + } + } + + if(dual) phase1=false; + rational[] C=concat(c,array(count,rational(0))); + simplexInit(C,a,b,count); + operator init(C,a,b,phase1); + + if(case != INFEASIBLE) { + xStandard=copy(x); + if(count > 0) + x.delete(n,n+count-1); + } + } +} diff --git a/Build/source/utils/asymptote/base/reload.js b/Build/source/utils/asymptote/base/reload.js new file mode 100644 index 00000000000..a6526803056 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/res/notes.txt b/Build/source/utils/asymptote/base/res/notes.txt new file mode 100644 index 00000000000..6b845735105 --- /dev/null +++ b/Build/source/utils/asymptote/base/res/notes.txt @@ -0,0 +1,7 @@ +For now, I decide not to commit in the *.hdr reflectance image files +because of the size and that they are binary format. Meanwhile, the +images I use (temporarily) can be found at: +- <https://www.deviantart.com/zbyg/art/HDRi-Pack-1-97402522> +- <https://hdrihaven.com/> + +-- Supakorn "Jamie"
\ No newline at end of file diff --git a/Build/source/utils/asymptote/base/roundedpath.asy b/Build/source/utils/asymptote/base/roundedpath.asy new file mode 100644 index 00000000000..1a426f91dcb --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/shaders/fragment.glsl b/Build/source/utils/asymptote/base/shaders/fragment.glsl new file mode 100644 index 00000000000..057acbc744f --- /dev/null +++ b/Build/source/utils/asymptote/base/shaders/fragment.glsl @@ -0,0 +1,227 @@ +struct Material +{ + vec4 diffuse,emissive,specular; + vec4 parameters; +}; + +struct Light +{ + vec3 direction; + vec3 color; +}; + +uniform int nlights; +uniform Light lights[max(Nlights,1)]; + +uniform MaterialBuffer { + Material Materials[Nmaterials]; +}; + +#ifdef NORMAL +#ifndef ORTHOGRAPHIC +in vec3 ViewPosition; +#endif +in vec3 Normal; +vec3 normal; +#endif + +#ifdef COLOR +in vec4 Color; +#endif + +flat in int materialIndex; +out vec4 outColor; + +// PBR material parameters +vec3 Diffuse; // Diffuse for nonmetals, reflectance for metals. +vec3 Specular; // Specular tint for nonmetals +float Metallic; // Metallic/Nonmetals parameter +float Fresnel0; // Fresnel at zero for nonmetals +float Roughness2; // roughness squared, for smoothing + +#ifdef ENABLE_TEXTURE +uniform sampler2D environmentMap; +const float PI=acos(-1.0); +const float twopi=2*PI; +const float halfpi=PI/2; + +const int numSamples=7; + +// (x,y,z) -> (r,theta,phi); +// theta -> [0,\pi]: colatitude +// phi -> [0, 2\pi]: longitude +vec3 cart2sphere(vec3 cart) +{ + float x=cart.z; + float y=cart.x; + float z=cart.y; + + float r=length(cart); + float phi=atan(y,x); + float theta=acos(z/r); + + return vec3(r,phi,theta); +} + +vec2 normalizedAngle(vec3 cartVec) +{ + vec3 sphericalVec=cart2sphere(cartVec); + sphericalVec.y=sphericalVec.y/(2*PI)-0.25; + sphericalVec.z=sphericalVec.z/PI; + return sphericalVec.yz; +} +#endif + +#ifdef NORMAL +// h is the halfway vector between normal and light direction +// GGX Trowbridge-Reitz Approximation +float NDF_TRG(vec3 h) +{ + float ndoth=max(dot(normal,h),0.0); + float alpha2=Roughness2*Roughness2; + float denom=ndoth*ndoth*(alpha2-1.0)+1.0; + return denom != 0.0 ? alpha2/(denom*denom) : 0.0; +} + +float GGX_Geom(vec3 v) +{ + float ndotv=max(dot(v,normal),0.0); + float ap=1.0+Roughness2; + float k=0.125*ap*ap; + return ndotv/((ndotv*(1.0-k))+k); +} + +float Geom(vec3 v, vec3 l) +{ + return GGX_Geom(v)*GGX_Geom(l); +} + +// Schlick's approximation +float Fresnel(vec3 h, vec3 v, float fresnel0) +{ + float a=1.0-max(dot(h,v),0.0); + float b=a*a; + return fresnel0+(1.0-fresnel0)*b*b*a; +} + +vec3 BRDF(vec3 viewDirection, vec3 lightDirection) +{ + vec3 lambertian=Diffuse; + // Cook-Torrance model + vec3 h=normalize(lightDirection+viewDirection); + + float omegain=max(dot(viewDirection,normal),0.0); + float omegaln=max(dot(lightDirection,normal),0.0); + + float D=NDF_TRG(h); + float G=Geom(viewDirection,lightDirection); + float F=Fresnel(h,viewDirection,Fresnel0); + + float denom=4.0*omegain*omegaln; + float rawReflectance=denom > 0.0 ? (D*G)/denom : 0.0; + + vec3 dielectric=mix(lambertian,rawReflectance*Specular,F); + vec3 metal=rawReflectance*Diffuse; + + return mix(dielectric,metal,Metallic); +} +#endif + +void main() +{ + vec4 diffuse; + vec4 emissive; + + Material m; +#ifdef TRANSPARENT + m=Materials[abs(materialIndex)-1]; + emissive=m.emissive; + if(materialIndex >= 0) + diffuse=m.diffuse; + else { + diffuse=Color; +#if Nlights == 0 + emissive += Color; +#endif + } +#else + m=Materials[int(materialIndex)]; + emissive=m.emissive; +#ifdef COLOR + diffuse=Color; +#if Nlights == 0 + emissive += Color; +#endif +#else + diffuse=m.diffuse; +#endif +#endif + +#if defined(NORMAL) && Nlights > 0 + Specular=m.specular.rgb; + vec4 parameters=m.parameters; + Roughness2=1.0-parameters[0]; + Roughness2=Roughness2*Roughness2; + Metallic=parameters[1]; + Fresnel0=parameters[2]; + Diffuse=diffuse.rgb; + + // Given a point x and direction \omega, + // L_i=\int_{\Omega}f(x,\omega_i,\omega) L(x,\omega_i)(\hat{n}\cdot \omega_i) + // d\omega_i, where \Omega is the hemisphere covering a point, + // f is the BRDF function, L is the radiance from a given angle and position. + + normal=normalize(Normal); + normal=gl_FrontFacing ? normal : -normal; +#ifdef ORTHOGRAPHIC + vec3 viewDir=vec3(0.0,0.0,1.0); +#else + vec3 viewDir=-normalize(ViewPosition); +#endif + // For a finite point light, the rendering equation simplifies. + vec3 color=emissive.rgb; + for(int i=0; i < nlights; ++i) { + Light Li=lights[i]; + vec3 L=Li.direction; + float cosTheta=max(dot(normal,L),0.0); // $\omega_i \cdot n$ term + vec3 radiance=cosTheta*Li.color; + color += BRDF(viewDir,L)*radiance; + } + +#if defined(ENABLE_TEXTURE) && !defined(COLOR) + // Experimental environment radiance using Riemann sums; + // can also do importance sampling. + vec3 envRadiance=vec3(0.0,0.0,0.0); + + vec3 normalPerp=vec3(-normal.y,normal.x,0.0); + if(length(normalPerp) == 0.0) + normalPerp=vec3(1.0,0.0,0.0); + + // we now have a normal basis; + normalPerp=normalize(normalPerp); + vec3 normalPerp2=normalize(cross(normal,normalPerp)); + + const float step=1.0/numSamples; + const float phistep=twopi*step; + const float thetastep=halfpi*step; + for (int iphi=0; iphi < numSamples; ++iphi) { + float phi=iphi*phistep; + for (int itheta=0; itheta < numSamples; ++itheta) { + float theta=itheta*thetastep; + + vec3 azimuth=cos(phi)*normalPerp+sin(phi)*normalPerp2; + vec3 L=sin(theta)*azimuth+cos(theta)*normal; + + vec3 rawRadiance=texture(environmentMap,normalizedAngle(L)).rgb; + vec3 surfRefl=BRDF(Z,L); + envRadiance += surfRefl*rawRadiance*sin(2.0*theta); + } + } + envRadiance *= halfpi*step*step; + color += envRadiance.rgb; +#endif + outColor=vec4(color,diffuse.a); +#else + outColor=emissive; +#endif +} diff --git a/Build/source/utils/asymptote/base/shaders/vertex.glsl b/Build/source/utils/asymptote/base/shaders/vertex.glsl new file mode 100644 index 00000000000..4b2a54b9897 --- /dev/null +++ b/Build/source/utils/asymptote/base/shaders/vertex.glsl @@ -0,0 +1,49 @@ +in vec3 position; + +uniform mat3 normMat; + +#ifdef NORMAL +#ifndef ORTHOGRAPHIC +out vec3 ViewPosition; +#endif +in vec3 normal; +out vec3 Normal; +#endif + +in int material; + +#ifdef COLOR +in vec4 color; +out vec4 Color; +#endif + +#ifdef WIDTH +in float width; +#endif + +uniform mat4 projViewMat; +uniform mat4 viewMat; + +flat out int materialIndex; + +void main() +{ + vec4 v=vec4(position,1.0); + gl_Position=projViewMat*v; +#ifdef NORMAL +#ifndef ORTHOGRAPHIC + ViewPosition=(viewMat*v).xyz; +#endif + Normal=normalize(normal*normMat); +#endif + +#ifdef COLOR + Color=color; +#endif + +#ifdef WIDTH + gl_PointSize=width; +#endif + + materialIndex=material; +} diff --git a/Build/source/utils/asymptote/base/simplex.asy b/Build/source/utils/asymptote/base/simplex.asy new file mode 100644 index 00000000000..e6d0410b23c --- /dev/null +++ b/Build/source/utils/asymptote/base/simplex.asy @@ -0,0 +1,363 @@ +// Real simplex solver written by John C. Bowman and Pouria Ramazi, 2018. + +struct simplex { + static int OPTIMAL=0; + static int UNBOUNDED=1; + static int INFEASIBLE=2; + + int case; + real[] x; + real cost; + bool dual=false; + + int m,n; + int J; + real EpsilonA; + + // Row reduce based on pivot E[I][J] + void rowreduce(real[][] E, int N, int I, int J) { + real[] EI=E[I]; + real v=EI[J]; + for(int j=0; j < J; ++j) EI[j] /= v; + EI[J]=1.0; + for(int j=J+1; j <= N; ++j) EI[j] /= v; + + for(int i=0; i < I; ++i) { + real[] Ei=E[i]; + real EiJ=Ei[J]; + for(int j=0; j < J; ++j) + Ei[j] -= EI[j]*EiJ; + Ei[J]=0.0; + for(int j=J+1; j <= N; ++j) + Ei[j] -= EI[j]*EiJ; + } + for(int i=I+1; i <= m; ++i) { + real[] Ei=E[i]; + real EiJ=Ei[J]; + for(int j=0; j < J; ++j) + Ei[j] -= EI[j]*EiJ; + Ei[J]=0.0; + for(int j=J+1; j <= N; ++j) + Ei[j] -= EI[j]*EiJ; + } + } + + int iterate(real[][] E, int N, int[] Bindices) { + while(true) { + // Bland's rule: first negative entry in reduced cost (bottom) row enters + real[] Em=E[m]; + for(J=1; J <= N; ++J) + if(Em[J] < 0) break; + + if(J > N) + break; + + int I=-1; + real t; + for(int i=0; i < m; ++i) { + real u=E[i][J]; + if(u > EpsilonA) { + t=E[i][0]/u; + I=i; + break; + } + } + for(int i=I+1; i < m; ++i) { + real u=E[i][J]; + if(u > EpsilonA) { + real r=E[i][0]/u; + if(r <= t && (r < t || Bindices[i] < Bindices[I])) { + t=r; I=i; + } // Bland's rule: exiting variable has smallest minimizing subscript + } + } + if(I == -1) + return UNBOUNDED; // Can only happen in Phase 2. + + // Generate new tableau + Bindices[I]=J; + rowreduce(E,N,I,J); + } + return OPTIMAL; + } + + int iterateDual(real[][] E, int N, int[] Bindices) { + while(true) { + // Bland's rule: negative variable with smallest subscript exits + int I; + for(I=0; I < m; ++I) { + if(E[I][0] < 0) break; + } + + if(I == m) + break; + + for(int i=I+1; i < m; ++i) { + if(E[i][0] < 0 && Bindices[i] < Bindices[I]) + I=i; + } + + real[] Em=E[m]; + real[] EI=E[I]; + int J=0; + real t; + for(int j=1; j <= N; ++j) { + real u=EI[j]; + if(u < -EpsilonA) { + t=-Em[j]/u; + J=j; + break; + } + } + for(int j=J+1; j <= N; ++j) { + real u=EI[j]; + if(u < -EpsilonA) { + real r=-Em[j]/u; + if(r < t) { + t=r; J=j; + } // Bland's rule: smallest minimizing subscript enters + } + } + if(J == 0) + return INFEASIBLE; // Can only happen in Phase 2. + + // Generate new tableau + Bindices[I]=J; + rowreduce(E,N,I,J); + } + return OPTIMAL; + } + + // Try to find a solution x to Ax=b that minimizes the cost c^T x, + // where A is an m x n matrix, x is a vector of n non-negative numbers, + // b is a vector of length m, and c is a vector of length n. + // Can set phase1=false if the last m columns of A form the identity matrix. + void operator init(real[] c, real[][] A, real[] b, bool phase1=true) { + static real epsilon=sqrt(realEpsilon); + real normA=norm(A); + real epsilonA=100.0*realEpsilon*normA; + EpsilonA=epsilon*normA; + + // Phase 1 + m=A.length; + if(m == 0) {case=INFEASIBLE; return;} + n=A[0].length; + if(n == 0) {case=INFEASIBLE; return;} + + real[][] E=new real[m+1][n+1]; + real[] Em=E[m]; + + for(int j=1; j <= n; ++j) + Em[j]=0; + + for(int i=0; i < m; ++i) { + real[] Ai=A[i]; + real[] Ei=E[i]; + if(b[i] >= 0 || dual) { + for(int j=1; j <= n; ++j) { + real Aij=Ai[j-1]; + Ei[j]=Aij; + Em[j] -= Aij; + } + } else { + for(int j=1; j <= n; ++j) { + real Aij=-Ai[j-1]; + Ei[j]=Aij; + Em[j] -= Aij; + } + } + } + + void basicValues() { + real sum=0; + for(int i=0; i < m; ++i) { + real B=dual ? b[i] : abs(b[i]); + E[i][0]=B; + sum -= B; + } + Em[0]=sum; + } + + int[] Bindices; + + if(phase1) { + Bindices=new int[m]; + int p=0; + + // Check for redundant basis vectors. + bool checkBasis(int j) { + for(int i=0; i < m; ++i) { + real[] Ei=E[i]; + if(i != p ? abs(Ei[j]) >= epsilonA : Ei[j] <= epsilonA) return false; + } + return true; + } + + int checkTableau() { + for(int j=1; j <= n; ++j) + if(checkBasis(j)) return j; + return 0; + } + + int k=0; + while(p < m) { + int j=checkTableau(); + if(j > 0) + Bindices[p]=j; + else { // Add an artificial variable + Bindices[p]=n+1+k; + for(int i=0; i < p; ++i) + E[i].push(0.0); + E[p].push(1.0); + for(int i=p+1; i < m; ++i) + E[i].push(0.0); + E[m].push(0.0); + ++k; + } + ++p; + } + + basicValues(); + iterate(E,n+k,Bindices); + + if(abs(Em[0]) > EpsilonA) { + case=INFEASIBLE; + return; + } + } else { + Bindices=sequence(new int(int x){return x;},m)+n-m+1; + basicValues(); + } + + real[] cB=phase1 ? new real[m] : c[n-m:n]; + real[][] D=phase1 ? new real[m+1][n+1] : E; + if(phase1) { + // Drive artificial variables out of basis. + for(int i=0; i < m; ++i) { + int k=Bindices[i]; + if(k > n) { + real[] Ei=E[i]; + int j; + for(j=1; j <= n; ++j) + if(abs(Ei[j]) > EpsilonA) break; + if(j > n) continue; + Bindices[i]=j; + rowreduce(E,n,i,j); + } + } + int ip=0; // reduced i + for(int i=0; i < m; ++i) { + int k=Bindices[i]; + if(k > n) continue; + Bindices[ip]=k; + cB[ip]=c[k-1]; + real[] Dip=D[ip]; + real[] Ei=E[i]; + for(int j=1; j <= n; ++j) + Dip[j]=Ei[j]; + Dip[0]=Ei[0]; + ++ip; + } + + real[] Dip=D[ip]; + real[] Em=E[m]; + for(int j=1; j <= n; ++j) + Dip[j]=Em[j]; + Dip[0]=Em[0]; + + if(m > ip) { + Bindices.delete(ip,m-1); + D.delete(ip,m-1); + m=ip; + } + } + + real[] Dm=D[m]; + for(int j=1; j <= n; ++j) { + real sum=0; + for(int k=0; k < m; ++k) + sum += cB[k]*D[k][j]; + Dm[j]=c[j-1]-sum; + } + + real sum=0; + for(int k=0; k < m; ++k) + sum += cB[k]*D[k][0]; + Dm[0]=-sum; + + case=(dual ? iterateDual : iterate)(D,n,Bindices); + if(case != OPTIMAL) + return; + + for(int j=0; j < n; ++j) + x[j]=0; + + for(int k=0; k < m; ++k) + x[Bindices[k]-1]=D[k][0]; + cost=-Dm[0]; + } + + // Try to find a solution x to sgn(Ax-b)=sgn(s) that minimizes the cost + // c^T x, where A is an m x n matrix, x is a vector of n non-negative + // numbers, b is a vector of length m, and c is a vector of length n. + void operator init(real[] c, real[][] A, int[] s, real[] b) { + int m=A.length; + if(m == 0) {case=INFEASIBLE; return;} + int n=A[0].length; + if(n == 0) {case=INFEASIBLE; return;} + + int count=0; + for(int i=0; i < m; ++i) + if(s[i] != 0) ++count; + + real[][] a=new real[m][n+count]; + + for(int i=0; i < m; ++i) { + real[] ai=a[i]; + real[] Ai=A[i]; + for(int j=0; j < n; ++j) { + ai[j]=Ai[j]; + } + } + + int k=0; + + bool phase1=false; + bool dual=count == m && all(c >= 0); + + for(int i=0; i < m; ++i) { + real[] ai=a[i]; + for(int j=0; j < k; ++j) + ai[n+j]=0; + int si=s[i]; + if(k < count) + ai[n+k]=-si; + for(int j=k+1; j < count; ++j) + ai[n+j]=0; + if(si == 0) phase1=true; + else { + ++k; + real bi=b[i]; + if(bi == 0) { + if(si == 1) { + s[i]=-1; + for(int j=0; j < n+count; ++j) + ai[j]=-ai[j]; + } + } else if(dual && si == 1) { + b[i]=-bi; + s[i]=-1; + for(int j=0; j < n+count; ++j) + ai[j]=-ai[j]; + } else if(si*bi > 0) + phase1=true; + } + } + + if(dual) phase1=false; + operator init(concat(c,array(count,0.0)),a,b,phase1); + + if(case == OPTIMAL && count > 0) + x.delete(n,n+count-1); + } +} diff --git a/Build/source/utils/asymptote/base/size10.asy b/Build/source/utils/asymptote/base/size10.asy new file mode 100644 index 00000000000..3bbd31227a9 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/size11.asy b/Build/source/utils/asymptote/base/size11.asy new file mode 100644 index 00000000000..93712288152 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/slide.asy b/Build/source/utils/asymptote/base/slide.asy new file mode 100644 index 00000000000..9d26c680652 --- /dev/null +++ b/Build/source/utils/asymptote/base/slide.asy @@ -0,0 +1,620 @@ +import fontsize; +usepackage("asycolors"); + +bool reverse=false; // Set to true to enable reverse video. +bool stepping=false; // Set to true to enable stepping. +bool itemstep=true; // Set to false to disable stepping on each item. + +settings.toolbar=false; // Disable 3D toolbar by default. +if(settings.render < 0) settings.render=4; + +bool allowstepping=false; // Allow stepping for current slide. + +real pagemargin=0.5cm; +real pagewidth=-2pagemargin; +real pageheight=-2pagemargin; + +bool landscape=orientation == Landscape || orientation == Seascape; + +if(landscape) { + orientation=Portrait; + pagewidth += settings.paperheight; + pageheight += settings.paperwidth; +} else { + pagewidth += settings.paperwidth; + pageheight += settings.paperheight; +} + +size(pagewidth,pageheight,IgnoreAspect); +picture background; + +real minipagemargin=1inch; +real minipagewidth=pagewidth-2minipagemargin; + +transform tinv=inverse(fixedscaling((-1,-1),(1,1),currentpen)); + +pen itempen=fontsize(24pt); +pen codepen=fontsize(20pt); +pen titlepagepen=fontsize(36pt); +pen authorpen=fontsize(24pt); +pen institutionpen=authorpen; +pen datepen=fontsize(18pt); +pen urlpen=datepen; + +real itemskip=0.5; +real codeskip=0.25; +real aboveequationskip=-1.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"); +texpreamble("\usepackage{amsmath}"); + +// Evaluate user command line option. +void usersetting() +{ + plain.usersetting(); + if(reverse) { // Black background + reversevideo(); + } else { // White background + normalvideo(); + } +} + +void numberpage(pen p=pagenumberpen) +{ + if(havepagenumber) { + label((string) page,pagenumberposition,pagenumberalign,p); + } +} + +void nextpage(pen p=pagenumberpen) +{ + if(!empty()) { + numberpage(p); + newpage(); + } + background(); + firststep=true; +} + +void newslide(bool stepping=true) +{ + allowstepping=stepping; + nextpage(); + ++page; + havepagenumber=true; + currentposition=startposition; + firstnode=new int[] {currentpicture.nodes.length}; + lastnode.delete(); +} + +bool checkposition() +{ + if(abs(currentposition.x) > 1 || abs(currentposition.y) > 1) { + newslide(); + return false; + } + return true; +} + +void erasestep(int erasenode) { + if(!stepping || !allowstepping) return; + if(!checkposition()) return; + lastnode.push(erasenode); + nextpage(steppagenumberpen); + for(int i=0; i < firstnode.length; ++i) { + for(int j=firstnode[i]; j <= lastnode[i]; ++j) { + tex(bulletcolor(oldbulletcolor)); + currentpicture.add(currentpicture.nodes[j].d); + } + } + firstnode.push(currentpicture.nodes.length-1); + tex(bulletcolor(newbulletcolor)); +} + +void step() +{ + // Step without erasing anything. + erasestep(currentpicture.nodes.length-1); +} + +void incrementposition(pair z) +{ + currentposition += z; +} + +void title(string s, pair position=N, pair align=titlealign, + pen p=titlepen, bool newslide=true) +{ + if(newslide) newslide(); + checkposition(); + frame f; + if(s != "") label(f,minipage("\center "+s,minipagewidth),(0,0),align,p); + add(f,position,labelmargin(p)*align); + currentposition=(currentposition.x,position.y+ + (tinv*(min(f)-titleskip*I*lineskip(p)*pt)).y); +} + +void outline(string s="Outline", pair position=N, pair align=titlealign, + pen p=titlepen) +{ + newslide(stepping=false); + title(s,position,align,p,newslide=false); +} + +void remark(bool center=false, string s, pair align=0, pen p=itempen, + real indent=0, bool minipage=true, real skip=itemskip, + filltype filltype=NoFill, bool step=false) +{ + checkposition(); + if(minipage) s=minipage(s,minipagewidth); + + pair offset; + if(center) { + if(align == 0) align=S; + offset=(0,currentposition.y); + } else { + if(align == 0) align=SE; + offset=currentposition; + } + + frame f; + label(f,s,(indent,0),align,p,filltype); + pair m=tinv*min(f); + pair M=tinv*min(f); + + if(abs(offset.x+M.x) > 1) + warning("slidetoowide","slide too wide on page "+(string) page+':\n'+ + (string) s); + + if(abs(offset.y+M.y) > 1) { + void toohigh() { + warning("slidetoohigh","slide too high on page "+(string) page+':\n'+ + (string) s); + } + if(M.y-m.y < 2) { + newslide(); offset=(offset.x,currentposition.y); + if(offset.y+M.y > 1 || offset.y+m.y < -1) toohigh(); + } else toohigh(); + } + + if(step) { + if(!firststep) step(); + firststep=false; + } + + add(f,offset); + incrementposition((0,(tinv*(min(f)-skip*I*lineskip(p)*pt)).y)); +} + +void center(string s, pen p=itempen) +{ + remark(center=true,"\center "+s,p); +} + +void vbox(string s, pen p=itempen) +{ + remark(center=true,"\vbox{"+s+"}",p,minipage=false,skip=0); +} + +void skip(real n=1) +{ + incrementposition((0,(tinv*(-n*itemskip*I*lineskip(itempen)*pt)).y)); +} + +void equation(string s, pen p=itempen) +{ + skip(aboveequationskip); + vbox("\begin{gather*}"+s+"\end{gather*}",p); +} + +void equations(string s, pen p=itempen) +{ + skip(aboveequationskip); + if(find(s,"&") >= 0) + vbox("\begin{align*}"+s+"\end{align*}",p); + else + vbox("\begin{gather*}"+s+"\end{gather*}",p); +} + +void display(frame[] f, real margin=0, pair align=S, pen p=itempen, + pen figuremattpen=figuremattpen, bool final=true) +{ + if(f.length == 0) return; + real[] width=new real[f.length]; + real sum; + for(int i=0; i < f.length; ++i) { + width[i]=size(f[i]).x; + sum += width[i]; + } + if(sum > pagewidth) + warning("toowide","slide too wide on page "+(string) page); + else margin=(pagewidth-sum)/(f.length+1); + real pos; + frame F; + for(int i=0; i < f.length; ++i) { + real w=0.5*(margin+width[i]); + pos += w; + add(F,f[i],(pos,0),Fill(figureborder,figuremattpen)); + pos += w; + } + add(F,(0,currentposition.y),align); + if (final) { + real a=0.5(unit(align).y-1); + incrementposition( + (0, (tinv*(a*(max(F)-min(F))-itemskip*I*lineskip(p)*pt)).y)); + } +} + +void display(frame f, real margin=0, pair align=S, pen p=itempen, + pen figuremattpen=figuremattpen, bool final=true) +{ + display(new frame[] {f},margin,align,p,figuremattpen, final); +} + +void display(string[] s, real margin=0, string[] captions=new string[], + string caption="", pair align=S, pen p=itempen, + pen figuremattpen=figuremattpen, bool final=true) +{ + frame[] f=new frame[s.length]; + frame F; + for(int i=0; i < s.length; ++i) { + f[i]=newframe; + label(f[i],s[i]); + add(F,f[i],(0,0)); + } + real y=point(F,S).y; + int stop=min(s.length,captions.length); + for(int i=0; i < stop; ++i) { + if(captions[i] != "") + label(f[i],captions[i],point(f[i],S).x+I*y,S); + } + display(f,margin,align,p,figuremattpen, final); + if(caption != "") center(caption,p); +} + +void display(string s, string caption="", pair align=S, pen p=itempen, + pen figuremattpen=figuremattpen, bool final=true) +{ + display(new string[] {s},caption,align,p,figuremattpen, final); +} + +void figure(string[] s, string options="", real margin=0, + string[] captions=new string[], string caption="", + pair align=S, pen p=itempen, pen figuremattpen=figuremattpen, + bool final=true) +{ + string[] S; + for(int i=0; i < s.length; ++i) { + S[i]=graphic(s[i],options); + } + + display(S,margin,captions,caption,align,itempen,figuremattpen,final); +} + +void figure(string s, string options="", string caption="", pair align=S, + pen p=itempen, pen figuremattpen=figuremattpen, bool final=true) +{ + figure(new string[] {s},options,caption,align,p,figuremattpen,final); +} + +void multifigure(string[] slist, string options="", string caption="", + pair align=S, pen p=itempen, pen figuremattpen=figuremattpen, + bool step=itemstep) +{ + if(step) { + int lastnode=currentpicture.nodes.length-1; + for (int i=0; i<slist.length-1; ++i) { + figure(slist[i],options,caption,align,p,figuremattpen,final=false); + erasestep(lastnode); + } + } + figure(slist[slist.length-1],options,caption,align,p,figuremattpen,final=true); + + if(!firststep) step(); + firststep=false; +} + +void indexedfigure(string prefix, int first, int last, + string options="", string caption="", + pair align=S, pen p=itempen, pen figuremattpen=figuremattpen, + bool step=itemstep) +{ + bool Stepping=stepping; + stepping=true; + string[] s; + for(int i=first; i <= last; ++i) + s.push(prefix+string(i)); + multifigure(s,options,caption,align,p,figuremattpen,step=step); + stepping=Stepping; +} + +string[] codefile; + +void asyinclude(string s, real xsize=0, real ysize=xsize) +{ + picture currentpictureSave=currentpicture; + currentpicture=new picture; + _eval("include \""+s+"\";",true); + s=stripdirectory(outprefix()+"_"+s); + codefile.push(s); + frame f=(xsize > 0 || ysize > 0) ? + currentpicture.fit(xsize,ysize) : currentpicture.fit(); + currentpicture=currentpictureSave; + display(f); +} + +string cropcode(string s) +{ + while(substr(s,0,1) == '\n') s=substr(s,1,length(s)); + while(substr(s,length(s)-1,1) == '\n') s=substr(s,0,length(s)-1); + return s; +} + +void code(bool center=false, string s, pen p=codepen, + real indent=0, real skip=codeskip, + filltype filltype=NoFill) +{ + remark(center,"{\tt "+verbatim(cropcode(s))+"}",p,indent,skip,filltype); +} + +void filecode(bool center=false, string s, pen p=codepen, real indent=0, + real skip=codeskip, filltype filltype=NoFill) +{ + code(center,file(s),p,indent,skip,filltype); +} + +void asyfigure(string s, string options="", string caption="", pair align=S, + pen p=codepen, pen figuremattpen=figuremattpen, + filltype filltype=NoFill, bool newslide=false) +{ + string a=s+".asy"; + asy(nativeformat(),s); + s += "."+nativeformat(); + if(newslide && !empty()) { + newslide(); + currentposition=(currentposition.x,0); + align=0; + } + figure(s,options,caption,align,p,figuremattpen); +} + +string asywrite(string s, string preamble="") +{ + static int count=0; + string name=outprefix()+"_slide"+(string) count; + ++count; + file temp=output(name+".asy"); + write(temp,preamble); + write(temp,s); + close(temp); + codefile.push(name); + return name; +} + +void asycode(bool center=false, string s, string options="", + string caption="", string preamble="", + pair align=S, pen p=codepen, pen figuremattpen=figuremattpen, + real indent=0, real skip=codeskip, + filltype filltype=NoFill, bool newslide=false) +{ + code(center,s,p,indent,skip,filltype); + asyfigure(asywrite(s,preamble),options,caption,align,p,figuremattpen,filltype, + newslide); +} + +void asyfilecode(bool center=false, string s, string options="", + string caption="", + pair align=S, pen p=codepen, pen figuremattpen=figuremattpen, + real indent=0, real skip=codeskip, + filltype filltype=NoFill, bool newslide=false) +{ + filecode(center,s+".asy",p,indent,skip,filltype); + asyfigure(s,options,caption,align,p,figuremattpen,filltype,newslide); +} + +void item(string s, pen p=itempen, bool step=itemstep) +{ + frame b; + label(b,bullet,(0,0),p); + real bulletwidth=max(b).x-min(b).x; + remark(bullet+"\hangindent"+(string) (bulletwidth/pt)+"pt$\,$"+s,p, + -bulletwidth,step=step); +} + +void subitem(string s, pen p=itempen) +{ + remark("\quad -- "+s,p); +} + +void titlepage(string title, string author, string institution="", + string date="", string url="", bool newslide=false) +{ + newslide(); + currentposition=titlepageposition; + center(title,titlepagepen); + center(author,authorpen); + if(institution != "") center(institution,institutionpen); + currentposition -= dateskip; + if(date != "") center(date,datepen); + currentposition -= urlskip; + if(url != "") center("{\tt "+url+"}",urlpen); +} + +// Resolve optional bibtex citations: +void bibliographystyle(string name) +{ + settings.twice=true; + settings.keepaux=true; + texpreamble("\bibliographystyle{"+name+"}"); +} + +void bibliography(string name) +{ + numberpage(); + havepagenumber=false; + string s=texcolor(backgroundcolor); + if(s != "") tex("\definecolor{Background}"+s+"\pagecolor{Background}%"); + label("",itempen); + tex("\eject\def\refname{\fontsize{"+string(fontsize(titlepen))+"}{"+ + string(lineskip(titlepen))+"}\selectfont References}%"); + real hmargin,vmargin; + if(pdf()) { + hmargin=1; + vmargin=0; + } else { + hmargin=1.5; + vmargin=1; + } + string s; + if(landscape) { + s="{\centering\textheight="+string(pageheight-1inch)+"bp\textwidth="+ + string(pagewidth-1.5inches)+"bp"+ + "\vsize=\textheight\hsize=\textwidth\linewidth=\hsize"+ + "\topmargin="+string(vmargin)+"in\oddsidemargin="+string(hmargin)+"in"; + } else + s="{\centering\textheight="+string(pageheight-0.5inches)+"bp\textwidth="+ + string(pagewidth-0.5inches)+ + "bp\hsize=\textwidth\linewidth=\textwidth\vsize=\textheight"+ + "\topmargin=0.5in\oddsidemargin=1in"; + s += "\evensidemargin=\oddsidemargin\bibliography{"+name+"}\eject}"; + tex(s); +} + +exitfcn currentexitfunction=atexit(); + +void exitfunction() +{ + numberpage(); + if(currentexitfunction != null) currentexitfunction(); + if(!settings.keep) + for(int i=0; i < codefile.length; ++i) { + string name=codefile[i]; + delete(name+"."+nativeformat()); + delete(name+"_.aux"); + delete(name+".asy"); + } + codefile=new string[]; +} + +atexit(exitfunction); diff --git a/Build/source/utils/asymptote/base/slopefield.asy b/Build/source/utils/asymptote/base/slopefield.asy new file mode 100644 index 00000000000..e87e2cd36f9 --- /dev/null +++ b/Build/source/utils/asymptote/base/slopefield.asy @@ -0,0 +1,86 @@ +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); + } + } + 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/Build/source/utils/asymptote/base/smoothcontour3.asy b/Build/source/utils/asymptote/base/smoothcontour3.asy new file mode 100644 index 00000000000..be6f900a4d1 --- /dev/null +++ b/Build/source/utils/asymptote/base/smoothcontour3.asy @@ -0,0 +1,1582 @@ +// Copyright 2015 Charles Staats III +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// smoothcontour3 +// An Asymptote module for drawing smooth implicitly defined surfaces +// author: Charles Staats III +// charles dot staats dot iii at gmail dot com + +import graph_settings; // for nmesh +import three; +import math; + +/***********************************************/ +/******** CREATING BEZIER PATCHES **************/ +/******** WITH SPECIFIED NORMALS **************/ +/***********************************************/ + +// The weight given to minimizing the sum of squares of +// the mixed partials at the corners of the bezier patch. +// If this weight is zero, the result is undefined in +// places and can be rather wild even where it is +// defined. +// The struct is used to as a namespace. +struct pathwithnormals_settings { + static real wildnessweight = 1e-3; +} +private from pathwithnormals_settings unravel wildnessweight; + +// The Bernstein basis polynomials of degree 3: +real B03(real t) { return (1-t)^3; } +real B13(real t) { return 3*t*(1-t)^2; } +real B23(real t) { return 3*t^2*(1-t); } +real B33(real t) { return t^3; } + +private typedef real function(real); +function[] bernstein = new function[] {B03, B13, B23, B33}; + +// This function attempts to produce a Bezier patch +// with the specified boundary path and normal directions. +// For instance, the patch should be normal to +// u0normals[0] at (0, 0.25), +// normal to u0normals[1] at (0, 0.5), and +// normal to u0normals[2] at (0, 0.75). +// The actual normal (as computed by the patch.normal() function) +// may be parallel to the specified normal, antiparallel, or +// even zero. +// +// A small amount of deviation is allowed in order to stabilize +// the algorithm (by keeping the mixed partials at the corners from +// growing too large). +// +// Note that the specified normals are projected to be orthogonal to +// the specified boundary path. However, the entries in the array +// remain intact. +patch patchwithnormals(path3 external, triple[] u0normals, triple[] u1normals, + triple[] v0normals, triple[] v1normals) +{ + assert(cyclic(external)); + assert(length(external) == 4); + assert(u0normals.length == 3); + assert(u1normals.length == 3); + assert(v0normals.length == 3); + assert(v1normals.length == 3); + + triple[][] controlpoints = new triple[4][4]; + controlpoints[0][0] = point(external,0); + controlpoints[1][0] = postcontrol(external,0); + controlpoints[2][0] = precontrol(external,1); + controlpoints[3][0] = point(external,1); + controlpoints[3][1] = postcontrol(external,1); + controlpoints[3][2] = precontrol(external,2); + controlpoints[3][3] = point(external,2); + controlpoints[2][3] = postcontrol(external,2); + controlpoints[1][3] = precontrol(external,3); + controlpoints[0][3] = point(external,3); + controlpoints[0][2] = postcontrol(external,3); + controlpoints[0][1] = precontrol(external, 4); + + real[][] matrix = new real[24][12]; + for (int i = 0; i < matrix.length; ++i) + for (int j = 0; j < matrix[i].length; ++j) + matrix[i][j] = 0; + real[] rightvector = new real[24]; + for (int i = 0; i < rightvector.length; ++i) + rightvector[i] = 0; + + void addtocoeff(int i, int j, int count, triple coeffs) { + if (1 <= i && i <= 2 && 1 <= j && j <= 2) { + int position = 3 * (2 * (i-1) + (j-1)); + matrix[count][position] += coeffs.x; + matrix[count][position+1] += coeffs.y; + matrix[count][position+2] += coeffs.z; + } else { + rightvector[count] -= dot(controlpoints[i][j], coeffs); + } + } + + void addtocoeff(int i, int j, int count, real coeff) { + if (1 <= i && i <= 2 && 1 <= j && j <= 2) { + int position = 3 * (2 * (i-1) + (j-1)); + matrix[count][position] += coeff; + matrix[count+1][position+1] += coeff; + matrix[count+2][position+2] += coeff; + } else { + rightvector[count] -= controlpoints[i][j].x * coeff; + rightvector[count+1] -= controlpoints[i][j].y * coeff; + rightvector[count+2] -= controlpoints[i][j].z * coeff; + } + } + + int count = 0; + + void apply_u0(int j, real a, triple n) { + real factor = 3 * bernstein[j](a); + addtocoeff(0,j,count,-factor*n); + addtocoeff(1,j,count,factor*n); + } + void apply_u0(real a, triple n) { + triple tangent = dir(external, 4-a); + n -= dot(n,tangent)*tangent; + n = unit(n); + for (int j = 0; j < 4; ++j) { + apply_u0(j,a,n); + } + ++count; + } + apply_u0(0.25, u0normals[0]); + apply_u0(0.5, u0normals[1]); + apply_u0(0.75, u0normals[2]); + + void apply_u1(int j, real a, triple n) { + real factor = 3 * bernstein[j](a); + addtocoeff(3,j,count,factor*n); + addtocoeff(2,j,count,-factor*n); + } + void apply_u1(real a, triple n) { + triple tangent = dir(external, 1+a); + n -= dot(n,tangent)*tangent; + n = unit(n); + for (int j = 0; j < 4; ++j) + apply_u1(j,a,n); + ++count; + } + apply_u1(0.25, u1normals[0]); + apply_u1(0.5, u1normals[1]); + apply_u1(0.75, u1normals[2]); + + void apply_v0(int i, real a, triple n) { + real factor = 3 * bernstein[i](a); + addtocoeff(i,0,count,-factor*n); + addtocoeff(i,1,count,factor*n); + } + void apply_v0(real a, triple n) { + triple tangent = dir(external, a); + n -= dot(n,tangent) * tangent; + n = unit(n); + for (int i = 0; i < 4; ++i) + apply_v0(i,a,n); + ++count; + } + apply_v0(0.25, v0normals[0]); + apply_v0(0.5, v0normals[1]); + apply_v0(0.75, v0normals[2]); + + void apply_v1(int i, real a, triple n) { + real factor = 3 * bernstein[i](a); + addtocoeff(i,3,count,factor*n); + addtocoeff(i,2,count,-factor*n); + } + void apply_v1(real a, triple n) { + triple tangent = dir(external, 3-a); + n -= dot(n,tangent)*tangent; + n = unit(n); + for (int i = 0; i < 4; ++i) + apply_v1(i,a,n); + ++count; + } + apply_v1(0.25, v1normals[0]); + apply_v1(0.5, v1normals[1]); + apply_v1(0.75, v1normals[2]); + + addtocoeff(0,0,count,9*wildnessweight); + addtocoeff(1,1,count,9*wildnessweight); + addtocoeff(0,1,count,-9*wildnessweight); + addtocoeff(1,0,count,-9*wildnessweight); + count+=3; + addtocoeff(3,3,count,9*wildnessweight); + addtocoeff(2,2,count,9*wildnessweight); + addtocoeff(3,2,count,-9*wildnessweight); + addtocoeff(2,3,count,-9*wildnessweight); + count+=3; + addtocoeff(0,3,count,9*wildnessweight); + addtocoeff(1,2,count,9*wildnessweight); + addtocoeff(1,3,count,-9*wildnessweight); + addtocoeff(0,2,count,-9*wildnessweight); + count += 3; + addtocoeff(3,0,count,9*wildnessweight); + addtocoeff(2,1,count,9*wildnessweight); + addtocoeff(3,1,count,-9*wildnessweight); + addtocoeff(2,0,count,-9*wildnessweight); + count += 3; + + real[] solution = leastsquares(matrix, rightvector, warn=false); + if (solution.length == 0) { // if the matrix was singular + write("Warning: unable to solve matrix for specifying edge normals " + + "on bezier patch. Using coons patch."); + return patch(external); + } + + for (int i = 1; i <= 2; ++i) { + for (int j = 1; j <= 2; ++j) { + int position = 3 * (2 * (i-1) + (j-1)); + controlpoints[i][j] = (solution[position], + solution[position+1], + solution[position+2]); + } + } + + return patch(controlpoints); +} + +// This function attempts to produce a Bezier triangle +// with the specified boundary path and normal directions at the +// edge midpoints. The bezier triangle should be normal to +// n1 at point(external, 0.5), +// normal to n2 at point(external, 1.5), and +// normal to n3 at point(external, 2.5). +// The actual normal (as computed by the patch.normal() function) +// may be parallel to the specified normal, antiparallel, or +// even zero. +// +// A small amount of deviation is allowed in order to stabilize +// the algorithm (by keeping the mixed partials at the corners from +// growing too large). +patch trianglewithnormals(path3 external, triple n1, + triple n2, triple n3) { + assert(cyclic(external)); + assert(length(external) == 3); + // Use the formal symbols a3, a2b, abc, etc. to denote the control points, + // following the Wikipedia article on Bezier triangles. + triple a3 = point(external, 0), a2b = postcontrol(external, 0), + ab2 = precontrol(external, 1), b3 = point(external, 1), + b2c = postcontrol(external, 1), bc2 = precontrol(external, 2), + c3 = point(external, 2), ac2 = postcontrol(external, 2), + a2c = precontrol(external, 0); + + // Use orthogonal projection to ensure that the normal vectors are + // actually normal to the boundary path. + triple tangent = dir(external, 0.5); + n1 -= dot(n1,tangent)*tangent; + n1 = unit(n1); + + tangent = dir(external, 1.5); + n2 -= dot(n2,tangent)*tangent; + n2 = unit(n2); + + tangent = dir(external, 2.5); + n3 -= dot(n3,tangent)*tangent; + n3 = unit(n3); + + real wild = 2 * wildnessweight; + real[][] matrix = { {n1.x, n1.y, n1.z}, + {n2.x, n2.y, n2.z}, + {n3.x, n3.y, n3.z}, + { wild, 0, 0}, + { 0, wild, 0}, + { 0, 0, wild} }; + real[] rightvector = + { dot(n1, (a3 + 3a2b + 3ab2 + b3 - 2a2c - 2b2c)) / 4, + dot(n2, (b3 + 3b2c + 3bc2 + c3 - 2ab2 - 2ac2)) / 4, + dot(n3, (c3 + 3ac2 + 3a2c + a3 - 2bc2 - 2a2b)) / 4 }; + + // The inner control point that minimizes the sum of squares of + // the mixed partials on the corners. + triple tameinnercontrol = + ((a2b + a2c - a3) + (ab2 + b2c - b3) + (ac2 + bc2 - c3)) / 3; + rightvector.append(wild * new real[] + {tameinnercontrol.x, tameinnercontrol.y, tameinnercontrol.z}); + real[] solution = leastsquares(matrix, rightvector, warn=false); + if (solution.length == 0) { // if the matrix was singular + write("Warning: unable to solve matrix for specifying edge normals " + + "on bezier triangle. Using coons triangle."); + return patch(external); + } + triple innercontrol = (solution[0], solution[1], solution[2]); + return patch(external, innercontrol); +} + +// A wrapper for the previous functions when the normal direction +// is given as a function of direction. The wrapper can also +// accommodate cyclic boundary paths of between one and four +// segments, although the results are best by far when there +// are three or four segments. +patch patchwithnormals(path3 external, triple normalat(triple)) { + assert(cyclic(external)); + assert(1 <= length(external) && length(external) <= 4); + if (length(external) == 3) { + triple n1 = normalat(point(external, 0.5)); + triple n2 = normalat(point(external, 1.5)); + triple n3 = normalat(point(external, 2.5)); + return trianglewithnormals(external, n1, n2, n3); + } + while (length(external) < 4) external = external -- cycle; + triple[] u0normals = new triple[3]; + triple[] u1normals = new triple[3]; + triple[] v0normals = new triple[3]; + triple[] v1normals = new triple[3]; + for (int i = 1; i <= 3; ++i) { + v0normals[i-1] = unit(normalat(point(external, i/4))); + u1normals[i-1] = unit(normalat(point(external, 1 + i/4))); + v1normals[i-1] = unit(normalat(point(external, 3 - i/4))); + u0normals[i-1] = unit(normalat(point(external, 4 - i/4))); + } + return patchwithnormals(external, u0normals, u1normals, v0normals, v1normals); +} + +/***********************************************/ +/********* DUAL CUBE GRAPH UTILITY *************/ +/***********************************************/ + +// Suppose a plane intersects a (hollow) cube, and +// does not intersect any vertices. Then its intersection +// with cube forms a cycle. The goal of the code below +// is to reconstruct the order of the cycle +// given only an unordered list of which edges the plane +// intersects. +// +// Basically, the question is this: If we know the points +// in which a more-or-less planar surface intersects the +// edges of cube, how do we connect those points? +// +// When I wrote the code, I was thinking in terms of the +// dual graph of a cube, in which "vertices" are really +// faces of the cube and "edges" connect those "vertices." + +// An enum for the different "vertices" (i.e. faces) +// available. NULL_VERTEX is primarily intended as a +// return value to indicate the absence of a desired +// vertex. +private int NULL_VERTEX = -1; +private int XHIGH = 0; +private int XLOW = 1; +private int YHIGH = 2; +private int YLOW = 3; +private int ZHIGH = 4; +private int ZLOW = 5; + +// An unordered set of nonnegative integers. +// Since the intent is to use +// only the six values from the enum above, no effort +// was made to use scalable algorithms. +struct intset { + private bool[] ints = new bool[0]; + private int size = 0; + + bool contains(int item) { + assert(item >= 0); + if (item >= ints.length) return false; + return ints[item]; + } + + // Returns true if the item was added (i.e., was + // not already present). + bool add(int item) { + assert(item >= 0); + while (item >= ints.length) ints.push(false); + if (ints[item]) return false; + ints[item] = true; + ++size; + return true; + } + + int[] elements() { + int[] toreturn; + for (int i = 0; i < ints.length; ++i) { + if (ints[i]) toreturn.push(i); + } + return toreturn; + } + + int size() { return size; } +} + +// A map from integers to sets of integers. Again, no +// attempt is made to use scalable data structures. +struct int_to_intset { + int[] keys = new int[0]; + intset[] values = new intset[0]; + + void add(int key, int value) { + for (int i = 0; i < keys.length; ++i) { + if (keys[i] == key) { + values[i].add(value); + return; + } + } + keys.push(key); + intset newset; + values.push(newset); + newset.add(value); + } + + private int indexOf(int key) { + for (int i = 0; i < keys.length; ++i) { + if (keys[i] == key) return i; + } + return -1; + } + + int[] get(int key) { + int i = indexOf(key); + if (i < 0) return new int[0]; + else return values[i].elements(); + } + + int numvalues(int key) { + int i = indexOf(key); + if (i < 0) return 0; + else return values[i].size(); + } + + int numkeys() { + return keys.length; + } +} + +// A struct intended to represent an undirected edge between +// two "vertices." +struct edge { + int start; + int end; + void operator init(int a, int b) { + start = a; + end = b; + } + bool bordersvertex(int v) { return start == v || end == v; } +} + +string operator cast(edge e) { + int a, b; + if (e.start <= e.end) {a = e.start; b = e.end;} + else {a = e.end; b = e.start; } + return (string)a + " <-> " + (string)b; +} + +bool operator == (edge a, edge b) { + if (a.start == b.start && a.end == b.end) return true; + if (a.start == b.end && a.end == b.start) return true; + return false; +} + +string operator cast(edge[] edges) { + string toreturn = "{ "; + for (int i = 0; i < edges.length; ++i) { + toreturn += edges[i]; + if (i < edges.length-1) toreturn += ", "; + } + return toreturn + " }"; +} + +// Finally, the function that strings together a list of edges +// into a cycle. It makes assumptions that hold true if the +// list of edges did in fact come from a plane intersection +// containing no vertices of the cube. For instance, such a +// plane can contain at most two noncollinear points of any +// one face; consequently, no face can border more than two of +// the selected edges. +// +// If the underlying assumptions prove to be false, the function +// returns null. +int[] makecircle(edge[] edges) { + if (edges.length == 0) return new int[0]; + int_to_intset graph; + for (edge e : edges) { + graph.add(e.start, e.end); + graph.add(e.end, e.start); + } + int currentvertex = edges[0].start; + int startvertex = currentvertex; + int lastvertex = NULL_VERTEX; + int[] toreturn = new int[0]; + do { + toreturn.push(currentvertex); + int[] adjacentvertices = graph.get(currentvertex); + if (adjacentvertices.length != 2) return null; + for (int v : adjacentvertices) { + if (v != lastvertex) { + lastvertex = currentvertex; + currentvertex = v; + break; + } + } + } while (currentvertex != startvertex); + if (toreturn.length != graph.numkeys()) return null; + toreturn.cyclic = true; + return toreturn; +} + +/***********************************************/ +/********** PATHS BETWEEN POINTS ***************/ +/***********************************************/ +// Construct paths between two points with additional +// constraints; for instance, the path must be orthogonal +// to a certain vector at each of the endpoints, must +// lie within a specified plane or a specified face +// of a rectangular solid,.... + +// A vector (typically a normal vector) at a specified position. +struct positionedvector { + triple position; + triple direction; + void operator init(triple position, triple direction) { + this.position = position; + this.direction = direction; + } +} + +string operator cast(positionedvector vv) { + return "position: " + (string)(vv.position) + " vector: " + (string)vv.direction; +} + +// The angle, in degrees, between two vectors. +real angledegrees(triple a, triple b) { + real dotprod = dot(a,b); + real lengthprod = max(abs(a) * abs(b), abs(dotprod)); + if (lengthprod == 0) return 0; + return aCos(dotprod / lengthprod); +} + +// A path (single curved segment) between two points. At each point +// is specified a vector orthogonal to the path. +path3 pathbetween(positionedvector v1, positionedvector v2) { + triple n1 = unit(v1.direction); + triple n2 = unit(v2.direction); + + triple p1 = v1.position; + triple p2 = v2.position; + triple delta = p2-p1; + + triple dir1 = delta - dot(delta, n1)*n1; + triple dir2 = delta - dot(delta, n2)*n2; + return p1 {dir1} .. {dir2} p2; +} + +// Assuming v1 and v2 are linearly independent, returns an array {a, b} +// such that a v1 + b v2 is the orthogonal projection of toproject onto +// the span of v1 and v2. If v1 and v2 are dependent, returns an empty array +// (if warn==false) or throws an error (if warn==true). +real[] projecttospan_findcoeffs(triple toproject, triple v1, triple v2, + bool warn=false) { + real[][] matrix = {{v1.x, v2.x}, + {v1.y, v2.y}, + {v1.z, v2.z}}; + real[] desiredanswer = {toproject.x, toproject.y, toproject.z}; + return leastsquares(matrix, desiredanswer, warn=warn); +} + +// Project the triple toproject into the span of a and b, but restrict +// to the quarter-plane of linear combinations a v1 + b v2 such that +// a >= mincoeff and b >= mincoeff. If v1 and v2 are linearly dependent, +// return a random (positive) linear combination. +triple projecttospan(triple toproject, triple v1, triple v2, + real mincoeff = 0.05) { + real[] coeffs = projecttospan_findcoeffs(toproject, v1, v2, warn=false); + real a, b; + if (coeffs.length == 0) { + a = mincoeff + unitrand(); + b = mincoeff + unitrand(); + } else { + a = max(coeffs[0], mincoeff); + b = max(coeffs[1], mincoeff); + } + return a*v1 + b*v2; +} + +// A path between two specified vertices of a cyclic path. The +// path tangent at each endpoint is guaranteed to lie within the +// quarter-plane spanned by positive linear combinations of the +// tangents of the two outgoing paths at that endpoint. +path3 pathbetween(path3 edgecycle, int vertex1, int vertex2) { + triple point1 = point(edgecycle, vertex1); + triple point2 = point(edgecycle, vertex2); + + triple v1 = -dir(edgecycle, vertex1, sign=-1); + triple v2 = dir(edgecycle, vertex1, sign= 1); + triple direction1 = projecttospan(unit(point2-point1), v1, v2); + + v1 = -dir(edgecycle, vertex2, sign=-1); + v2 = dir(edgecycle, vertex2, sign= 1); + triple direction2 = projecttospan(unit(point1-point2), v1, v2); + + return point1 {direction1} .. {-direction2} point2; +} + +// This function applies a heuristic to choose two "opposite" +// vertices (separated by three segments) of edgecycle, which +// is required to be a cyclic path consisting of 5 or 6 segments. +// The two chosen vertices are pushed to savevertices. +// +// The function returns a path between the two chosen vertices. The +// path tangent at each endpoint is guaranteed to lie within the +// quarter-plane spanned by positive linear combinations of the +// tangents of the two outgoing paths at that endpoint. +path3 bisector(path3 edgecycle, int[] savevertices) { + real mincoeff = 0.05; + assert(cyclic(edgecycle)); + int n = length(edgecycle); + assert(n >= 5 && n <= 6); + triple[] forwarddirections = sequence(new triple(int i) { + return dir(edgecycle, i, sign=1); + }, n); + forwarddirections.cyclic = true; + triple[] backwarddirections = sequence(new triple(int i) { + return -dir(edgecycle, i, sign=-1); + }, n); + backwarddirections.cyclic = true; + real[] angles = sequence(new real(int i) { + return angledegrees(forwarddirections[i], backwarddirections[i]); + }, n); + angles.cyclic = true; + int lastindex = (n == 5 ? 4 : 2); + real maxgoodness = 0; + int chosenindex = -1; + triple directionout, directionin; + for (int i = 0; i <= lastindex; ++i) { + int opposite = i + 3; + triple vec = unit(point(edgecycle, opposite) - point(edgecycle, i)); + real[] coeffsbegin = projecttospan_findcoeffs(vec, forwarddirections[i], + backwarddirections[i]); + if (coeffsbegin.length == 0) continue; + coeffsbegin[0] = max(coeffsbegin[0], mincoeff); + coeffsbegin[1] = max(coeffsbegin[1], mincoeff); + + real[] coeffsend = projecttospan_findcoeffs(-vec, forwarddirections[opposite], + backwarddirections[opposite]); + if (coeffsend.length == 0) continue; + coeffsend[0] = max(coeffsend[0], mincoeff); + coeffsend[1] = max(coeffsend[1], mincoeff); + + real goodness = angles[i] * angles[opposite] * coeffsbegin[0] * coeffsend[0] + * coeffsbegin[1] * coeffsend[1]; + if (goodness > maxgoodness) { + maxgoodness = goodness; + directionout = coeffsbegin[0] * forwarddirections[i] + + coeffsbegin[1] * backwarddirections[i]; + directionin = -(coeffsend[0] * forwarddirections[opposite] + + coeffsend[1] * backwarddirections[opposite]); + chosenindex = i; + } + } + if (chosenindex == -1) { + savevertices.push(0); + savevertices.push(3); + return pathbetween(edgecycle, 0, 3); + } else { + savevertices.push(chosenindex); + savevertices.push(chosenindex+3); + return point(edgecycle, chosenindex) {directionout} .. + {directionin} point(edgecycle, chosenindex + 3); + } +} + +// A path between two specified points (with specified normals) that lies +// within a specified face of a rectangular solid. +path3 pathinface(positionedvector v1, positionedvector v2, + triple facenorm, triple edge1normout, triple edge2normout) +{ + triple dir1 = cross(v1.direction, facenorm); + real dotprod = dot(dir1, edge1normout); + if (dotprod > 0) dir1 = -dir1; + // Believe it or not, this "tiebreaker" is actually relevant at times, + // for instance, when graphing the cone x^2 + y^2 = z^2 over the region + // -1 <= x,y,z <= 1. + else if (dotprod == 0 && dot(dir1, v2.position - v1.position) < 0) dir1 = -dir1; + + triple dir2 = cross(v2.direction, facenorm); + dotprod = dot(dir2, edge2normout); + if (dotprod < 0) dir2 = -dir2; + else if (dotprod == 0 && dot(dir2, v2.position - v1.position) < 0) dir2 = -dir2; + + return v1.position {dir1} .. {dir2} v2.position; +} + +triple normalout(int face) { + if (face == XHIGH) return X; + else if (face == YHIGH) return Y; + else if (face == ZHIGH) return Z; + else if (face == XLOW) return -X; + else if (face == YLOW) return -Y; + else if (face == ZLOW) return -Z; + else return O; +} + +// A path between two specified points (with specified normals) that lies +// within a specified face of a rectangular solid. +path3 pathinface(positionedvector v1, positionedvector v2, + int face, int edge1face, int edge2face) { + return pathinface(v1, v2, normalout(face), normalout(edge1face), + normalout(edge2face)); +} + +/***********************************************/ +/******** DRAWING IMPLICIT SURFACES ************/ +/***********************************************/ + +// DEPRECATED +// Quadrilateralization: +// Produce a surface (array of *nondegenerate* Bezier patches) with a +// specified three-segment boundary. The surface should approximate the +// zero locus of the specified f with its specified gradient. +// +// If it is not possible to produce the desired result without leaving the +// specified rectangular region, returns a length-zero array. +// +// Dividing a triangle into smaller quadrilaterals this way is opposite +// the usual trend in mathematics. However, *before the introduction of bezier +// triangles,* the pathwithnormals algorithm +// did a poor job of choosing a good surface when the boundary path did +// not consist of four positive-length segments. +patch[] triangletoquads(path3 external, real f(triple), triple grad(triple), + triple a, triple b) { + static real epsilon = 1e-3; + assert(length(external) == 3); + assert(cyclic(external)); + + triple c0 = point(external, 0); + triple c1 = point(external, 1); + triple c2 = point(external, 2); + + triple center = (c0 + c1 + c2) / 3; + triple n = unit(cross(c1-c0, c2-c0)); + + real g(real t) { return f(center + t*n); } + + real tmin = -realMax, tmax = realMax; + void absorb(real t) { + if (t < 0) tmin = max(t,tmin); + else tmax = min(t,tmax); + } + if (n.x != 0) { + absorb((a.x - center.x) / n.x); + absorb((b.x - center.x) / n.x); + } + if (n.y != 0) { + absorb((a.y - center.y) / n.y); + absorb((b.y - center.y) / n.y); + } + if (n.z != 0) { + absorb((a.z - center.z) / n.z); + absorb((b.z - center.z) / n.z); + } + + real fa = g(tmin); + real fb = g(tmax); + if ((fa > 0 && fb > 0) || (fa < 0 && fb < 0)) { + return new patch[0]; + } else { + real t = findroot(g, tmin, tmax, fa=fa, fb=fb); + center += t * n; + } + + n = unit(grad(center)); + + triple m0 = point(external, 0.5); + positionedvector m0 = positionedvector(m0, unit(grad(m0))); + triple m1 = point(external, 1.5); + positionedvector m1 = positionedvector(m1, unit(grad(m1))); + triple m2 = point(external, 2.5); + positionedvector m2 = positionedvector(m2, unit(grad(m2))); + positionedvector c = positionedvector(center, unit(grad(center))); + + path3 pathto_m0 = pathbetween(c, m0); + path3 pathto_m1 = pathbetween(c, m1); + path3 pathto_m2 = pathbetween(c, m2); + + path3 quad0 = subpath(external, 0, 0.5) + & reverse(pathto_m0) + & pathto_m2 + & subpath(external, -0.5, 0) + & cycle; + path3 quad1 = subpath(external, 1, 1.5) + & reverse(pathto_m1) + & pathto_m0 + & subpath(external, 0.5, 1) + & cycle; + path3 quad2 = subpath(external, 2, 2.5) + & reverse(pathto_m2) + & pathto_m1 + & subpath(external, 1.5, 2) + & cycle; + + return new patch[] {patchwithnormals(quad0, grad), + patchwithnormals(quad1, grad), + patchwithnormals(quad2, grad)}; +} + +// Attempts to fill the path external (which should by a cyclic path consisting of +// three segments) with bezier triangle(s). Returns an empty array if it fails. +// +// In more detail: A single bezier triangle is computed using trianglewithnormals. The normals of +// the resulting triangle at the midpoint of each edge are computed. If any of these normals +// is in the negative f direction, the external triangle is subdivided into four external triangles +// and the same procedure is applied to each. If one or more of them has an incorrectly oriented +// edge normal, the function gives up and returns an empty array. +// +// Thus, the returned array consists of 0, 1, or 4 bezier triangles; no other array lengths +// are possible. +// +// This function assumes that the path orientation is consistent with f (and its gradient) +// -- i.e., that +// at a corner, (tangent in) x (tangent out) is in the positive f direction. +patch[] maketriangle(path3 external, real f(triple), + triple grad(triple), bool allowsubdivide = true) { + assert(cyclic(external)); + assert(length(external) == 3); + triple m1 = point(external, 0.5); + triple n1 = unit(grad(m1)); + triple m2 = point(external, 1.5); + triple n2 = unit(grad(m2)); + triple m3 = point(external, 2.5); + triple n3 = unit(grad(m3)); + patch beziertriangle = trianglewithnormals(external, n1, n2, n3); + if (dot(n1, beziertriangle.normal(0.5, 0)) >= 0 && + dot(n2, beziertriangle.normal(0.5, 0.5)) >= 0 && + dot(n3, beziertriangle.normal(0, 0.5)) >= 0) + return new patch[] {beziertriangle}; + + if (!allowsubdivide) return new patch[0]; + + positionedvector m1 = positionedvector(m1, n1); + positionedvector m2 = positionedvector(m2, n2); + positionedvector m3 = positionedvector(m3, n3); + path3 p12 = pathbetween(m1, m2); + path3 p23 = pathbetween(m2, m3); + path3 p31 = pathbetween(m3, m1); + patch[] triangles = maketriangle(p12 & p23 & p31 & cycle, f, grad=grad, + allowsubdivide=false); + if (triangles.length < 1) return new patch[0]; + + triangles.append(maketriangle(subpath(external, -0.5, 0.5) & reverse(p31) & cycle, + f, grad=grad, allowsubdivide=false)); + if (triangles.length < 2) return new patch[0]; + + triangles.append(maketriangle(subpath(external, 0.5, 1.5) & reverse(p12) & cycle, + f, grad=grad, allowsubdivide=false)); + if (triangles.length < 3) return new patch[0]; + + triangles.append(maketriangle(subpath(external, 1.5, 2.5) & reverse(p23) & cycle, + f, grad=grad, allowsubdivide=false)); + if (triangles.length < 4) return new patch[0]; + + return triangles; +} + + +// Returns true if the point is "nonsingular" (in the sense that the magnitude +// of the gradient is not too small) AND very close to the zero locus of f +// (assuming f is locally linear). +bool check_fpt_zero(triple testpoint, real f(triple), triple grad(triple)) { + real testval = f(testpoint); + real slope = abs(grad(testpoint)); + static real tolerance = 2*rootfinder_settings.roottolerance; + return !(slope > tolerance && abs(testval) / slope > tolerance); +} + +// Returns true if pt lies within the rectangular solid with +// opposite corners at a and b. +bool checkptincube(triple pt, triple a, triple b) { + real xmin = a.x; + real xmax = b.x; + real ymin = a.y; + real ymax = b.y; + real zmin = a.z; + real zmax = b.z; + if (xmin > xmax) { real t = xmax; xmax=xmin; xmin=t; } + if (ymin > ymax) { real t = ymax; ymax=ymin; ymin=t; } + if (zmin > zmax) { real t = zmax; zmax=zmin; zmin=t; } + + return ((xmin <= pt.x) && (pt.x <= xmax) && + (ymin <= pt.y) && (pt.y <= ymax) && + (zmin <= pt.z) && (pt.z <= zmax)); + +} + +// A convenience function for combining the previous two tests. +bool checkpt(triple testpt, real f(triple), triple grad(triple), + triple a, triple b) { + return checkptincube(testpt, a, b) && + check_fpt_zero(testpt, f, grad); +} + +// Attempts to fill in the boundary cycle with a collection of +// patches to approximate smoothly the zero locus of f. If unable to +// do so while satisfying certain checks, returns null. +// This is distinct from returning an empty +// array, which merely indicates that the boundary cycle is too small +// to be worth filling in. +patch[] quadpatches(path3 edgecycle, positionedvector[] corners, + real f(triple), triple grad(triple), + triple a, triple b, bool usetriangles) { + assert(corners.cyclic); + + // The tolerance for considering two points "essentially identical." + static real tolerance = 2.5 * rootfinder_settings.roottolerance; + + // If there are two neighboring vertices that are essentially identical, + // unify them into one. + for (int i = 0; i < corners.length; ++i) { + if (abs(corners[i].position - corners[i+1].position) < tolerance) { + if (corners.length == 2) return new patch[0]; + corners.delete(i); + edgecycle = subpath(edgecycle, 0, i) + & subpath(edgecycle, i+1, length(edgecycle)) + & cycle; + --i; + assert(length(edgecycle) == corners.length); + } + } + + static real areatolerance = tolerance^2; + + assert(corners.length >= 2); + if (corners.length == 2) { + // If the area is too small, just ignore it; otherwise, subdivide. + real area0 = abs(cross(-dir(edgecycle, 0, sign=-1, normalize=false), + dir(edgecycle, 0, sign=1, normalize=false))); + real area1 = abs(cross(-dir(edgecycle, 1, sign=-1, normalize=false), + dir(edgecycle, 1, sign=1, normalize=false))); + if (area0 < areatolerance && area1 < areatolerance) return new patch[0]; + else return null; + } + if (length(edgecycle) > 6) abort("too many edges: not possible."); + + for (int i = 0; i < length(edgecycle); ++i) { + if (angledegrees(dir(edgecycle,i,sign=1), + dir(edgecycle,i+1,sign=-1)) > 80) { + return null; + } + } + + if (length(edgecycle) == 3) { + patch[] toreturn = usetriangles ? maketriangle(edgecycle, f, grad) + : triangletoquads(edgecycle, f, grad, a, b); + if (toreturn.length == 0) return null; + else return toreturn; + } + if (length(edgecycle) == 4) { + return new patch[] {patchwithnormals(edgecycle, grad)}; + } + + int[] bisectorindices; + path3 middleguide = bisector(edgecycle, bisectorindices); + + triple testpoint = point(middleguide, 0.5); + if (!checkpt(testpoint, f, grad, a, b)) { + return null; + } + + patch[] toreturn = null; + path3 firstpatch = subpath(edgecycle, bisectorindices[0], bisectorindices[1]) + & reverse(middleguide) & cycle; + if (length(edgecycle) == 5) { + path3 secondpatch = middleguide + & subpath(edgecycle, bisectorindices[1], 5+bisectorindices[0]) & cycle; + toreturn = usetriangles ? maketriangle(secondpatch, f, grad) + : triangletoquads(secondpatch, f, grad, a, b); + if (toreturn.length == 0) return null; + toreturn.push(patchwithnormals(firstpatch, grad)); + } else { + // now length(edgecycle) == 6 + path3 secondpatch = middleguide + & subpath(edgecycle, bisectorindices[1], 6+bisectorindices[0]) + & cycle; + toreturn = new patch[] {patchwithnormals(firstpatch, grad), + patchwithnormals(secondpatch, grad)}; + } + return toreturn; +} + +// Numerical gradient of a function +typedef triple vectorfunction(triple); +vectorfunction nGrad(real f(triple)) { + static real epsilon = 1e-3; + return new triple(triple v) { + return ( (f(v + epsilon*X) - f(v - epsilon*X)) / (2 epsilon), + (f(v + epsilon*Y) - f(v - epsilon*Y)) / (2 epsilon), + (f(v + epsilon*Z) - f(v - epsilon*Z)) / (2 epsilon) ); + }; +} + +// A point together with a value at that location. +struct evaluatedpoint { + triple pt; + real value; + void operator init(triple pt, real value) { + this.pt = pt; + this.value = value; + } +} + +triple operator cast(evaluatedpoint p) { return p.pt; } + +// Compute the values of a function at every vertex of an nx by ny by nz +// array of rectangular solids. +evaluatedpoint[][][] make3dgrid(triple a, triple b, int nx, int ny, int nz, + real f(triple), bool allowzero = false) +{ + evaluatedpoint[][][] toreturn = new evaluatedpoint[nx+1][ny+1][nz+1]; + for (int i = 0; i <= nx; ++i) { + for (int j = 0; j <= ny; ++j) { + for (int k = 0; k <= nz; ++k) { + triple pt = (interp(a.x, b.x, i/nx), + interp(a.y, b.y, j/ny), + interp(a.z, b.z, k/nz)); + real value = f(pt); + if (value == 0 && !allowzero) value = 1e-5; + toreturn[i][j][k] = evaluatedpoint(pt, value); + } + } + } + return toreturn; +} + +// The following utilities make, for instance, slice(A, i, j, k, l) +// equivalent to what A[i:j][k:l] ought to mean for two- and three- +// -dimensional arrays of evaluatedpoints and of positionedvectors. +typedef evaluatedpoint T; +T[][] slice(T[][] a, int start1, int end1, int start2, int end2) { + T[][] toreturn = new T[end1-start1][]; + for (int i = start1; i < end1; ++i) { + toreturn[i-start1] = a[i][start2:end2]; + } + return toreturn; +} +T[][][] slice(T[][][] a, int start1, int end1, + int start2, int end2, + int start3, int end3) { + T[][][] toreturn = new T[end1-start1][][]; + for (int i = start1; i < end1; ++i) { + toreturn[i-start1] = slice(a[i], start2, end2, start3, end3); + } + return toreturn; +} +typedef positionedvector T; +T[][] slice(T[][] a, int start1, int end1, int start2, int end2) { + T[][] toreturn = new T[end1-start1][]; + for (int i = start1; i < end1; ++i) { + toreturn[i-start1] = a[i][start2:end2]; + } + return toreturn; +} +T[][][] slice(T[][][] a, int start1, int end1, + int start2, int end2, + int start3, int end3) { + T[][][] toreturn = new T[end1-start1][][]; + for (int i = start1; i < end1; ++i) { + toreturn[i-start1] = slice(a[i], start2, end2, start3, end3); + } + return toreturn; +} + +// An object of class gridwithzeros stores the values of a function at each vertex +// of a three-dimensional grid, together with zeros of the function along edges +// of the grid and the gradient of the function at each such zero. +struct gridwithzeros { + int nx, ny, nz; + evaluatedpoint[][][] corners; + positionedvector[][][] xdirzeros; + positionedvector[][][] ydirzeros; + positionedvector[][][] zdirzeros; + triple grad(triple); + real f(triple); + int maxdepth; + bool usetriangles; + + // Populate the edges with zeros that have a sign change and are not already + // populated. + void fillzeros() { + for (int j = 0; j < ny+1; ++j) { + for (int k = 0; k < nz+1; ++k) { + real y = corners[0][j][k].pt.y; + real z = corners[0][j][k].pt.z; + real f_along_x(real t) { return f((t, y, z)); } + for (int i = 0; i < nx; ++i) { + if (xdirzeros[i][j][k] != null) continue; + evaluatedpoint start = corners[i][j][k]; + evaluatedpoint end = corners[i+1][j][k]; + if ((start.value > 0 && end.value > 0) || (start.value < 0 && end.value < 0)) + xdirzeros[i][j][k] = null; + else { + triple root = (0,y,z); + root += X * findroot(f_along_x, start.pt.x, end.pt.x, + fa=start.value, fb=end.value); + triple normal = grad(root); + xdirzeros[i][j][k] = positionedvector(root, normal); + } + } + } + } + + for (int i = 0; i < nx+1; ++i) { + for (int k = 0; k < nz+1; ++k) { + real x = corners[i][0][k].pt.x; + real z = corners[i][0][k].pt.z; + real f_along_y(real t) { return f((x, t, z)); } + for (int j = 0; j < ny; ++j) { + if (ydirzeros[i][j][k] != null) continue; + evaluatedpoint start = corners[i][j][k]; + evaluatedpoint end = corners[i][j+1][k]; + if ((start.value > 0 && end.value > 0) || (start.value < 0 && end.value < 0)) + ydirzeros[i][j][k] = null; + else { + triple root = (x,0,z); + root += Y * findroot(f_along_y, start.pt.y, end.pt.y, + fa=start.value, fb=end.value); + triple normal = grad(root); + ydirzeros[i][j][k] = positionedvector(root, normal); + } + } + } + } + + for (int i = 0; i < nx+1; ++i) { + for (int j = 0; j < ny+1; ++j) { + real x = corners[i][j][0].pt.x; + real y = corners[i][j][0].pt.y; + real f_along_z(real t) { return f((x, y, t)); } + for (int k = 0; k < nz; ++k) { + if (zdirzeros[i][j][k] != null) continue; + evaluatedpoint start = corners[i][j][k]; + evaluatedpoint end = corners[i][j][k+1]; + if ((start.value > 0 && end.value > 0) || (start.value < 0 && end.value < 0)) + zdirzeros[i][j][k] = null; + else { + triple root = (x,y,0); + root += Z * findroot(f_along_z, start.pt.z, end.pt.z, + fa=start.value, fb=end.value); + triple normal = grad(root); + zdirzeros[i][j][k] = positionedvector(root, normal); + } + } + } + } + } + + // Fill in the grid vertices and the zeros along edges. Each cube starts at + // depth one and the depth increases each time it subdivides; maxdepth is the + // maximum subdivision depth. When a cube at maxdepth cannot be resolved to + // patches, it is left empty. + void operator init(int nx, int ny, int nz, + real f(triple), triple a, triple b, + int maxdepth = 6, bool usetriangles) { + this.nx = nx; + this.ny = ny; + this.nz = nz; + grad = nGrad(f); + this.f = f; + this.maxdepth = maxdepth; + this.usetriangles = usetriangles; + corners = make3dgrid(a, b, nx, ny, nz, f); + xdirzeros = new positionedvector[nx][ny+1][nz+1]; + ydirzeros = new positionedvector[nx+1][ny][nz+1]; + zdirzeros = new positionedvector[nx+1][ny+1][nz]; + + for (int i = 0; i <= nx; ++i) { + for (int j = 0; j <= ny; ++j) { + for (int k = 0; k <= nz; ++k) { + if (i < nx) xdirzeros[i][j][k] = null; + if (j < ny) ydirzeros[i][j][k] = null; + if (k < nz) zdirzeros[i][j][k] = null; + } + } + } + + fillzeros(); + } + + // Doubles nx, ny, and nz by halving the sizes of the cubes along the x, y, and z + // directions (resulting in 8 times as many cubes). Already existing data about + // function values and zeros is copied; vertices and edges with no such pre-existing + // data are populated. + // + // Returns true if subdivide succeeded, false if it failed (because maxdepth + // was exceeded). + bool subdivide() { + if (maxdepth <= 1) { + return false; + } + --maxdepth; + triple a = corners[0][0][0]; + triple b = corners[nx][ny][nz]; + nx *= 2; + ny *= 2; + nz *= 2; + evaluatedpoint[][][] oldcorners = corners; + corners = new evaluatedpoint[nx+1][ny+1][nz+1]; + for (int i = 0; i <= nx; ++i) { + for (int j = 0; j <= ny; ++j) { + for (int k = 0; k <= nz; ++k) { + if (i % 2 == 0 && j % 2 == 0 && k % 2 == 0) { + corners[i][j][k] = oldcorners[quotient(i,2)][quotient(j,2)][quotient(k,2)]; + } else { + triple pt = (interp(a.x, b.x, i/nx), + interp(a.y, b.y, j/ny), + interp(a.z, b.z, k/nz)); + real value = f(pt); + if (value == 0) value = 1e-5; + corners[i][j][k] = evaluatedpoint(pt, value); + } + } + } + } + + positionedvector[][][] oldxdir = xdirzeros; + xdirzeros = new positionedvector[nx][ny+1][nz+1]; + for (int i = 0; i < nx; ++i) { + for (int j = 0; j < ny + 1; ++j) { + for (int k = 0; k < nz + 1; ++k) { + if (j % 2 != 0 || k % 2 != 0) { + xdirzeros[i][j][k] = null; + } else { + positionedvector zero = oldxdir[quotient(i,2)][quotient(j,2)][quotient(k,2)]; + if (zero == null) { + xdirzeros[i][j][k] = null; + continue; + } + real x = zero.position.x; + if (x > interp(a.x, b.x, i/nx) && x < interp(a.x, b.x, (i+1)/nx)) { + xdirzeros[i][j][k] = zero; + } else { + xdirzeros[i][j][k] = null; + } + } + } + } + } + + positionedvector[][][] oldydir = ydirzeros; + ydirzeros = new positionedvector[nx+1][ny][nz+1]; + for (int i = 0; i < nx+1; ++i) { + for (int j = 0; j < ny; ++j) { + for (int k = 0; k < nz + 1; ++k) { + if (i % 2 != 0 || k % 2 != 0) { + ydirzeros[i][j][k] = null; + } else { + positionedvector zero = oldydir[quotient(i,2)][quotient(j,2)][quotient(k,2)]; + if (zero == null) { + ydirzeros[i][j][k] = null; + continue; + } + real y = zero.position.y; + if (y > interp(a.y, b.y, j/ny) && y < interp(a.y, b.y, (j+1)/ny)) { + ydirzeros[i][j][k] = zero; + } else { + ydirzeros[i][j][k] = null; + } + } + } + } + } + + positionedvector[][][] oldzdir = zdirzeros; + zdirzeros = new positionedvector[nx+1][ny+1][nz]; + for (int i = 0; i < nx + 1; ++i) { + for (int j = 0; j < ny + 1; ++j) { + for (int k = 0; k < nz; ++k) { + if (i % 2 != 0 || j % 2 != 0) { + zdirzeros[i][j][k] = null; + } else { + positionedvector zero = oldzdir[quotient(i,2)][quotient(j,2)][quotient(k,2)]; + if (zero == null) { + zdirzeros[i][j][k] = null; + continue; + } + real z = zero.position.z; + if (z > interp(a.z, b.z, k/nz) && z < interp(a.z, b.z, (k+1)/nz)) { + zdirzeros[i][j][k] = zero; + } else { + zdirzeros[i][j][k] = null; + } + } + } + } + } + + fillzeros(); + return true; + } + + // Forward declaration of the draw method, which will be called by drawcube(). + patch[] draw(bool[] reportactive = null); + + // Construct the patches, assuming that we are working + // with a single cube (nx = ny = nz = 1). This method will subdivide the + // cube if necessary. The parameter reportactive should be an array of + // length 6. Setting an entry to true indicates that the surface abuts the + // corresponding face (according to the earlier enum), and thus that the + // algorithm should be sure that something is drawn in the cube sharing + // that face--even if all the vertices of that cube have the same sign. + patch[] drawcube(bool[] reportactive = null) { + // First, determine which edges (if any) actually have zeros on them. + edge[] zeroedges = new edge[0]; + positionedvector[] zeros = new positionedvector[0]; + + int currentface, nextface; + + void pushifnonnull(positionedvector v) { + if (v != null) { + zeroedges.push(edge(currentface, nextface)); + zeros.push(v); + } + } + positionedvector findzero(int face1, int face2) { + edge e = edge(face1, face2); + for (int i = 0; i < zeroedges.length; ++i) { + if (zeroedges[i] == e) return zeros[i]; + } + return null; + } + + currentface = XLOW; + nextface = YHIGH; + pushifnonnull(zdirzeros[0][1][0]); + nextface = YLOW; + pushifnonnull(zdirzeros[0][0][0]); + nextface = ZHIGH; + pushifnonnull(ydirzeros[0][0][1]); + nextface = ZLOW; + pushifnonnull(ydirzeros[0][0][0]); + + currentface = XHIGH; + nextface = YHIGH; + pushifnonnull(zdirzeros[1][1][0]); + nextface = YLOW; + pushifnonnull(zdirzeros[1][0][0]); + nextface = ZHIGH; + pushifnonnull(ydirzeros[1][0][1]); + nextface = ZLOW; + pushifnonnull(ydirzeros[1][0][0]); + + currentface = YHIGH; + nextface = ZHIGH; + pushifnonnull(xdirzeros[0][1][1]); + currentface = ZHIGH; + nextface = YLOW; + pushifnonnull(xdirzeros[0][0][1]); + currentface = YLOW; + nextface = ZLOW; + pushifnonnull(xdirzeros[0][0][0]); + currentface = ZLOW; + nextface = YHIGH; + pushifnonnull(xdirzeros[0][1][0]); + + //Now, string those edges together to make a circle. + + patch[] subdividecube() { + if (!subdivide()) { + return new patch[0]; + } + return draw(reportactive); + } + if (zeroedges.length < 3) { + return subdividecube(); + } + int[] faceorder = makecircle(zeroedges); + if (alias(faceorder,null)) { + return subdividecube(); + } + positionedvector[] patchcorners = new positionedvector[0]; + for (int i = 0; i < faceorder.length; ++i) { + patchcorners.push(findzero(faceorder[i], faceorder[i+1])); + } + patchcorners.cyclic = true; + + //Now, produce the cyclic path around the edges. + path3 edgecycle; + for (int i = 0; i < faceorder.length; ++i) { + path3 currentpath = pathinface(patchcorners[i], patchcorners[i+1], + faceorder[i+1], faceorder[i], + faceorder[i+2]); + triple testpoint = point(currentpath, 0.5); + if (!checkpt(testpoint, f, grad, corners[0][0][0], corners[1][1][1])) { + return subdividecube(); + } + + edgecycle = edgecycle & currentpath; + } + edgecycle = edgecycle & cycle; + + + { // Ensure the outward normals are pointing in the same direction as the gradient. + triple tangentin = patchcorners[0].position - precontrol(edgecycle, 0); + triple tangentout = postcontrol(edgecycle, 0) - patchcorners[0].position; + triple normal = cross(tangentin, tangentout); + if (dot(normal, patchcorners[0].direction) < 0) { + edgecycle = reverse(edgecycle); + patchcorners = patchcorners[-sequence(patchcorners.length)]; + patchcorners.cyclic = true; + } + } + + patch[] toreturn = quadpatches(edgecycle, patchcorners, f, grad, + corners[0][0][0], corners[1][1][1], usetriangles); + if (alias(toreturn, null)) return subdividecube(); + return toreturn; + } + + // Extracts the specified cube as a gridwithzeros object with + // nx = ny = nz = 1. + gridwithzeros getcube(int i, int j, int k) { + gridwithzeros cube = new gridwithzeros; + cube.grad = grad; + cube.f = f; + cube.nx = 1; + cube.ny = 1; + cube.nz = 1; + cube.maxdepth = maxdepth; + cube.usetriangles = usetriangles; + cube.corners = slice(corners,i,i+2,j,j+2,k,k+2); + cube.xdirzeros = slice(xdirzeros,i,i+1,j,j+2,k,k+2); + cube.ydirzeros = slice(ydirzeros,i,i+2,j,j+1,k,k+2); + cube.zdirzeros = slice(zdirzeros,i,i+2,j,j+2,k,k+1); + return cube; + } + + // Returns an array of patches representing the surface. + // The parameter reportactive should be an array of + // length 6. Setting an entry to true indicates that the surface abuts the + // corresponding face of the cube that bounds the entire grid. + // + // If reportactive == null, it is assumed that this is a top-level call; + // a dot is printed to stdout for each cube drawn as a very rough + // progress indicator. + // + // If reportactive != null, then it is assumed that the caller had a strong + // reason to believe that this grid contains a part of the surface; the + // grid will subdivide all the way to maxdepth if necessary to find points + // on the surface. + draw = new patch[](bool[] reportactive = null) { + if (alias(reportactive, null)) progress(true); + // A list of all the patches not already drawn but known + // to contain part of the surface. This "queue" is + // actually implemented as stack for simplicity, since + // it does not make any difference. In a multi-threaded + // version of the algorithm, a queue (shared across all threads) + // would make more sense than a stack. + triple[] queue = new triple[0]; + bool[][][] enqueued = new bool[nx][ny][nz]; + for (int i = 0; i < enqueued.length; ++i) { + for (int j = 0; j < enqueued[i].length; ++j) { + for (int k = 0; k < enqueued[i][j].length; ++k) { + enqueued[i][j][k] = false; + } + } + } + + void enqueue(int i, int j, int k) { + if (i >= 0 && i < nx + && j >= 0 && j < ny + && k >= 0 && k < nz + && !enqueued[i][j][k]) { + queue.push((i,j,k)); + enqueued[i][j][k] = true; + } + if (!alias(reportactive, null)) { + if (i < 0) reportactive[XLOW] = true; + if (i >= nx) reportactive[XHIGH] = true; + if (j < 0) reportactive[YLOW] = true; + if (j >= ny) reportactive[YHIGH] = true; + if (k < 0) reportactive[ZLOW] = true; + if (k >= nz) reportactive[ZHIGH] = true; + } + } + + for (int i = 0; i < nx+1; ++i) { + for (int j = 0; j < ny+1; ++j) { + for (int k = 0; k < nz+1; ++k) { + if (i < nx && xdirzeros[i][j][k] != null) { + for (int jj = j-1; jj <= j; ++jj) + for (int kk = k-1; kk <= k; ++kk) + enqueue(i, jj, kk); + } + if (j < ny && ydirzeros[i][j][k] != null) { + for (int ii = i-1; ii <= i; ++ii) + for (int kk = k-1; kk <= k; ++kk) + enqueue(ii, j, kk); + } + if (k < nz && zdirzeros[i][j][k] != null) { + for (int ii = i-1; ii <= i; ++ii) + for (int jj = j-1; jj <= j; ++jj) + enqueue(ii, jj, k); + } + } + } + } + + if (!alias(reportactive, null) && queue.length == 0) { + if (subdivide()) return draw(reportactive); + } + + patch[] surface = new patch[0]; + + while (queue.length > 0) { + triple coord = queue.pop(); + int i = floor(coord.x); + int j = floor(coord.y); + int k = floor(coord.z); + bool[] reportface = array(6, false); + patch[] toappend = getcube(i,j,k).drawcube(reportface); + if (reportface[XLOW]) enqueue(i-1,j,k); + if (reportface[XHIGH]) enqueue(i+1,j,k); + if (reportface[YLOW]) enqueue(i,j-1,k); + if (reportface[YHIGH]) enqueue(i,j+1,k); + if (reportface[ZLOW]) enqueue(i,j,k-1); + if (reportface[ZHIGH]) enqueue(i,j,k+1); + surface.append(toappend); + if (alias(reportactive, null)) progress(); + } + if (alias(reportactive, null)) progress(false); + return surface; + }; +} + +// The external interface of this whole module. Accepts exactly one +// function (throws an error if two or zero functions are specified). +// The function should be differentiable. (Whatever you do, do not +// pass in an indicator function!) Ideally, the zero locus of the +// function should be smooth; singularities will significantly slow +// down the algorithm and potentially give bad results. +// +// Returns a plot of the zero locus of the function within the +// rectangular solid with opposite corners at a and b. +// +// Additional parameters: +// n - the number of initial segments in each of the x, y, z directions. +// overlapedges - if true, the patches of the surface are slightly enlarged +// to compensate for an artifact in which the viewer can see through the +// boundary between patches. (Some of this may actually be a result of +// edges not lining up perfectly, but I'm fairly sure a lot of it arises +// purely as a rendering artifact.) +// nx - override n in the x direction +// ny - override n in the y direction +// nz - override n in the z direction +// maxdepth - the maximum depth to which the algorithm will subdivide in +// an effort to find patches that closely approximate the true surface. +surface implicitsurface(real f(triple) = null, real ff(real,real,real) = null, + triple a, triple b, + int n = nmesh, + bool keyword overlapedges = false, + int keyword nx=n, int keyword ny=n, + int keyword nz=n, + int keyword maxdepth = 8, + bool keyword usetriangles=true) { + if (f == null && ff == null) + abort("implicitsurface called without specifying a function."); + if (f != null && ff != null) + abort("Only specify one function when calling implicitsurface."); + if (f == null) f = new real(triple w) { return ff(w.x, w.y, w.z); }; + gridwithzeros grid = gridwithzeros(nx, ny, nz, f, a, b, maxdepth=maxdepth, + usetriangles=usetriangles); + patch[] patches = grid.draw(); + if (overlapedges) { + for (int i = 0; i < patches.length; ++i) { + triple center = (patches[i].triangular ? + patches[i].point(1/3, 1/3) : patches[i].point(1/2,1/2)); + transform3 T=shift(center) * scale3(1.03) * shift(-center); + patches[i] = T * patches[i]; + } + } + return surface(...patches); +} diff --git a/Build/source/utils/asymptote/base/solids.asy b/Build/source/utils/asymptote/base/solids.asy new file mode 100644 index 00000000000..72110d6f7aa --- /dev/null +++ b/Build/source/utils/asymptote/base/solids.asy @@ -0,0 +1,400 @@ +import graph3; + +pen defaultbackpen=linetype(new real[] {4,4},4,scale=false); + +// A solid geometry package. + +// Try to find a bounding tangent line between two paths. +real[] tangent(path p, path q, bool side) +{ + static real fuzz=1.0e-5; + + if((cyclic(p) && inside(p,point(q,0)) || + cyclic(q) && inside(q,point(p,0))) && + intersect(p,q,fuzz).length == 0) return new real[]; + + for(int i=0; i < 100; ++i) { + real ta=side ? mintimes(p)[1] : maxtimes(p)[1]; + real tb=side ? mintimes(q)[1] : maxtimes(q)[1]; + pair a=point(p,ta); + pair b=point(q,tb); + real angle=angle(b-a,warn=false); + if(abs(angle) <= sqrtEpsilon || abs(abs(0.5*angle)-pi) <= sqrtEpsilon) + return new real[] {ta,tb}; + transform t=rotate(-degrees(angle)); + p=t*p; + q=t*q; + } + return new real[]; +} + +path line(path p, path q, real[] t) +{ + return point(p,t[0])--point(q,t[1]); +} + +// Return the projection of a generalized cylinder of height h constructed +// from area base in the XY plane and aligned with axis. +path[] cylinder(path3 base, real h, triple axis=Z, projection P) +{ + base=rotate(-colatitude(axis),cross(axis,Z))*base; + path3 top=shift(h*axis)*base; + path Base=project(base,P); + path Top=project(top,P); + real[] t1=tangent(Base,Top,true); + real[] t2=tangent(Base,Top,false); + path p=subpath(Base,t1[0]/P.ninterpolate,t2[0]/P.ninterpolate); + path q=subpath(Base,t2[0]/P.ninterpolate,t1[0]/P.ninterpolate); + return Base^^Top^^line(Base,Top,t1)^^line(Base,Top,t2); +} + +// The three-dimensional "wireframe" used to visualize a volume of revolution +struct skeleton { + struct curve { + path3[] front; + path3[] back; + } + // transverse skeleton (perpendicular to axis of revolution) + curve transverse; + // longitudinal skeleton (parallel to axis of revolution) + curve longitudinal; +} + +// A surface of revolution generated by rotating a planar path3 g +// from angle1 to angle2 about c--c+axis. +struct revolution { + triple c; + path3 g; + triple axis; + real angle1,angle2; + triple M; + triple m; + + static real epsilon=10*sqrtEpsilon; + + void operator init(triple c=O, path3 g, triple axis=Z, real angle1=0, + real angle2=360) { + this.c=c; + this.g=g; + this.axis=unit(axis); + this.angle1=angle1; + this.angle2=angle2; + M=max(g); + m=min(g); + } + + + revolution copy() { + return revolution(c,g,axis,angle1,angle2); + } + + triple vertex(int i, real j) { + triple v=point(g,i); + triple center=c+dot(v-c,axis)*axis; + triple perp=v-center; + triple normal=cross(axis,perp); + return center+Cos(j)*perp+Sin(j)*normal; + } + + // Construct the surface of rotation generated by rotating g + // from angle1 to angle2 sampled n times about the line c--c+axis. + // An optional surface pen color(int i, real j) may be specified + // to override the color at vertex(i,j). + surface surface(int n=nslice, pen color(int i, real j)=null) { + return surface(c,g,axis,n,angle1,angle2,color); + } + + path3 slice(real position, int n=nCircle) { + triple v=point(g,position); + triple center=c+dot(v-c,axis)*axis; + triple perp=v-center; + if(abs(perp) <= epsilon*max(abs(m),abs(M))) return center; + triple v1=center+rotate(angle1,axis)*perp; + triple v2=center+rotate(angle2,axis)*perp; + path3 p=Arc(center,v1,v2,axis,n); + return (angle2-angle1) % 360 == 0 ? p&cycle : p; + } + + // add transverse slice to skeleton s; + void transverse(skeleton s, real t, int n=nslice, projection P) { + skeleton.curve s=s.transverse; + path3 S=slice(t,n); + int L=length(g); + real midtime=0.5*L; + real sign=sgn(dot(axis,P.camera-c))*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)-P.camera) <= abs(midpoint(p2)-P.camera)) { + s.front.push(p1); + if(cyclic(S)) + s.back.push(p2 & P2); + else { + s.back.push(p2); + s.back.push(P2); + } + } else { + if(cyclic(S)) + s.front.push(p2 & P2); + else { + s.front.push(p2); + s.front.push(P2); + } + s.back.push(p1); + } + } else { + if((t <= midtime && sign < 0) || (t >= midtime && sign > 0)) + s.front.push(S); + else + s.back.push(S); + } + } + } + + // add m evenly spaced transverse slices to skeleton s + void transverse(skeleton s, int m=0, int n=nslice, projection P) { + if(m == 0) { + int N=size(g); + for(int i=0; i < N; ++i) + transverse(s,(real) i,n,P); + } else if(m == 1) + transverse(s,reltime(g,0.5),n,P); + else { + real factor=1/(m-1); + for(int i=0; i < m; ++i) + transverse(s,reltime(g,i*factor),n,P); + } + } + + // return approximate silhouette based on m evenly spaced transverse slices; + // must be recomputed if camera is adjusted + path3[] silhouette(int m=64, projection P=currentprojection) { + if(is3D()) + warning("2Dsilhouette", + "silhouette routine is intended only for 2d projections"); + path3 G,H; + int N=size(g); + int M=(m == 0) ? N : m; + real factor=m == 1 ? 0 : 1/(m-1); + int n=nslice; + + real tfirst=-1; + real tlast; + for(int i=0; i < M; ++i) { + real t=(m == 0) ? i : reltime(g,i*factor); + path3 S=slice(t,n); + 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; + real sign=sgn(dot(axis,P.camera-c))*sgn(dot(axis,dir(g,midtime))); + + skeleton sfirst; + transverse(sfirst,tfirst,n,P); + triple delta=this.M-this.m; + path3 cap; + if(dot(delta,axis) == 0 || (tfirst <= epsilon && sign < 0)) { + cap=sfirst.transverse.front[0]; + } else { + if(sign > 0) { + if(sfirst.transverse.front.length > 0) + G=reverse(sfirst.transverse.front[0])..G; + } else { + if(sfirst.transverse.back.length > 0) + G=sfirst.transverse.back[0]..G; + } + } + + skeleton slast; + transverse(slast,tlast,n,P); + if(dot(delta,axis) == 0 || (tlast >= L-epsilon && sign > 0)) { + cap=slast.transverse.front[0]; + } else { + if(sign > 0) { + if(slast.transverse.back.length > 0) + H=reverse(slast.transverse.back[0])..H; + } else { + if(slast.transverse.front.length > 0) + H=slast.transverse.front[0]..H; + } + } + + return size(cap) == 0 ? G^^H : G^^H^^cap; + } + + // add longitudinal curves to skeleton; + void longitudinal(skeleton s, int n=nslice, projection P) { + real t, d=0; + // Find a point on g of maximal distance from the axis. + int N=size(g); + for(int i=0; i < N; ++i) { + triple v=point(g,i); + triple center=c+dot(v-c,axis)*axis; + real r=abs(v-center); + if(r > d) { + t=i; + d=r; + } + } + path3 S=slice(t,n); + path3 Sm=slice(t+epsilon,n); + path3 Sp=slice(t-epsilon,n); + path sp=project(Sp,P); + path sm=project(Sm,P); + real[] t1=tangent(sp,sm,true); + real[] t2=tangent(sp,sm,false); + transform3 T=transpose(align(axis)); + real Longitude(triple v) {return longitude(T*(v-c),warn=false);} + real ref=Longitude(point(g,t)); + real angle(real t) {return Longitude(point(S,t/P.ninterpolate))-ref;} + 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 && + (length(p2) == 0 || + abs(midpoint(p1)-P.camera) <= abs(midpoint(p2)-P.camera))) { + s.longitudinal.front.push(p1); + s.longitudinal.back.push(p2); + } else { + s.longitudinal.back.push(p1); + s.longitudinal.front.push(p2); + } + } + } + push(t1); + push(t2); + } + + skeleton skeleton(int m=0, int n=nslice, projection P) { + skeleton s; + transverse(s,m,n,P); + longitudinal(s,n,P); + return s; + } +} + +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); +} + +surface surface(revolution r, int n=nslice, pen color(int i, real j)=null) +{ + return r.surface(n,color); +} + +// Draw on picture pic the skeleton of the surface of revolution r. +// Draw the front portion of each of the m transverse slices with pen p and +// the back portion with pen backpen. Rotational arcs are based on +// n-point approximations to the unit circle. +void draw(picture pic=currentpicture, revolution r, int m=0, int n=nslice, + pen frontpen=currentpen, pen backpen=frontpen, + pen longitudinalpen=frontpen, pen longitudinalbackpen=backpen, + light light=currentlight, string name="", + render render=defaultrender, projection P=currentprojection) +{ + if(is3D()) { + pen thin=thin(); + void drawskeleton(frame f, transform3 t, projection P) { + skeleton s=r.skeleton(m,n,inverse(t)*P); + if(frontpen != nullpen) { + draw(f,t*s.transverse.back,thin+defaultbackpen+backpen,light); + draw(f,t*s.transverse.front,thin+frontpen,light); + } + if(longitudinalpen != nullpen) { + draw(f,t*s.longitudinal.back,thin+defaultbackpen+longitudinalbackpen, + light); + draw(f,t*s.longitudinal.front,thin+longitudinalpen,light); + } + } + + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(pic,name == "" ? "skeleton" : name,render); + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + drawskeleton(f,t,P); + if(pic != null) + pic.addBox(min(f,P),max(f,P),min(frontpen),max(frontpen)); + }); + frame f; + drawskeleton(f,identity4,P); + pic.addBox(min3(f),max3(f)); + if(group) + endgroup3(pic); + } else { + skeleton s=r.skeleton(m,n,P); + if(frontpen != nullpen) { + draw(pic,s.transverse.back,defaultbackpen+backpen,light); + draw(pic,s.transverse.front,frontpen,light); + } + if(longitudinalpen != nullpen) { + draw(pic,s.longitudinal.back,defaultbackpen+longitudinalbackpen, + light); + draw(pic,s.longitudinal.front,longitudinalpen,light); + } + } +} + +// Return a right circular cylinder of height h in the direction of axis +// based on a circle centered at c with radius r. +// Note: unitcylinder provides a smoother and more efficient representation. +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 representation. +revolution sphere(triple c=O, real r, int n=nslice) +{ + return revolution(c,Arc(c,r,180-sqrtEpsilon,0,sqrtEpsilon,0,Y,n),Z); +} diff --git a/Build/source/utils/asymptote/base/stats.asy b/Build/source/utils/asymptote/base/stats.asy new file mode 100644 index 00000000000..be9efdfa429 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/syzygy.asy b/Build/source/utils/asymptote/base/syzygy.asy new file mode 100644 index 00000000000..93889b252e5 --- /dev/null +++ b/Build/source/utils/asymptote/base/syzygy.asy @@ -0,0 +1,926 @@ +/***** syzygy.asy {{{1 + * Andy Hammerlindl 2006/12/02 + * + * Automates the drawing of braids, relations, and syzygies, along with the + * corresponding equations. + * + * See + * http://katlas.math.toronto.edu/drorbn/index.php?title=06-1350/Syzygies_in_Asymptote + * For more information. + *****/ +struct Component { // {{{1 + // The number of strings coming in or out of the component. + int in; + int out; + + // Which 'out' string each 'in' string is connected to. For deriving + // equations. + int[] connections; + + string symbol; // For pullback notation. + string lsym; // For linear equations. + string codename; // For Mathematica code. + + guide[] draw(picture pic, guide[] ins); +} + +// Utility functions {{{1 +pair[] endpoints(guide[] a) { + pair[] z; + for (int i=0; i<a.length; ++i) + z.push(endpoint(a[i])); + return z; +} + +pair min(pair[] z) { + pair m=(infinity, infinity); + for (int i=0; i<z.length; ++i) { + if (z[i].x < m.x) + m=(z[i].x,m.y); + if (z[i].y < m.y) + m=(m.x,z[i].y); + } + return m; +} + +pair max(pair[] z) { + pair M=(-infinity, -infinity); + for (int i=0; i<z.length; ++i) { + if (z[i].x > M.x) + M=(z[i].x,M.y); + if (z[i].y > M.y) + M=(M.x,z[i].y); + } + return M; +} + +// Component Definitions {{{1 +real hwratio=1.4; +real gapfactor=6; + +Component bp=new Component; +bp.in=2; bp.out=2; +bp.connections=new int[] {1,0}; +bp.symbol="B^+"; bp.lsym="b^+"; bp.codename="bp"; +bp.draw=new guide[] (picture pic, guide[] ins) { + pair[] z=endpoints(ins); + pair m=min(z), M=max(z); + real w=M.x-m.x, h=hwratio*w; + pair centre=(0.5(m.x+M.x),M.y+h/2); + + /* + return new guide[] {ins[1]..centre{NW}..z[0]+h*N, + ins[0]..centre{NE}..z[1]+h*N}; + */ + + real offset=gapfactor*linewidth(currentpen); + draw(pic, ins[1]..(centre-offset*NW){NW}); + return new guide[] {(centre+offset*NW){NW}..z[0]+h*N, + ins[0]..centre{NE}..z[1]+h*N}; +}; + +Component bm=new Component; +bm.in=2; bm.out=2; +bm.connections=new int[] {1,0}; +bm.symbol="B^-"; bm.lsym="b^-"; bm.codename="bm"; +bm.draw=new guide[] (picture pic, guide[] ins) { + pair[] z=endpoints(ins); + pair m=min(z), M=max(z); + real w=M.x-m.x, h=hwratio*w; + pair centre=(0.5(m.x+M.x),M.y+h/2); + + /* + return new guide[] {ins[1]..centre{NW}..z[0]+h*N, + ins[0]..centre{NE}..z[1]+h*N}; + */ + + real offset=gapfactor*linewidth(currentpen); + draw(pic, ins[0]..(centre-offset*NE){NE}); + return new guide[] {ins[1]..centre{NW}..z[0]+h*N, + (centre+offset*NE){NE}..z[1]+h*N}; +}; + +Component phi=new Component; +phi.in=2; phi.out=1; +phi.connections=new int[] {0,0}; +phi.symbol="\Phi"; phi.lsym="\phi"; phi.codename="phi"; +phi.draw=new guide[] (picture pic, guide[] ins) { + pair[] z=endpoints(ins); + pair m=min(z), M=max(z); + real w=M.x-m.x, h=hwratio*w; + pair centre=(0.5(m.x+M.x),M.y+h/2); + + + //real offset=4*linewidth(currentpen); + draw(pic, ins[0]..centre{NE}); + draw(pic, ins[1]..centre{NW}); + draw(pic, centre,linewidth(5*linewidth(currentpen))); + dot(pic, centre); + return new guide[] {centre..centre+0.5h*N}; +}; + +Component wye=new Component; +wye.in=1; wye.out=2; +wye.connections=null; // TODO: Fix this! +wye.symbol="Y"; wye.lsym="y"; wye.codename="wye"; +wye.draw=new guide[] (picture pic, guide[] ins) { + pair z=endpoint(ins[0]); + real w=10, h=hwratio*w; // The 10 is a guess here, and may produce badness. + pair centre=(z.x,z.y+h/2); + + + draw(pic, ins[0]..centre); + draw(pic, centre,linewidth(5*linewidth(currentpen))); + return new guide[] {centre{NW}..centre+(-0.5w,0.5h), + centre{NE}..centre+(0.5w,0.5h)}; +}; + + +struct Braid { // {{{1 + // Members {{{2 + // Number of lines initially. + int n; + + struct Placement { + Component c; + int place; + + Placement copy() { + Placement p=new Placement; + p.c=this.c; p.place=this.place; + return p; + } + } + Placement[] places; + + void add(Component c, int place) { + Placement p=new Placement; + p.c=c; p.place=place; + places.push(p); + } + + void add(Braid sub, int place) { + for (int i=0; i<sub.places.length; ++i) + add(sub.places[i].c,sub.places[i].place+place); + } + + // Drawing {{{2 + guide[] drawStep(picture pic, Placement p, guide[] ins) { + int i=0,j=0; + + // Draw the component. + Component c=p.c; + //write("drawing "+c.symbol+" at place "+(string)p.place); + guide[] couts=c.draw(pic, ins[sequence(c.in)+p.place]); + + pair M=max(endpoints(couts)); + + // Extend lines not in the component. + guide[] outs; + pair[] z=endpoints(ins); + while (i<p.place) { + outs.push(ins[i]..(z[i].x,M.y)); + ++i; + } + + outs.append(couts); + i+=c.in; + + while (i<ins.length) { + outs.push(ins[i]..(z[i].x,M.y)); + ++i; + } + + return outs; + } + + void drawEnd(picture pic, guide[] ins, real minheight=0) { + pair[] z=endpoints(ins); + for (int i=0; i<ins.length; ++i) { + draw(pic, z[i].y >= minheight ? ins[i] : ins[i]..(z[i].x,minheight)); + } + } + + void draw(picture pic, guide[] ins, real minheight=0) { + int steps=places.length; + + guide[] nodes=ins; + for (int i=0; i<steps; ++i) { + Placement p=places[i]; + nodes=drawStep(pic, places[i], nodes); + } + + drawEnd(pic, nodes, minheight); + } + + void draw(picture pic=currentpicture, real spacing=15, + real minheight=2hwratio*spacing) { + pair[] ins; + for (int i=0; i<n; ++i) + ins.push((spacing*i,0)); + + draw(pic, ins, minheight); + } + + // Utilities {{{2 + int in() { + return n; + } + int out() { + int steps=places.length; + int num=n; // The number of nodes at this step. + + for (int i=0; i<steps; ++i) { + Placement p=places[i]; + int nextNum=num-p.c.in+p.c.out; + num=nextNum; + } + return num; + } + + // Deep copy of a braid. + Braid copy() { + Braid b=new Braid; + b.n=this.n; + for (int i=0; i<this.places.length; ++i) + b.add(this.places[i].c,this.places[i].place); + return b; + } + + // Matching {{{2 + // Tests if a component p can be swapped with a component q which is assumed + // to be directly above it. + static bool swapable(Placement p, Placement q) { + return p.place + p.c.out <= q.place || // p is left of q or + q.place + q.c.in <= p.place; // q is left of p + } + + // Creates a new braid with a transposition of two components. + Braid swap(int i, int j) { + if (i>j) + return swap(j,i); + else { + assert(j==i+1); assert(swapable(places[i],places[j])); + + Placement p=places[i].copy(); + Placement q=places[j].copy(); + /*write("swap:"); + write("p originally at " + (string)p.place); + write("q originally at " + (string)q.place); + write("p.c.in: " + (string)p.c.in + " p.c.out: " + (string)p.c.out); + write("q.c.in: " + (string)q.c.in + " q.c.out: " + (string)q.c.out);*/ + if (q.place + q.c.in <= p.place) + // q is left of p - adjust for q renumbering strings. + p.place+=q.c.out-q.c.in; + else if (p.place + p.c.out <= q.place) + // q is right of p - adjust for p renumbering strings. + q.place+=p.c.in-p.c.out; + else + // q is directly on top of p + assert(false, "swapable"); + + /*write("q now at " + (string)q.place); + write("p now at " + (string)p.place);*/ + + Braid b=this.copy(); + b.places[i]=q; + b.places[j]=p; + return b; + } + } + + // Tests if the component at index 'start' can be moved to index 'end' + // without interfering with other components. + bool moveable(int start, int end) { + assert(start<places.length); assert(end<places.length); + if (start==end) + return true; + else if (end<start) + return moveable(end,start); + else { + assert(start<end); + Placement p=places[start].copy(); + for (int step=start; step<end; ++step) { + Placement q=places[step+1]; + if (q.place + q.c.in <= p.place) + // q is left of p - adjust for q renumbering strings. + p.place+=q.c.out-q.c.in; + else if (p.place + p.c.out <= q.place) + // q is right of p - nothing to do. + continue; + else + // q is directly on top of p + return false; + } + return true; + } + } + + bool matchComponent(Braid sub, int subindex, int place, int step) { + int i=subindex; + return sub.places[i].c == this.places[step].c && + sub.places[i].place + place == this.places[step].place; + } + + // Returns true if a sub-braid occurs within the one at the specified + // coordinates with no component occuring anywhere inbetween. + bool exactMatch(Braid sub, int place, int step) { + for (int i=0; i<sub.places.length; ++i) { + if (!matchComponent(sub, i, place, i+step)) { + write("match failed at iteration: ", i); + return false; + } + } + return true; + } + + /* + bool findSubsequence(Braid sub, int place, int size, int[] acc) { + // If we've matched all the components, we've won. + if (acc.length >= sub.places.length) + return true; + + // The next component to match. + Placement p=sub.places[acc.length]; + + // Start looking immediately after the last match. + for (int step=acc[acc.length-1]+1; step<this.places.length; ++step) { + Placement q=this.places[step]; + */ + + bool tryMatch(Braid sub, int place, int size, int[] acc) { + // If we've matched all the components, we've won. + if (acc.length >= sub.places.length) + return true; + + // The next component to match. + Placement p=sub.places[acc.length]; + + // Start looking immediately after the last match. + for (int step=acc[acc.length-1]+1; step<this.places.length; ++step) { + Placement q=this.places[step]; + // Check if the next component is in the set of strings used by the + // subbraid. + if (q.place + q.c.in > place && q.place < place + size) { + // It's in the window, so it must match the next component in the + // subbraid. + if (p.c==q.c && p.place+place==q.place) { + // A match - go on to the next component. + acc.push(step); + return tryMatch(sub, place, size, acc); // TODO: Adjust place/size. + } + else + return false; + } + + // TODO: Adjust place and size. + } + + // We've run out of components to match. + return false; + } + + + // This attempts to find a subbraid within the braid. It allows other + // components to be interspersed with the components of the subbraid so long + // as they don't occur on the same string as the ones the subbraid lies on. + // Returns null on failure. + int[] match(Braid sub, int place) { + for (int i=0; i<=this.places.length-sub.places.length; ++i) { + // Find where the first component of the subbraid matches and try to + // match the rest of the braid starting from there. + if (matchComponent(sub, 0, place, i)) { + int[] result; + result.push(i); + if (tryMatch(sub,place,sub.n,result)) + return result; + } + } + return null; + } + + // Equations {{{2 + // Returns the string that 'place' moves to when going through the section + // with Placement p. + static int advancePast(Placement p, int place) { + // If it's to the left of the component, it is unaffected. + return place<p.place ? place : + // If it's to the right of the component, adjust the numbering due + // to the change of the number of strings in the component. + p.place+p.c.in <= place ? place - p.c.in + p.c.out : + // If it's in the component, ask the component to do the work. + p.place + p.c.connections[place-p.place]; + } + + // Adjust the place (at step 0) to the step given, to find which string it is + // on in that part of the diagram. + int advanceToStep(int step, int place) { + assert(place>=0 && place<n); + assert(step>=0 && step<places.length); + + for (int i=0; i<step; ++i) + place=advancePast(places[i], place); + + return place; + } + + int pullbackWindowPlace(int step, int place, + int w_place, int w_size) { + place=advanceToStep(step,place); + return place < w_place ? 1 : // The shielding. + w_place + w_size <= place ? 0 : // The string doesn't touch it. + place-w_place+2; + } + + int pullbackPlace(int step, int place) { + // Move to the right step. + //write("advance: ", step, place, advanceToStep(step,place)); + //place=advanceToStep(step,place); + Placement p=places[step]; + return pullbackWindowPlace(step,place, p.place, p.c.in); + /*return place < p.place ? 1 : // The shielding. + p.place + p.c.in <= place ? 0 : // The string doesn't touch it. + place-p.place+2;*/ + } + + int[] pullbackWindow(int step, int w_place, int w_size) { + int[] a={1}; + for (int place=0; place<n; ++place) + a.push(pullbackWindowPlace(step, place, w_place, w_size)); + return a; + } + + int[] pullback(int step) { + Placement p=places[step]; + return pullbackWindow(step, p.place, p.c.in); + /*int[] a={1}; + for (int place=0; place<n; ++place) + a.push(pullbackPlace(step, place)); + return a;*/ + } + + string stepToFormula(int step) { + // Determine the pullbacks. + string s="(1"; + for (int place=0; place<n; ++place) + //write("pullback: ", step, place, pullbackString(step,place)); + s+=(string)pullbackPlace(step, place); + s+=")^\star "+places[step].c.symbol; + return s; + } + + // Write it as a formula with pullback notation. + string toFormula() { + if (places.length==0) + return "1"; + else { + string s; + for (int step=0; step<places.length; ++step) { + if (step>0) + s+=" "; + s+=stepToFormula(step); + } + return s; + } + } + + string windowToLinear(int step, int w_place, int w_size) { + int[] a=pullbackWindow(step, w_place, w_size); + string s="("; + for (int arg=1; arg<=w_size+1; ++arg) { + if (arg>1) + s+=","; + bool first=true; + for (int var=0; var<a.length; ++var) { + if (a[var]==arg) { + if (first) + first=false; + else + s+="+"; + s+="x_"+(string)(var+1); + } + } + } + return s+")"; + } + + string windowToCode(int step, int w_place, int w_size) { + int[] a=pullbackWindow(step, w_place, w_size); + string s="["; + for (int arg=1; arg<=w_size+1; ++arg) { + if (arg>1) + s+=", "; + bool first=true; + for (int var=0; var<a.length; ++var) { + if (a[var]==arg) { + if (first) + first=false; + else + s+=" + "; + s+="x"+(string)(var+1); + } + } + } + return s+"]"; + } + + string stepToLinear(int step) { + //int[] a=pullback(step); + Placement p=places[step]; + return p.c.lsym+windowToLinear(step, p.place, p.c.in); + + /*string s=p.c.lsym+"("; + for (int arg=1; arg<=p.c.in+1; ++arg) { + if (arg>1) + s+=","; + bool first=true; + for (int var=0; var<a.length; ++var) { + if (a[var]==arg) { + if (first) + first=false; + else + s+="+"; + s+="x_"+(string)(var+1); + } + } + } + return s+")";*/ + } + + string stepToCode(int step) { + Placement p=places[step]; + return p.c.codename+windowToCode(step, p.place, p.c.in); + } + + string toLinear(bool subtract=false) { + if (places.length==0) + return subtract ? "0" : ""; // or "1" ? + else { + string s = subtract ? " - " : ""; + for (int step=0; step<places.length; ++step) { + if (step>0) + s+= subtract ? " - " : " + "; + s+=stepToLinear(step); + } + return s; + } + } + + string toCode(bool subtract=false) { + if (places.length==0) + return subtract ? "0" : ""; // or "1" ? + else { + string s = subtract ? " - " : ""; + for (int step=0; step<places.length; ++step) { + if (step>0) + s+= subtract ? " - " : " + "; + s+=stepToCode(step); + } + return s; + } + } +} + +struct Relation { // {{{1 + Braid lhs, rhs; + + string lsym, codename; + bool inverted=false; + + string toFormula() { + return lhs.toFormula() + " = " + rhs.toFormula(); + } + + string linearName() { + assert(lhs.n==rhs.n); + assert(lsym!=""); + + string s=(inverted ? "-" : "") + lsym+"("; + for (int i=1; i<=lhs.n+1; ++i) { + if (i>1) + s+=","; + s+="x_"+(string)i; + } + return s+")"; + } + + string fullCodeName() { + assert(lhs.n==rhs.n); + assert(codename!=""); + + string s=(inverted ? "minus" : "") + codename+"["; + for (int i=1; i<=lhs.n+1; ++i) { + if (i>1) + s+=", "; + s+="x"+(string)i+"_"; + } + return s+"]"; + } + + string toLinear() { + return linearName() + " = " + lhs.toLinear() + rhs.toLinear(true); + } + + string toCode() { + return fullCodeName() + " :> " + lhs.toCode() + rhs.toCode(true); + } + + void draw(picture pic=currentpicture) { + picture left; lhs.draw(left); + frame l=left.fit(); + picture right; rhs.draw(right); + frame r=right.fit(); + + real xpad=30; + + add(pic, l); + label(pic, "=", (max(l).x + 0.5xpad, 0.25(max(l).y+max(r).y))); + add(pic, r, (max(l).x+xpad,0)); + } +} + +Relation operator- (Relation r) { + Relation opposite; + opposite.lhs=r.rhs; + opposite.rhs=r.lhs; + opposite.lsym=r.lsym; + opposite.codename=r.codename; + opposite.inverted=!r.inverted; + return opposite; +} + + +Braid apply(Relation r, Braid b, int step, int place) { + bool valid=b.exactMatch(r.lhs,place,step); + if (valid) { + Braid result=new Braid; + result.n=b.n; + for (int i=0; i<step; ++i) + result.places.push(b.places[i]); + result.add(r.rhs,place); + for (int i=step+r.lhs.places.length; i<b.places.length; ++i) + result.places.push(b.places[i]); + return result; + } + else { + write("Invalid match!"); + return null; + } +} + +// Tableau {{{1 + +// Draw a number of frames in a nice circular arrangement. +picture tableau(frame[] cards, bool number=false) { + int n=cards.length; + + // Calculate the max height and width of the frames (assuming min(f)=(0,0)). + pair M=(0,0); + for (int i=0; i<n; ++i) { + pair z=max(cards[i]); + if (z.x > M.x) + M=(z.x,M.y); + if (z.y > M.y) + M=(M.x,z.y); + } + + picture pic; + real xpad=2.0, ypad=1.3; + void place(int index, real row, real column) { + pair z=((M.x*xpad)*column,(M.y*ypad)*row); + add(pic, cards[index], z); + if (number) { + label(pic,(string)index, z+(0.5M.x,0), S); + } + } + + // Handle small collections. + if (n<=4) { + for (int i=0; i<n; ++i) + place(i,0,i); + } + else { + int rows=quotient(n-1,2), columns=3; + + // Add the top middle card. + place(0,rows-1,1); + + // place cards down the right side. + for (int i=1; i<rows; ++i) + place(i, rows-i,2); + + // place cards at the bottom. + if (n%2==0) { + place(rows,0,2); + place(rows+1,0,1); + place(rows+2,0,0); + } + else { + place(rows,0,1.5); + place(rows+1,0,0.5); + } + + // place cards up the left side. + for (int i=1; i<rows; ++i) + place(i+n-rows,i,0); + } + + return pic; +} + +struct Syzygy { // {{{1 + // Setup {{{2 + Braid initial=null; + bool cyclic=true; + bool showall=false; + bool number=false; // Number the diagrams when drawn. + + string lsym, codename; + + bool watched=false; + bool uptodate=true; + + struct Move { + Braid action(Braid); + Relation rel; + int place, step; + } + + Move[] moves; + + void apply(Relation r, int step, int place) { + Move m=new Move; + m.rel=r; + m.place=place; m.step=step; + m.action=new Braid (Braid b) { + return apply(r, b, step, place); + }; + moves.push(m); + + uptodate = false; + } + + void swap(int i, int j) { + Move m=new Move; + m.rel=null; + m.action=new Braid (Braid b) { + return b.swap(i, j); + }; + moves.push(m); + + uptodate = false; + } + + // Drawing {{{2 + picture[] drawMoves() { + picture[] pics; + + assert(initial!=null, "must set initial braid"); + Braid b=initial; + + picture pic; + b.draw(pic); + pics.push(pic); + + for (int i=0; i<moves.length; ++i) { + b=moves[i].action(b); + if (showall || moves[i].rel != null) { + picture pic; + b.draw(pic); + pics.push(pic); + } + } + + // Remove the last picture. + if (this.cyclic) + pics.pop(); + + return pics; + } + + void draw(picture pic=currentpicture) { + pic.add(tableau(fit(drawMoves()), this.number)); + } + + void updatefunction() { + if (!uptodate) { + picture pic; this.draw(pic); + shipout(pic); + uptodate = true; + } + } + + void oldupdatefunction() = null; + + void watch() { + if (!watched) { + watched = true; + oldupdatefunction = atupdate(); + atupdate(this.updatefunction); + uptodate = false; + } + } + + void unwatch() { + assert(watched == true); + atupdate(oldupdatefunction); + uptodate = false; + } + + // Writing {{{2 + string linearName() { + assert(lsym!=""); + + string s=lsym+"("; + for (int i=1; i<=initial.n+1; ++i) { + if (i>1) + s+=","; + s+="x_"+(string)i; + } + return s+")"; + } + + string fullCodeName() { + assert(codename!=""); + + string s=codename+"["; + for (int i=1; i<=initial.n+1; ++i) { + if (i>1) + s+=", "; + s+="x"+(string)i+"_"; + } + return s+"]"; + } + + string toLinear() { + string s=linearName()+" = "; + + Braid b=initial; + bool first=true; + for (int i=0; i<moves.length; ++i) { + Move m=moves[i]; + if (m.rel != null) { + if (first) { + first=false; + if (m.rel.inverted) + s+=" - "; + } + else + s+=m.rel.inverted ? " - " : " + "; + s+=m.rel.lsym+b.windowToLinear(m.step, m.place, m.rel.lhs.n); + } + b=m.action(b); + } + + return s; + } + + string toCode() { + string s=fullCodeName()+" :> "; + + Braid b=initial; + bool first=true; + for (int i=0; i<moves.length; ++i) { + Move m=moves[i]; + if (m.rel != null) { + if (first) { + first=false; + if (m.rel.inverted) + s+=" - "; + } + else + s+=m.rel.inverted ? " - " : " + "; + s+=m.rel.codename+b.windowToCode(m.step, m.place, m.rel.lhs.n); + } + b=m.action(b); + } + + return s; + } + +} + +// Relation definitions {{{1 +// If you define more relations that you think would be useful, please email +// them to me, and I'll add them to the script. --Andy. +Relation r3; +r3.lhs.n=3; +r3.lsym="\rho_3"; r3.codename="rho3"; +r3.lhs.add(bp,0); r3.lhs.add(bp,1); r3.lhs.add(bp,0); +r3.rhs.n=3; +r3.rhs.add(bp,1); r3.rhs.add(bp,0); r3.rhs.add(bp,1); + +Relation r4a; +r4a.lhs.n=3; +r4a.lsym="\rho_{4a}"; r4a.codename="rho4a"; +r4a.lhs.add(bp,0); r4a.lhs.add(bp,1); r4a.lhs.add(phi,0); +r4a.rhs.n=3; +r4a.rhs.add(phi,1); r4a.rhs.add(bp,0); + +Relation r4b; +r4b.lhs.n=3; +r4b.lsym="\rho_{4b}"; r4b.codename="rho4b"; +r4b.lhs.add(bp,1); r4b.lhs.add(bp,0); r4b.lhs.add(phi,1); +r4b.rhs.n=3; +r4b.rhs.add(phi,0); r4b.rhs.add(bp,0); + diff --git a/Build/source/utils/asymptote/base/texcolors.asy b/Build/source/utils/asymptote/base/texcolors.asy new file mode 100644 index 00000000000..90d9606cb70 --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/three.asy b/Build/source/utils/asymptote/base/three.asy new file mode 100644 index 00000000000..5fc0d2f1096 --- /dev/null +++ b/Build/source/utils/asymptote/base/three.asy @@ -0,0 +1,3239 @@ +private import math; + +if(settings.xasy) + settings.render=0; + +if(prc0()) { + if(!latex()) settings.prc=false; + else { + access embed; + Embed=embed.embedplayer; + } +} + +// Useful lossy compression values. +restricted real Zero=0; +restricted real Low=0.0001; +restricted real Medium=0.001; +restricted real High=0.01; + +restricted int PRCsphere=0; // Renders slowly but produces smaller PRC files. +restricted int NURBSsphere=1; // Renders fast but produces larger PRC files. + +struct render +{ + // PRC parameters: + real compression; // lossy compression parameter (0=no compression) + real granularity; // PRC rendering granularity + + bool closed; // use one-sided rendering? + bool tessellate; // use tessellated mesh to store straight patches? + + bool3 merge; // merge nodes before rendering, for faster but + // lower quality PRC rendering (the value default means + // merge opaque patches only). + + int sphere; // PRC sphere type (PRCsphere or NURBSsphere). + + // General parameters: + real margin; // shrink amount for rendered openGL viewport, in bp. + bool labelfill; // fill PRC subdivision cracks in unlighted labels + + bool partnames; // assign part name indices to compound objects + bool defaultnames; // assign default names to unnamed objects + + static render defaultrender; + + void operator init(real compression=defaultrender.compression, + real granularity=defaultrender.granularity, + bool closed=defaultrender.closed, + bool tessellate=defaultrender.tessellate, + bool3 merge=defaultrender.merge, + int sphere=defaultrender.sphere, + real margin=defaultrender.margin, + bool labelfill=defaultrender.labelfill, + bool partnames=defaultrender.partnames, + bool defaultnames=defaultrender.defaultnames) + { + this.compression=compression; + this.granularity=granularity; + this.closed=closed; + this.tessellate=tessellate; + this.merge=merge; + this.sphere=sphere; + this.margin=margin; + this.labelfill=labelfill; + this.partnames=partnames; + this.defaultnames=defaultnames; + } +} + +render operator init() {return render();} + +render defaultrender=render.defaultrender=new render; +defaultrender.compression=High; +defaultrender.granularity=Medium; +defaultrender.closed=false; +defaultrender.tessellate=false; +defaultrender.merge=false; +defaultrender.margin=0.02; +defaultrender.sphere=NURBSsphere; +defaultrender.labelfill=true; +defaultrender.partnames=false; +defaultrender.defaultnames=true; + +real defaultshininess=0.7; +real defaultmetallic=0.0; +real defaultfresnel0=0.04; + + + +real angleprecision=1e-5; // Precision for centering perspective projections. +int maxangleiterations=25; + +string defaultembed3Doptions="3Dmenu"; +string defaultembed3Dscript; +real defaulteyetoview=63mm/1000mm; + +string partname(int i, render render=defaultrender) +{ + return render.partnames ? string(i+1) : ""; +} + +triple O=(0,0,0); +triple X=(1,0,0), Y=(0,1,0), Z=(0,0,1); + +// A translation in 3D space. +transform3 shift(triple v) +{ + transform3 t=identity(4); + t[0][3]=v.x; + t[1][3]=v.y; + t[2][3]=v.z; + return t; +} + +// Avoid two parentheses. +transform3 shift(real x, real y, real z) +{ + return shift((x,y,z)); +} + +transform3 shift(transform3 t) +{ + transform3 T=identity(4); + T[0][3]=t[0][3]; + T[1][3]=t[1][3]; + T[2][3]=t[2][3]; + return T; +} + +// A 3D scaling in the x direction. +transform3 xscale3(real x) +{ + transform3 t=identity(4); + t[0][0]=x; + return t; +} + +// A 3D scaling in the y direction. +transform3 yscale3(real y) +{ + transform3 t=identity(4); + t[1][1]=y; + return t; +} + +// A 3D scaling in the z direction. +transform3 zscale3(real z) +{ + transform3 t=identity(4); + t[2][2]=z; + return t; +} + +// A 3D scaling by s in the v direction. +transform3 scale(triple v, real s) +{ + v=unit(v); + s -= 1; + return new real[][] { + {1+s*v.x^2, s*v.x*v.y, s*v.x*v.z, 0}, + {s*v.x*v.y, 1+s*v.y^2, s*v.y*v.z, 0}, + {s*v.x*v.z, s*v.y*v.z, 1+s*v.z^2, 0}, + {0, 0, 0, 1}}; +} + +// A transformation representing rotation by an angle in degrees about +// an axis v through the origin (in the right-handed direction). +transform3 rotate(real angle, triple v) +{ + if(v == O) abort("cannot rotate about the zero vector"); + v=unit(v); + real x=v.x, y=v.y, z=v.z; + real s=Sin(angle), c=Cos(angle), t=1-c; + + return new real[][] { + {t*x^2+c, t*x*y-s*z, t*x*z+s*y, 0}, + {t*x*y+s*z, t*y^2+c, t*y*z-s*x, 0}, + {t*x*z-s*y, t*y*z+s*x, t*z^2+c, 0}, + {0, 0, 0, 1}}; +} + +// A transformation representing rotation by an angle in degrees about +// the line u--v (in the right-handed direction). +transform3 rotate(real angle, triple u, triple v) +{ + return shift(u)*rotate(angle,v-u)*shift(-u); +} + +// Reflects about the plane through u, v, and w. +transform3 reflect(triple u, triple v, triple w) +{ + triple n=unit(cross(v-u,w-u)); + if(n == O) + abort("points determining reflection plane cannot be colinear"); + + return new real[][] { + {1-2*n.x^2, -2*n.x*n.y, -2*n.x*n.z, u.x}, + {-2*n.x*n.y, 1-2*n.y^2, -2*n.y*n.z, u.y}, + {-2*n.x*n.z, -2*n.y*n.z, 1-2*n.z^2, u.z}, + {0, 0, 0, 1} + }*shift(-u); +} + +// Project u onto v. +triple project(triple u, triple v) +{ + v=unit(v); + return dot(u,v)*v; +} + +// Return a unit vector perpendicular to a given unit vector v. +triple perp(triple v) +{ + triple u=cross(v,Y); + real norm=sqrtEpsilon*abs(v); + if(abs(u) > norm) return unit(u); + u=cross(v,Z); + return (abs(u) > norm) ? unit(u) : X; +} + +// Return the transformation corresponding to moving the camera from the target +// (looking in the negative z direction) to the point 'eye' (looking at target, +// orienting the camera so that direction 'up' points upwards. +// Since, in actuality, we are transforming the points instead of the camera, +// we calculate the inverse matrix. +// Based on the gluLookAt implementation in the OpenGL manual. +transform3 look(triple eye, triple up=Z, triple target=O) +{ + triple f=unit(target-eye); + if(f == O) + f=-Z; // The eye is already at the origin: look down. + + triple s=cross(f,up); + + // If the eye is pointing either directly up or down, there is no + // preferred "up" direction. Pick one arbitrarily. + s=s != O ? unit(s) : perp(f); + + triple u=cross(s,f); + + transform3 M={{ s.x, s.y, s.z, 0}, + { u.x, u.y, u.z, 0}, + {-f.x, -f.y, -f.z, 0}, + { 0, 0, 0, 1}}; + + return M*shift(-eye); +} + +// Return a matrix to do perspective distortion based on a triple v. +transform3 distort(triple v) +{ + transform3 t=identity(4); + real d=length(v); + if(d == 0) return t; + t[3][2]=-1/d; + t[3][3]=0; + return t; +} + +projection operator * (transform3 t, projection P) +{ + projection P=P.copy(); + if(!P.absolute) { + P.camera=t*P.camera; + triple target=P.target; + P.target=t*P.target; + if(P.infinity) + P.normal=t*(target+P.normal)-P.target; + else + P.normal=P.vector(); + P.calculate(); + } + return P; +} + +// With this, save() and restore() in plain also save and restore the +// currentprojection. +addSaveFunction(new restoreThunk() { + projection P=currentprojection.copy(); + return new void() { + currentprojection=P; + }; + }); + +pair project(triple v, projection P=currentprojection) +{ + return project(v,P.t); +} + +pair dir(triple v, triple dir, projection P) +{ + return unit(project(v+0.5dir,P)-project(v-0.5*dir,P)); +} + +// Uses the homogenous coordinate to perform perspective distortion. +// When combined with a projection to the XY plane, this effectively maps +// points in three space to a plane through target and +// perpendicular to the vector camera-target. +projection perspective(triple camera, triple up=Z, triple target=O, + real zoom=1, real angle=0, pair viewportshift=0, + bool showtarget=true, bool autoadjust=true, + bool center=autoadjust) +{ + if(camera == target) + abort("camera cannot be at target"); + return projection(camera,up,target,zoom,angle,viewportshift, + showtarget,autoadjust,center, + new transformation(triple camera, triple up, triple target) + {return transformation(look(camera,up,target), + distort(camera-target));}); +} + +projection perspective(real x, real y, real z, triple up=Z, triple target=O, + real zoom=1, real angle=0, pair viewportshift=0, + bool showtarget=true, bool autoadjust=true, + bool center=autoadjust) +{ + return perspective((x,y,z),up,target,zoom,angle,viewportshift,showtarget, + autoadjust,center); +} + +projection orthographic(triple camera, triple up=Z, triple target=O, + real zoom=1, pair viewportshift=0, + bool showtarget=true, bool center=false) +{ + return projection(camera,up,target,zoom,viewportshift,showtarget, + center=center,new transformation(triple camera, triple up, + triple target) { + return transformation(look(camera,up,target));}); +} + +projection orthographic(real x, real y, real z, triple up=Z, + triple target=O, real zoom=1, pair viewportshift=0, + bool showtarget=true, bool center=false) +{ + return orthographic((x,y,z),up,target,zoom,viewportshift,showtarget, + center=center); +} + +// Compute camera position with x axis below the horizontal at angle alpha, +// y axis below the horizontal at angle beta, and z axis up. +triple camera(real alpha, real beta) +{ + real denom=Tan(alpha+beta); + real Tanalpha=Tan(alpha); + real Tanbeta=Tan(beta); + return (sqrt(Tanalpha/denom),sqrt(Tanbeta/denom),sqrt(Tanalpha*Tanbeta)); +} + +projection oblique(real angle=45) +{ + transform3 t=identity(4); + real c2=Cos(angle)^2; + real s2=1-c2; + t[0][2]=-c2; + t[1][2]=-s2; + t[2][2]=1; + t[2][3]=-1; + return projection((c2,s2,1),up=Y,normal=(0,0,1), + new transformation(triple,triple,triple) { + return transformation(t);}); +} + +projection obliqueZ(real angle=45) {return oblique(angle);} + +projection obliqueX(real angle=45) +{ + transform3 t=identity(4); + real c2=Cos(angle)^2; + real s2=1-c2; + t[0][0]=-c2; + t[1][0]=-s2; + t[1][1]=0; + t[0][1]=1; + t[1][2]=1; + t[2][2]=0; + t[2][0]=1; + t[2][3]=-1; + return projection((1,c2,s2),normal=(1,0,0), + new transformation(triple,triple,triple) { + return transformation(t);}); +} + +projection obliqueY(real angle=45) +{ + transform3 t=identity(4); + real c2=Cos(angle)^2; + real s2=1-c2; + t[0][1]=c2; + t[1][1]=s2; + t[1][2]=1; + t[2][1]=-1; + t[2][2]=0; + t[2][3]=-1; + return projection((c2,-1,s2),normal=(0,-1,0), + new transformation(triple,triple,triple) { + return transformation(t);}); +} + +projection oblique=oblique(); +projection obliqueX=obliqueX(), obliqueY=obliqueY(), obliqueZ=obliqueZ(); + +projection LeftView=orthographic(-X,showtarget=true); +projection RightView=orthographic(X,showtarget=true); +projection FrontView=orthographic(-Y,showtarget=true); +projection BackView=orthographic(Y,showtarget=true); +projection BottomView=orthographic(-Z,up=-Y,showtarget=true); +projection TopView=orthographic(Z,up=Y,showtarget=true); + +currentprojection=perspective(5,4,2); + +projection projection() +{ + projection P; + real[] a=_projection(); + if(a.length == 0 || a[10] == 0.0) return currentprojection; + int k=0; + return a[0] == 1 ? + orthographic((a[++k],a[++k],a[++k]),(a[++k],a[++k],a[++k]), + (a[++k],a[++k],a[++k]),a[++k],(a[k += 2],a[++k])) : + perspective((a[++k],a[++k],a[++k]),(a[++k],a[++k],a[++k]), + (a[++k],a[++k],a[++k]),a[++k],a[++k],(a[++k],a[++k])); +} + +// Map pair z to a triple by inverting the projection P onto the +// plane perpendicular to normal and passing through point. +triple invert(pair z, triple normal, triple point, + projection P=currentprojection) +{ + transform3 t=P.t; + real[][] A={{t[0][0]-z.x*t[3][0],t[0][1]-z.x*t[3][1],t[0][2]-z.x*t[3][2]}, + {t[1][0]-z.y*t[3][0],t[1][1]-z.y*t[3][1],t[1][2]-z.y*t[3][2]}, + {normal.x,normal.y,normal.z}}; + real[] b={z.x*t[3][3]-t[0][3],z.y*t[3][3]-t[1][3],dot(normal,point)}; + real[] x=solve(A,b,warn=false); + return x.length > 0 ? (x[0],x[1],x[2]) : P.camera; +} + +// Map pair to a triple on the projection plane. +triple invert(pair z, projection P=currentprojection) +{ + return invert(z,P.normal,P.target,P); +} + +// Map pair dir to a triple direction at point v on the projection plane. +triple invert(pair dir, triple v, projection P=currentprojection) +{ + return invert(project(v,P)+dir,P.normal,v,P)-v; +} + +pair xypart(triple v) +{ + return (v.x,v.y); +} + +struct control { + triple post,pre; + bool active=false; + bool straight=true; + void operator init(triple post, triple pre, bool straight=false) { + this.post=post; + this.pre=pre; + active=true; + this.straight=straight; + } +} + +control nocontrol; + +control operator * (transform3 t, control c) +{ + control C; + C.post=t*c.post; + C.pre=t*c.pre; + C.active=c.active; + C.straight=c.straight; + return C; +} + +void write(file file, control c) +{ + write(file,".. controls "); + write(file,c.post); + write(file," and "); + write(file,c.pre); +} + +struct Tension { + real out,in; + bool atLeast; + bool active; + void operator init(real out=1, real in=1, bool atLeast=false, + bool active=true) { + real check(real val) { + if(val < 0.75) abort("tension cannot be less than 3/4"); + return val; + } + this.out=check(out); + this.in=check(in); + this.atLeast=atLeast; + this.active=active; + } +} + +Tension operator init() +{ + return Tension(); +} + +Tension noTension; +noTension.active=false; + +void write(file file, Tension t) +{ + write(file,"..tension "); + if(t.atLeast) write(file,"atleast "); + write(file,t.out); + write(file," and "); + write(file,t.in); +} + +struct dir { + triple dir; + real gamma=1; // endpoint curl + bool Curl; // curl specified + bool active() { + return dir != O || Curl; + } + void init(triple v) { + this.dir=v; + } + void init(real gamma) { + if(gamma < 0) abort("curl cannot be less than 0"); + this.gamma=gamma; + this.Curl=true; + } + void init(dir d) { + dir=d.dir; + gamma=d.gamma; + Curl=d.Curl; + } + void default(triple v) { + if(!active()) init(v); + } + void default(dir d) { + if(!active()) init(d); + } + dir copy() { + dir d=new dir; + d.init(this); + return d; + } +} + +void write(file file, dir d) +{ + if(d.dir != O) { + write(file,"{"); write(file,unit(d.dir)); write(file,"}"); + } else if(d.Curl) { + write(file,"{curl "); write(file,d.gamma); write(file,"}"); + } +} + +dir operator * (transform3 t, dir d) +{ + dir D=d.copy(); + D.init(unit(shiftless(t)*d.dir)); + return D; +} + +void checkEmpty(int n) { + if(n == 0) + abort("nullpath3 has no points"); +} + +int adjustedIndex(int i, int n, bool cycles) +{ + checkEmpty(n); + if(cycles) + return i % n; + else if(i < 0) + return 0; + else if(i >= n) + return n-1; + else + return i; +} + +struct flatguide3 { + triple[] nodes; + bool[] cyclic; // true if node is really a cycle + control[] control; // control points for segment starting at node + Tension[] Tension; // Tension parameters for segment starting at node + dir[] in,out; // in and out directions for segment starting at node + + bool cyclic() {int n=cyclic.length; return n > 0 ? cyclic[n-1] : false;} + bool precyclic() {int i=find(cyclic); return i >= 0 && i < cyclic.length-1;} + + int size() { + return cyclic() ? nodes.length-1 : nodes.length; + } + + void node(triple v, bool b=false) { + nodes.push(v); + control.push(nocontrol); + Tension.push(noTension); + in.push(new dir); + out.push(new dir); + cyclic.push(b); + } + + void control(triple post, triple pre) { + if(control.length > 0) { + control c=control(post,pre,false); + control[control.length-1]=c; + } + } + + void Tension(real out, real in, bool atLeast) { + if(Tension.length > 0) + Tension[Tension.length-1]=Tension(out,in,atLeast,true); + } + + void in(triple v) { + if(in.length > 0) { + in[in.length-1].init(v); + } + } + + void out(triple v) { + if(out.length > 0) { + out[out.length-1].init(v); + } + } + + void in(real gamma) { + if(in.length > 0) { + in[in.length-1].init(gamma); + } + } + + void out(real gamma) { + if(out.length > 0) { + out[out.length-1].init(gamma); + } + } + + void cycleToken() { + if(nodes.length > 0) + node(nodes[0],true); + } + + // Return true if outgoing direction at node i is known. + bool solved(int i) { + return out[i].active() || control[i].active; + } +} + +void write(file file, string s="", explicit flatguide3 x, suffix suffix=none) +{ + write(file,s); + if(x.size() == 0) write(file,"<nullpath3>"); + else for(int i=0; i < x.nodes.length; ++i) { + if(i > 0) write(file,endl); + if(x.cyclic[i]) write(file,"cycle"); + else write(file,x.nodes[i]); + if(i < x.nodes.length-1) { + // Explicit control points trump other specifiers + if(x.control[i].active) + write(file,x.control[i]); + else { + write(file,x.out[i]); + if(x.Tension[i].active) write(file,x.Tension[i]); + } + write(file,".."); + if(!x.control[i].active) write(file,x.in[i]); + } + } + write(file,suffix); +} + +void write(string s="", flatguide3 x, suffix suffix=endl) +{ + write(stdout,s,x,suffix); +} + +// A guide3 is most easily represented as something that modifies a flatguide3. +typedef void guide3(flatguide3); + +restricted void nullpath3(flatguide3) {}; + +guide3 operator init() {return nullpath3;} + +guide3 operator cast(triple v) +{ + return new void(flatguide3 f) { + f.node(v); + }; +} + +guide3 operator cast(cycleToken) { + return new void(flatguide3 f) { + f.cycleToken(); + }; +} + +guide3 operator controls(triple post, triple pre) +{ + return new void(flatguide3 f) { + f.control(post,pre); + }; +}; + +guide3 operator controls(triple v) +{ + return operator controls(v,v); +} + +guide3 operator cast(tensionSpecifier t) +{ + return new void(flatguide3 f) { + f.Tension(t.out, t.in, t.atLeast); + }; +} + +guide3 operator cast(curlSpecifier spec) +{ + return new void(flatguide3 f) { + if(spec.side == JOIN_OUT) f.out(spec.value); + else if(spec.side == JOIN_IN) f.in(spec.value); + else + abort("invalid curl specifier"); + }; +} + +guide3 operator spec(triple v, int side) +{ + return new void(flatguide3 f) { + if(side == JOIN_OUT) f.out(v); + else if(side == JOIN_IN) f.in(v); + else + abort("invalid direction specifier"); + }; +} + +guide3 operator -- (... guide3[] g) +{ + return new void(flatguide3 f) { + if(g.length > 0) { + for(int i=0; i < g.length-1; ++i) { + g[i](f); + f.out(1); + f.in(1); + } + g[g.length-1](f); + } + }; +} + +guide3 operator .. (... guide3[] g) +{ + return new void(flatguide3 f) { + for(int i=0; i < g.length; ++i) + g[i](f); + }; +} + +typedef guide3 interpolate3(... guide3[]); + +interpolate3 join3(tensionSpecifier t) +{ + return new guide3(... guide3[] a) { + if(a.length == 0) return nullpath3; + guide3 g=a[0]; + for(int i=1; i < a.length; ++i) + g=g..t..a[i]; + return g; + }; +} + +interpolate3 operator ::=join3(operator tension(1,true)); +interpolate3 operator ---=join3(operator tension(infinity,true)); + +flatguide3 operator cast(guide3 g) +{ + flatguide3 f; + g(f); + return f; +} + +flatguide3[] operator cast(guide3[] g) +{ + flatguide3[] p=new flatguide3[g.length]; + for(int i=0; i < g.length; ++i) { + flatguide3 f; + g[i](f); + p[i]=f; + } + return p; +} + +// A version of asin that tolerates numerical imprecision +real asin1(real x) +{ + return asin(min(max(x,-1),1)); +} + +// A version of acos that tolerates numerical imprecision +real acos1(real x) +{ + return acos(min(max(x,-1),1)); +} + +struct Controls { + triple c0,c1; + + // 3D extension of John Hobby's control point formula + // (cf. The MetaFont Book, page 131), + // as described in John C. Bowman and A. Hammerlindl, + // TUGBOAT: The Communications of the TeX Users Group 29:2 (2008). + + void operator init(triple v0, triple v1, triple d0, triple d1, real tout, + real tin, bool atLeast) { + triple v=v1-v0; + triple u=unit(v); + real L=length(v); + d0=unit(d0); + d1=unit(d1); + real theta=acos1(dot(d0,u)); + real phi=acos1(dot(d1,u)); + if(dot(cross(d0,v),cross(v,d1)) < 0) phi=-phi; + c0=v0+d0*L*relativedistance(theta,phi,tout,atLeast); + c1=v1-d1*L*relativedistance(phi,theta,tin,atLeast); + } +} + +private triple cross(triple d0, triple d1, triple reference) +{ + triple normal=cross(d0,d1); + return normal == O ? reference : normal; +} + +private triple dir(real theta, triple d0, triple d1, triple reference) +{ + triple normal=cross(d0,d1,reference); + if(normal == O) return d1; + return rotate(degrees(theta),dot(normal,reference) >= 0 ? normal : -normal)* + d1; +} + +private real angle(triple d0, triple d1, triple reference) +{ + real theta=acos1(dot(unit(d0),unit(d1))); + return dot(cross(d0,d1,reference),reference) >= 0 ? theta : -theta; +} + +// 3D extension of John Hobby's angle formula (The MetaFont Book, page 131). +// Notational differences: here psi[i] is the turning angle at z[i+1], +// beta[i] is the tension for segment i, and in[i] is the incoming +// direction for segment i (where segment i begins at node i). + +real[] theta(triple[] v, real[] alpha, real[] beta, + triple dir0, triple dirn, real g0, real gn, triple reference) +{ + real[] a,b,c,f,l,psi; + int n=alpha.length; + bool cyclic=v.cyclic; + for(int i=0; i < n; ++i) + l[i]=1/length(v[i+1]-v[i]); + int i0,in; + if(cyclic) {i0=0; in=n;} + else {i0=1; in=n-1;} + for(int i=0; i < in; ++i) + psi[i]=angle(v[i+1]-v[i],v[i+2]-v[i+1],reference); + if(cyclic) { + l.cyclic=true; + psi.cyclic=true; + } else { + psi[n-1]=0; + if(dir0 == O) { + real a0=alpha[0]; + real b0=beta[0]; + real chi=g0*(b0/a0)^2; + a[0]=0; + b[0]=3a0-a0/b0+chi; + real C=chi*(3a0-1)+a0/b0; + c[0]=C; + f[0]=-C*psi[0]; + } else { + a[0]=c[0]=0; + b[0]=1; + f[0]=angle(v[1]-v[0],dir0,reference); + } + if(dirn == O) { + real an=alpha[n-1]; + real bn=beta[n-1]; + real chi=gn*(an/bn)^2; + a[n]=chi*(3bn-1)+bn/an; + b[n]=3bn-bn/an+chi; + c[n]=f[n]=0; + } else { + a[n]=c[n]=0; + b[n]=1; + f[n]=angle(v[n]-v[n-1],dirn,reference); + } + } + + for(int i=i0; i < n; ++i) { + real in=beta[i-1]^2*l[i-1]; + real A=in/alpha[i-1]; + a[i]=A; + real B=3*in-A; + real out=alpha[i]^2*l[i]; + real C=out/beta[i]; + b[i]=B+3*out-C; + c[i]=C; + f[i]=-B*psi[i-1]-C*psi[i]; + } + + return tridiagonal(a,b,c,f); +} + +triple reference(triple[] v, int n, triple d0, triple d1) +{ + triple[] V=sequence(new triple(int i) { + return cross(v[i+1]-v[i],v[i+2]-v[i+1]); + },n-1); + if(n > 0) { + V.push(cross(d0,v[1]-v[0])); + V.push(cross(v[n]-v[n-1],d1)); + } + + triple max=V[0]; + real M=abs(max); + for(int i=1; i < V.length; ++i) { + triple vi=V[i]; + real a=abs(vi); + if(a > M) { + M=a; + max=vi; + } + } + + triple reference; + for(int i=0; i < V.length; ++i) { + triple u=unit(V[i]); + reference += dot(u,max) < 0 ? -u : u; + } + + return reference; +} + +// Fill in missing directions for n cyclic nodes. +void aim(flatguide3 g, int N) +{ + bool cyclic=true; + int start=0, end=0; + + // If the cycle contains one or more direction specifiers, break the loop. + for(int k=0; k < N; ++k) + if(g.solved(k)) {cyclic=false; end=k; break;} + for(int k=N-1; k >= 0; --k) + if(g.solved(k)) {cyclic=false; start=k; break;} + while(start < N && g.control[start].active) ++start; + + int n=N-(start-end); + if(n <= 1 || (cyclic && n <= 2)) return; + + triple[] v=new triple[cyclic ? n : n+1]; + real[] alpha=new real[n]; + real[] beta=new real[n]; + for(int k=0; k < n; ++k) { + int K=(start+k) % N; + v[k]=g.nodes[K]; + alpha[k]=g.Tension[K].out; + beta[k]=g.Tension[K].in; + } + if(cyclic) { + v.cyclic=true; + alpha.cyclic=true; + beta.cyclic=true; + } else v[n]=g.nodes[(start+n) % N]; + int final=(end-1) % N; + + triple d0=g.out[start].dir; + triple d1=g.in[final].dir; + + triple reference=reference(v,n,d0,d1); + + real[] theta=theta(v,alpha,beta,d0,d1,g.out[start].gamma,g.in[final].gamma, + reference); + + v.cyclic=true; + theta.cyclic=true; + + for(int k=1; k < (cyclic ? n+1 : n); ++k) { + triple w=dir(theta[k],v[k]-v[k-1],v[k+1]-v[k],reference); + g.in[(start+k-1) % N].init(w); + g.out[(start+k) % N].init(w); + } + + if(g.out[start].dir == O) + g.out[start].init(dir(theta[0],v[0]-g.nodes[(start-1) % N],v[1]-v[0], + reference)); + if(g.in[final].dir == O) + g.in[final].init(dir(theta[n],v[n-1]-v[n-2],v[n]-v[n-1],reference)); +} + +// Fill in missing directions for the sequence of nodes i...n. +void aim(flatguide3 g, int i, int n) +{ + int j=n-i; + if(j > 1 || g.out[i].dir != O || g.in[i].dir != O) { + triple[] v=new triple[j+1]; + real[] alpha=new real[j]; + real[] beta=new real[j]; + for(int k=0; k < j; ++k) { + v[k]=g.nodes[i+k]; + alpha[k]=g.Tension[i+k].out; + beta[k]=g.Tension[i+k].in; + } + v[j]=g.nodes[n]; + + triple d0=g.out[i].dir; + triple d1=g.in[n-1].dir; + + triple reference=reference(v,j,d0,d1); + + real[] theta=theta(v,alpha,beta,d0,d1,g.out[i].gamma,g.in[n-1].gamma, + reference); + + for(int k=1; k < j; ++k) { + triple w=dir(theta[k],v[k]-v[k-1],v[k+1]-v[k],reference); + g.in[i+k-1].init(w); + g.out[i+k].init(w); + } + if(g.out[i].dir == O) { + triple w=dir(theta[0],g.in[i].dir,v[1]-v[0],reference); + if(i > 0) g.in[i-1].init(w); + g.out[i].init(w); + } + if(g.in[n-1].dir == O) { + triple w=dir(theta[j],g.out[n-1].dir,v[j]-v[j-1],reference); + g.in[n-1].init(w); + g.out[n].init(w); + } + } +} + +private real Fuzz=10*realEpsilon; + +triple XYplane(pair z) {return (z.x,z.y,0);} +triple YZplane(pair z) {return (0,z.x,z.y);} +triple ZXplane(pair z) {return (z.y,0,z.x);} + +bool cyclic(guide3 g) {flatguide3 f; g(f); return f.cyclic();} +int size(guide3 g) {flatguide3 f; g(f); return f.size();} +int length(guide3 g) {flatguide3 f; g(f); return f.nodes.length-1;} + +triple dir(path3 p) +{ + return dir(p,length(p)); +} + +triple dir(path3 p, path3 h) +{ + return unit(dir(p)+dir(h)); +} + +// return the point on path3 p at arclength L +triple arcpoint(path3 p, real L) +{ + return point(p,arctime(p,L)); +} + +// return the direction on path3 p at arclength L +triple arcdir(path3 p, real L) +{ + return dir(p,arctime(p,L)); +} + +// return the time on path3 p at the relative fraction l of its arclength +real reltime(path3 p, real l) +{ + return arctime(p,l*arclength(p)); +} + +// return the point on path3 p at the relative fraction l of its arclength +triple relpoint(path3 p, real l) +{ + return point(p,reltime(p,l)); +} + +// return the direction of path3 p at the relative fraction l of its arclength +triple reldir(path3 p, real l) +{ + return dir(p,reltime(p,l)); +} + +// return the initial point of path3 p +triple beginpoint(path3 p) +{ + return point(p,0); +} + +// return the point on path3 p at half of its arclength +triple midpoint(path3 p) +{ + return relpoint(p,0.5); +} + +// return the final point of path3 p +triple endpoint(path3 p) +{ + return point(p,length(p)); +} + +path3 path3(triple v) +{ + triple[] point={v}; + return path3(point,point,point,new bool[] {false},false); +} + +path3 path3(path p, triple plane(pair)=XYplane) +{ + int n=size(p); + return path3(sequence(new triple(int i) {return plane(precontrol(p,i));},n), + sequence(new triple(int i) {return plane(point(p,i));},n), + sequence(new triple(int i) {return plane(postcontrol(p,i));},n), + sequence(new bool(int i) {return straight(p,i);},n), + cyclic(p)); +} + +path3[] path3(explicit path[] g, triple plane(pair)=XYplane) +{ + return sequence(new path3(int i) {return path3(g[i],plane);},g.length); +} + +path3 interp(path3 a, path3 b, real t) +{ + int n=size(a); + return path3(sequence(new triple(int i) { + return interp(precontrol(a,i),precontrol(b,i),t);},n), + sequence(new triple(int i) {return interp(point(a,i),point(b,i),t);},n), + sequence(new triple(int i) {return interp(postcontrol(a,i), + postcontrol(b,i),t);},n), + sequence(new bool(int i) {return straight(a,i) && straight(b,i);},n), + cyclic(a) && cyclic(b)); +} + +path3 invert(path p, triple normal, triple point, + projection P=currentprojection) +{ + return path3(p,new triple(pair z) {return invert(z,normal,point,P);}); +} + +path3 invert(path p, triple point, projection P=currentprojection) +{ + return path3(p,new triple(pair z) {return invert(z,P.normal,point,P);}); +} + +path3 invert(path p, projection P=currentprojection) +{ + return path3(p,new triple(pair z) {return invert(z,P.normal,P.target,P);}); +} + +// Construct a path from a path3 by applying P to each control point. +path path(path3 p, pair P(triple)=xypart) +{ + int n=length(p); + if(n < 0) return nullpath; + guide g=P(point(p,0)); + if(n == 0) return g; + for(int i=1; i < n; ++i) + g=straight(p,i-1) ? g--P(point(p,i)) : + g..controls P(postcontrol(p,i-1)) and P(precontrol(p,i))..P(point(p,i)); + + if(straight(p,n-1)) + return cyclic(p) ? g--cycle : g--P(point(p,n)); + + pair post=P(postcontrol(p,n-1)); + pair pre=P(precontrol(p,n)); + return cyclic(p) ? g..controls post and pre..cycle : + g..controls post and pre..P(point(p,n)); +} + +void write(file file, string s="", explicit path3 x, suffix suffix=none) +{ + write(file,s); + int n=length(x); + if(n < 0) write("<nullpath3>"); + else { + for(int i=0; i < n; ++i) { + write(file,point(x,i)); + if(i < length(x)) { + if(straight(x,i)) write(file,"--"); + else { + write(file,".. controls "); + write(file,postcontrol(x,i)); + write(file," and "); + write(file,precontrol(x,i+1),newl); + write(file," .."); + } + } + } + if(cyclic(x)) + write(file,"cycle",suffix); + else + write(file,point(x,n),suffix); + } +} + +void write(string s="", explicit path3 x, suffix suffix=endl) +{ + write(stdout,s,x,suffix); +} + +void write(file file, string s="", explicit path3[] x, suffix suffix=none) +{ + write(file,s); + if(x.length > 0) write(file,x[0]); + for(int i=1; i < x.length; ++i) { + write(file,endl); + write(file," ^^"); + write(file,x[i]); + } + write(file,suffix); +} + +void write(string s="", explicit path3[] x, suffix suffix=endl) +{ + write(stdout,s,x,suffix); +} + +path3 solve(flatguide3 g) +{ + int n=g.nodes.length-1; + + // If duplicate points occur consecutively, add dummy controls (if absent). + for(int i=0; i < n; ++i) { + if(g.nodes[i] == g.nodes[i+1] && !g.control[i].active) + g.control[i]=control(g.nodes[i],g.nodes[i],straight=true); + } + + // Fill in empty direction specifiers inherited from explicit control points. + for(int i=0; i < n; ++i) { + if(g.control[i].active) { + g.out[i].init(g.control[i].post-g.nodes[i]); + g.in[i].init(g.nodes[i+1]-g.control[i].pre); + } + } + + // Propagate directions across nodes. + for(int i=0; i < n; ++i) { + int next=g.cyclic[i+1] ? 0 : i+1; + if(g.out[next].active()) + g.in[i].default(g.out[next]); + if(g.in[i].active()) { + g.out[next].default(g.in[i]); + g.out[i+1].default(g.in[i]); + } + } + + // Compute missing 3D directions. + // First, resolve cycles + int i=find(g.cyclic); + if(i > 0) { + aim(g,i); + // All other cycles can now be reduced to sequences. + triple v=g.out[0].dir; + for(int j=i; j <= n; ++j) { + if(g.cyclic[j]) { + g.in[j-1].default(v); + g.out[j].default(v); + if(g.nodes[j-1] == g.nodes[j] && !g.control[j-1].active) + g.control[j-1]=control(g.nodes[j-1],g.nodes[j-1]); + } + } + } + + // Next, resolve sequences. + int i=0; + int start=0; + while(i < n) { + // Look for a missing outgoing direction. + while(i <= n && g.solved(i)) {start=i; ++i;} + if(i > n) break; + // Look for the end of the sequence. + while(i < n && !g.solved(i)) ++i; + + while(start < i && g.control[start].active) ++start; + + if(start < i) + aim(g,start,i); + } + + // Compute missing 3D control points. + for(int i=0; i < n; ++i) { + int next=g.cyclic[i+1] ? 0 : i+1; + if(!g.control[i].active) { + control c; + if((g.out[i].Curl && g.in[i].Curl) || + (g.out[i].dir == O && g.in[i].dir == O)) { + // fill in straight control points for path3 functions + triple delta=(g.nodes[i+1]-g.nodes[i])/3; + c=control(g.nodes[i]+delta,g.nodes[i+1]-delta,straight=true); + } else { + Controls C=Controls(g.nodes[i],g.nodes[next],g.out[i].dir,g.in[i].dir, + g.Tension[i].out,g.Tension[i].in, + g.Tension[i].atLeast); + c=control(C.c0,C.c1); + } + g.control[i]=c; + } + } + + // Convert to Knuth's format (control points stored with nodes) + int n=g.nodes.length; + bool cyclic; + if(n > 0) { + cyclic=g.cyclic[n-1]; + if(cyclic) --n; + } + triple[] pre=new triple[n]; + triple[] point=new triple[n]; + triple[] post=new triple[n]; + bool[] straight=new bool[n]; + if(n > 0) { + for(int i=0; i < n-1; ++i) { + point[i]=g.nodes[i]; + post[i]=g.control[i].post; + pre[i+1]=g.control[i].pre; + straight[i]=g.control[i].straight; + } + point[n-1]=g.nodes[n-1]; + if(cyclic) { + pre[0]=g.control[n-1].pre; + post[n-1]=g.control[n-1].post; + straight[n-1]=g.control[n-1].straight; + } else { + pre[0]=point[0]; + post[n-1]=point[n-1]; + straight[n-1]=false; + } + } + + return path3(pre,point,post,straight,cyclic); +} + +path nurb(path3 p, projection P, int ninterpolate=P.ninterpolate) +{ + triple f=P.camera; + triple u=unit(P.normal); + transform3 t=P.t; + + path nurb(triple v0, triple v1, triple v2, triple v3) { + return nurb(project(v0,t),project(v1,t),project(v2,t),project(v3,t), + dot(u,f-v0),dot(u,f-v1),dot(u,f-v2),dot(u,f-v3),ninterpolate); + } + + path g; + + if(straight(p,0)) + g=project(point(p,0),t); + + int last=length(p); + for(int i=0; i < last; ++i) { + if(straight(p,i)) + g=g--project(point(p,i+1),t); + else + g=g&nurb(point(p,i),postcontrol(p,i),precontrol(p,i+1),point(p,i+1)); + } + + int n=length(g); + if(cyclic(p)) g=g&cycle; + + return g; +} + +path project(path3 p, projection P=currentprojection, + int ninterpolate=P.ninterpolate) +{ + guide g; + + int last=length(p); + if(last < 0) return g; + + transform3 t=P.t; + + if(ninterpolate == 1 || piecewisestraight(p)) { + g=project(point(p,0),t); + // Construct the path. + int stop=cyclic(p) ? last-1 : last; + for(int i=0; i < stop; ++i) { + if(straight(p,i)) + g=g--project(point(p,i+1),t); + else { + g=g..controls project(postcontrol(p,i),t) and + project(precontrol(p,i+1),t)..project(point(p,i+1),t); + } + } + } else return nurb(p,P); + + if(cyclic(p)) + g=straight(p,last-1) ? g--cycle : + g..controls project(postcontrol(p,last-1),t) and + project(precontrol(p,last),t)..cycle; + return g; +} + +pair[] project(triple[] v, projection P=currentprojection) +{ + return sequence(new pair(int i) {return project(v[i],P.t);},v.length); +} + +path[] project(explicit path3[] g, projection P=currentprojection) +{ + return sequence(new path(int i) {return project(g[i],P);},g.length); +} + +guide3 operator cast(path3 p) +{ + int last=length(p); + + bool cyclic=cyclic(p); + int stop=cyclic ? last-1 : last; + return new void(flatguide3 f) { + if(last >= 0) { + f.node(point(p,0)); + for(int i=0; i < stop; ++i) { + if(straight(p,i)) { + f.out(1); + f.in(1); + } else + f.control(postcontrol(p,i),precontrol(p,i+1)); + f.node(point(p,i+1)); + } + if(cyclic) { + if(straight(p,stop)) { + f.out(1); + f.in(1); + } else + f.control(postcontrol(p,stop),precontrol(p,last)); + f.cycleToken(); + } + } + }; +} + +// Return a unit normal vector to a planar path p (or O if the path is +// nonplanar). +triple normal(path3 p) +{ + triple normal; + real fuzz=sqrtEpsilon*abs(max(p)-min(p)); + real absnormal; + real theta; + + bool Cross(triple a, triple b) { + if(abs(a) >= fuzz && abs(b) >= fuzz) { + triple n=cross(unit(a),unit(b)); + real absn=abs(n); + if(absn < sqrtEpsilon) return false; + n=unit(n); + if(absnormal > 0 && + abs(normal-n) > sqrtEpsilon && abs(normal+n) > sqrtEpsilon) + return true; + else { + int sign=dot(n,normal) >= 0 ? 1 : -1; + theta += sign*asin1(absn); + if(absn > absnormal) { + absnormal=absn; + normal=n; + theta=sign*theta; + } + } + } + return false; + } + + int L=length(p); + if(L <= 0) return O; + + triple zi=point(p,0); + triple v0=zi-precontrol(p,0); + for(int i=0; i < L; ++i) { + triple c0=postcontrol(p,i); + triple c1=precontrol(p,i+1); + triple zp=point(p,i+1); + triple v1=c0-zi; + triple v2=c1-c0; + triple v3=zp-c1; + if(Cross(v0,v1) || Cross(v1,v2) || Cross(v2,v3)) return O; + v0=v3; + zi=zp; + } + return theta >= 0 ? normal : -normal; +} + +// Return a unit normal vector to a polygon with vertices in p. +triple normal(triple[] p) +{ + triple normal; + real fuzz=sqrtEpsilon*abs(maxbound(p)-minbound(p)); + real absnormal; + real theta; + + bool Cross(triple a, triple b) { + if(abs(a) >= fuzz && abs(b) >= fuzz) { + triple n=cross(unit(a),unit(b)); + real absn=abs(n); + n=unit(n); + if(absnormal > 0 && absn > sqrtEpsilon && + abs(normal-n) > sqrtEpsilon && abs(normal+n) > sqrtEpsilon) + return true; + else { + int sign=dot(n,normal) >= 0 ? 1 : -1; + theta += sign*asin1(absn); + if(absn > absnormal) { + absnormal=absn; + normal=n; + theta=sign*theta; + } + } + } + return false; + } + + if(p.length <= 0) return O; + + triple zi=p[0]; + triple v0=zi-p[p.length-1]; + for(int i=0; i < p.length-1; ++i) { + triple zp=p[i+1]; + triple v1=zp-zi; + if(Cross(v0,v1)) return O; + v0=v1; + zi=zp; + } + return theta >= 0 ? normal : -normal; +} + +// Transforms that map XY plane to YX, YZ, ZY, ZX, and XZ planes. +restricted transform3 XY=identity4; +restricted transform3 YX=rotate(-90,O,Z); +restricted transform3 YZ=rotate(90,O,Z)*rotate(90,O,X); +restricted transform3 ZY=rotate(-90,O,X)*YZ; +restricted transform3 ZX=rotate(-90,O,Z)*rotate(-90,O,Y); +restricted transform3 XZ=rotate(-90,O,Y)*ZX; + +private transform3 flip(transform3 t, triple X, triple Y, triple Z, + projection P) +{ + static transform3 flip(triple v) { + static real s(real x) {return x > 0 ? -1 : 1;} + return scale(s(v.x),s(v.y),s(v.z)); + } + + triple u=unit(P.normal); + triple up=unit(perp(P.up,u)); + bool upright=dot(Z,u) >= 0; + if(dot(Y,up) < 0) { + t=flip(Y)*t; + upright=!upright; + } + return upright ? t : flip(X)*t; +} + +restricted transform3 XY(projection P=currentprojection) +{ + return flip(XY,X,Y,Z,P); +} + +restricted transform3 YX(projection P=currentprojection) +{ + return flip(YX,Y,X,Z,P); +} + +restricted transform3 YZ(projection P=currentprojection) +{ + return flip(YZ,Y,Z,X,P); +} + +restricted transform3 ZY(projection P=currentprojection) +{ + return flip(ZY,Z,Y,X,P); +} + +restricted transform3 ZX(projection P=currentprojection) +{ + return flip(ZX,Z,X,Y,P); +} + +restricted transform3 XZ(projection P=currentprojection) +{ + return flip(XZ,X,Z,Y,P); +} + +// Transform3 that projects in direction dir onto plane with normal n +// through point O. +transform3 planeproject(triple n, triple O=O, triple dir=n) +{ + real a=n.x, b=n.y, c=n.z; + real u=dir.x, v=dir.y, w=dir.z; + real delta=1.0/(a*u+b*v+c*w); + real d=-(a*O.x+b*O.y+c*O.z)*delta; + return new real[][] { + {(b*v+c*w)*delta,-b*u*delta,-c*u*delta,-d*u}, + {-a*v*delta,(a*u+c*w)*delta,-c*v*delta,-d*v}, + {-a*w*delta,-b*w*delta,(a*u+b*v)*delta,-d*w}, + {0,0,0,1} + }; +} + +// Transform3 that projects in direction dir onto plane defined by p. +transform3 planeproject(path3 p, triple dir=O) +{ + triple n=normal(p); + return planeproject(n,point(p,0),dir == O ? n : dir); +} + +// Transform for projecting onto plane through point O with normal cross(u,v). +transform transform(triple u, triple v, triple O=O, + projection P=currentprojection) +{ + transform3 t=P.t; + real[] tO=t*new real[] {O.x,O.y,O.z,1}; + 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); +} + +// Align Label with normal in direction dir. +Label align(Label L, triple dir) +{ + Label L=L.copy(); + L.transform3(align(unit(dir))); + return L; +} + +// return a rotation that maps X,Y to the projection plane. +transform3 transform3(projection P=currentprojection) +{ + triple w=unit(P.normal); + triple v=unit(perp(P.up,w)); + if(v == O) v=cross(perp(w),w); + triple u=cross(v,w); + return u != O ? transform3(u,v,w) : identity(4); +} + +triple[] triples(real[] x, real[] y, real[] z) +{ + if(x.length != y.length || x.length != z.length) + abort("arrays have different lengths"); + return sequence(new triple(int i) {return (x[i],y[i],z[i]);},x.length); +} + +path3[] operator cast(path3 p) +{ + return new path3[] {p}; +} + +path3[] operator cast(guide3 g) +{ + return new path3[] {(path3) g}; +} + +path3[] operator ^^ (path3 p, path3 q) +{ + return new path3[] {p,q}; +} + +path3[] operator ^^ (path3 p, explicit path3[] q) +{ + return concat(new path3[] {p},q); +} + +path3[] operator ^^ (explicit path3[] p, path3 q) +{ + return concat(p,new path3[] {q}); +} + +path3[] operator ^^ (explicit path3[] p, explicit path3[] q) +{ + return concat(p,q); +} + +path3[] operator * (transform3 t, explicit path3[] p) +{ + return sequence(new path3(int i) {return t*p[i];},p.length); +} + +triple[] operator * (transform3 t, triple[] v) +{ + return sequence(new triple(int i) {return t*v[i];},v.length); +} + +triple[][] operator * (transform3 t, triple[][] v) +{ + triple[][] V=new triple[v.length][]; + for(int i=0; i < v.length; ++i) { + triple[] vi=v[i]; + V[i]=sequence(new triple(int j) {return t*vi[j];},vi.length); + } + return V; +} + +triple min(explicit path3[] p) +{ + checkEmpty(p.length); + triple minp=min(p[0]); + for(int i=1; i < p.length; ++i) + minp=minbound(minp,min(p[i])); + return minp; +} + +triple max(explicit path3[] p) +{ + checkEmpty(p.length); + triple maxp=max(p[0]); + for(int i=1; i < p.length; ++i) + maxp=maxbound(maxp,max(p[i])); + return maxp; +} + +path3 randompath3(int n, bool cumulate=true, interpolate3 join=operator ..) +{ + guide3 g; + triple w; + for(int i=0; i <= n; ++i) { + triple z=(unitrand()-0.5,unitrand()-0.5,unitrand()-0.5); + if(cumulate) w += z; + else w=z; + g=join(g,w); + } + return g; +} + +path3[] box(triple v1, triple v2) +{ + return + (v1.x,v1.y,v1.z)-- + (v1.x,v1.y,v2.z)-- + (v1.x,v2.y,v2.z)-- + (v1.x,v2.y,v1.z)-- + (v1.x,v1.y,v1.z)-- + (v2.x,v1.y,v1.z)-- + (v2.x,v1.y,v2.z)-- + (v2.x,v2.y,v2.z)-- + (v2.x,v2.y,v1.z)-- + (v2.x,v1.y,v1.z)^^ + (v2.x,v2.y,v1.z)-- + (v1.x,v2.y,v1.z)^^ + (v1.x,v2.y,v2.z)-- + (v2.x,v2.y,v2.z)^^ + (v2.x,v1.y,v2.z)-- + (v1.x,v1.y,v2.z); +} + +restricted path3[] unitbox=box(O,(1,1,1)); +restricted path3 unitcircle3=X..Y..-X..-Y..cycle; +restricted path3 unitsquare3=O--X--X+Y--Y--cycle; + +path3 circle(triple c, real r, triple normal=Z) +{ + path3 p=normal == Z ? unitcircle3 : align(unit(normal))*unitcircle3; + return shift(c)*scale3(r)*p; +} + +// return an arc centered at c from triple v1 to v2 (assuming |v2-c|=|v1-c|), +// drawing in the given direction. +// The normal must be explicitly specified if c and the endpoints are colinear. +path3 arc(triple c, triple v1, triple v2, triple normal=O, bool direction=CCW) +{ + v1 -= c; + real r=abs(v1); + v1=unit(v1); + v2=unit(v2-c); + + if(normal == O) { + normal=cross(v1,v2); + if(normal == O) abort("explicit normal required for these endpoints"); + } + + transform3 T; + bool align=normal != Z; + if(align) { + T=align(unit(normal)); + transform3 Tinv=transpose(T); + v1=Tinv*v1; + v2=Tinv*v2; + } + + string invalidnormal="invalid normal vector"; + real fuzz=sqrtEpsilon; + if(abs(v1.z) > fuzz || abs(v2.z) > fuzz) + abort(invalidnormal); + + real[] t1=intersect(unitcircle3,O--2*(v1.x,v1.y,0)); + real[] t2=intersect(unitcircle3,O--2*(v2.x,v2.y,0)); + + if(t1.length == 0 || t2.length == 0) + abort(invalidnormal); + + real t1=t1[0]; + real t2=t2[0]; + int n=length(unitcircle3); + if(direction) { + if(t1 >= t2) t1 -= n; + } else if(t2 >= t1) t2 -= n; + + path3 p=subpath(unitcircle3,t1,t2); + if(align) p=T*p; + return shift(c)*scale3(r)*p; +} + +// return an arc centered at c with radius r from c+r*dir(theta1,phi1) to +// c+r*dir(theta2,phi2) in degrees, drawing in the given direction +// relative to the normal vector cross(dir(theta1,phi1),dir(theta2,phi2)). +// The normal must be explicitly specified if c and the endpoints are colinear. +path3 arc(triple c, real r, real theta1, real phi1, real theta2, real phi2, + triple normal=O, bool direction) +{ + return arc(c,c+r*dir(theta1,phi1),c+r*dir(theta2,phi2),normal,direction); +} + +// return an arc centered at c with radius r from c+r*dir(theta1,phi1) to +// c+r*dir(theta2,phi2) in degrees, drawing drawing counterclockwise +// relative to the normal vector cross(dir(theta1,phi1),dir(theta2,phi2)) +// iff theta2 > theta1 or (theta2 == theta1 and phi2 >= phi1). +// The normal must be explicitly specified if c and the endpoints are colinear. +path3 arc(triple c, real r, real theta1, real phi1, real theta2, real phi2, + triple normal=O) +{ + return arc(c,r,theta1,phi1,theta2,phi2,normal, + theta2 > theta1 || (theta2 == theta1 && phi2 >= phi1) ? CCW : CW); +} + +private real epsilon=1000*realEpsilon; + +// Return a representation of the plane through point O with normal cross(u,v). +path3 plane(triple u, triple v, triple O=O) +{ + return O--O+u--O+u+v--O+v--cycle; +} + +// PRC/OpenGL support + +include three_light; + +void draw(frame f, path3 g, material p=currentpen, light light=nolight, + string name="", render render=defaultrender, + projection P=currentprojection); + +void begingroup3(frame f, string name="", render render=defaultrender, + triple center=O, int interaction=0) +{ + _begingroup3(f,name,render.compression,render.granularity,render.closed, + render.tessellate,render.merge == false, + render.merge == true,center,interaction); +} + +void begingroup3(picture pic=currentpicture, string name="", + render render=defaultrender, + triple center=O, int interaction=0) +{ + pic.add(new void(frame f, transform3, picture pic, projection) { + if(is3D()) + begingroup3(f,name,render,center,interaction); + if(pic != null) + begingroup(pic); + },true); +} + +void endgroup3(picture pic=currentpicture) +{ + pic.add(new void(frame f, transform3, picture pic, projection) { + if(is3D()) + endgroup3(f); + if(pic != null) + endgroup(pic); + },true); +} + +void addPath(picture pic, path3 g, pen p) +{ + if(size(g) > 0) + pic.addBox(min(g),max(g),min3(p),max3(p)); +} + +include three_surface; +include three_margins; + +pair min(path3 p, projection P) +{ + path3 q=P.T.modelview*p; + if(P.infinity) + return xypart(min(q)); + return maxratio(q)/P.T.projection[3][2]; +} + +pair max(path3 p, projection P) +{ + path3 q=P.T.modelview*p; + if(P.infinity) + return xypart(max(q)); + return minratio(q)/P.T.projection[3][2]; +} + +pair min(frame f, projection P) +{ + frame g=P.T.modelview*f; + if(P.infinity) + return xypart(min3(g)); + return maxratio(g)/P.T.projection[3][2]; +} + +pair max(frame f, projection P) +{ + frame g=P.T.modelview*f; + if(P.infinity) + return xypart(max3(g)); + return minratio(g)/P.T.projection[3][2]; +} + +void draw(picture pic=currentpicture, Label L="", path3 g, + align align=NoAlign, material p=currentpen, margin3 margin=NoMargin3, + light light=nolight, string name="", + render render=defaultrender) +{ + pen q=(pen) p; + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + path3 G=margin(t*g,q).g; + if(is3D()) { + draw(f,G,p,light,name,render,null); + if(pic != null && size(G) > 0) + pic.addBox(min(G,P),max(G,P),min(q),max(q)); + } + if(pic != null) + draw(pic,project(G,P),q); + },true); + Label L=L.copy(); + L.align(align); + if(L.s != "") { + L.p(q); + label(pic,L,g); + } + addPath(pic,g,q); +} + +include three_tube; + +draw=new void(frame f, path3 g, material p=currentpen, + light light=nolight, string name="", + render render=defaultrender, + projection P=currentprojection) { + pen q=(pen) p; + if(is3D()) { + real width=linewidth(q); + void drawthick(path3 g) { + if(settings.thick && width > 0) { + bool prc=prc(); + bool webgl=settings.outformat == "html"; + real linecap=linecap(q); + real r=0.5*width; + bool open=!cyclic(g); + int L=length(g); + triple g0=point(g,0); + triple gL=point(g,L); + if(open && L > 0) { + if(linecap == 2) { + g0 -= r*dir(g,0); + gL += r*dir(g,L); + g=g0..g..gL; + L += 2; + } + } + tube T=tube(g,width); + path3 c=T.center; + if(L >= 0) { + if(open) { + int Lc=length(c); + triple c0=point(c,0); + triple cL=point(c,Lc); + triple dir0=dir(g,0); + triple dirL=dir(g,L); + triple dirc0=dir(c,0); + triple dircL=dir(c,Lc); + transform3 t0=shift(g0)*align(-dir0); + transform3 tL=shift(gL)*align(dirL); + transform3 tc0=shift(c0)*align(-dirc0); + transform3 tcL=shift(cL)*align(dircL); + if(linecap == 0 || linecap == 2) { + transform3 scale2r=scale(r,r,1); + T.s.push(t0*scale2r*unitdisk); + if(L > 0) { + T.s.push(tL*scale2r*unitdisk); + } + } else if(linecap == 1) { + transform3 scale3r=scale3(r); + T.s.push(t0*scale3r*(straight(c,0) ? + unithemisphere : unitsphere)); + if(L > 0) + T.s.push(tL*scale3r*(straight(c,Lc-1) ? + unithemisphere : unitsphere)); + } + } +// Draw central core for better small-scale rendering. + if((!prc || piecewisestraight(g)) && !webgl && opacity(q) == 1) + _draw(f,c,p,light); + } + for(surface s : T.s) + draw(f,s,p,light,render); + } else _draw(f,g,p,light); + } + bool group=q != nullpen && (name != "" || render.defaultnames); + if(group) + begingroup3(f,name == "" ? "curve" : name,render); + if(linetype(q).length == 0) drawthick(g); + else { + real[] dash=linetype(adjust(q,arclength(g),cyclic(g))); + if(sum(dash) > 0) { + dash.cyclic=true; + real offset=offset(q); + real L=arclength(g); + int i=0; + real l=offset; + while(l <= L) { + real t1=arctime(g,l); + l += dash[i]; + real t2=arctime(g,min(l,L)); + drawthick(subpath(g,t1,t2)); + ++i; + l += dash[i]; + ++i; + } + } + } + if(group) + endgroup3(f); + } else draw(f,project(g,P),q); +}; + +void draw(frame f, explicit path3[] g, material p=currentpen, + light light=nolight, string name="", + render render=defaultrender, projection P=currentprojection) +{ + bool group=g.length > 1 && (name != "" || render.defaultnames); + if(group) + begingroup3(f,name == "" ? "curves" : name,render); + for(int i=0; i < g.length; ++i) + draw(f,g[i],p,light,partname(i,render),render,P); + if(group) + endgroup3(f); +} + +void draw(picture pic=currentpicture, explicit path3[] g, + material p=currentpen, margin3 margin=NoMargin3, light light=nolight, + string name="", render render=defaultrender) +{ + bool group=g.length > 1 && (name != "" || render.defaultnames); + if(group) + begingroup3(pic,name == "" ? "curves" : name,render); + for(int i=0; i < g.length; ++i) + draw(pic,g[i],p,margin,light,partname(i,render),render); + if(group) + endgroup3(pic); +} + +include three_arrows; + +void draw(picture pic=currentpicture, Label L="", path3 g, + align align=NoAlign, material p=currentpen, arrowbar3 arrow, + arrowbar3 bar=None, margin3 margin=NoMargin3, light light=nolight, + light arrowheadlight=currentlight, string name="", + render render=defaultrender) +{ + bool group=arrow != None || bar != None; + if(group) + begingroup3(pic,name,render); + bool drawpath=arrow(pic,g,p,margin,light,arrowheadlight); + if(bar(pic,g,p,margin,light,arrowheadlight) && drawpath) + draw(pic,L,g,align,p,margin,light,render); + if(group) + endgroup3(pic); + if(L.s != "") + label(pic,L,g,align,(pen) p); +} + +void draw(frame f, path3 g, material p=currentpen, arrowbar3 arrow, + light light=nolight, light arrowheadlight=currentlight, + string name="", render render=defaultrender, + projection P=currentprojection) +{ + picture pic; + bool group=arrow != None; + if(group) + begingroup3(f,name,render); + if(arrow(pic,g,p,NoMargin3,light,arrowheadlight)) + draw(f,g,p,light,render,P); + add(f,pic.fit()); + if(group) + endgroup3(f); +} + +void add(picture pic=currentpicture, void d(picture,transform3), + bool exact=false) +{ + pic.add(d,exact); +} + +// Fit the picture src using the identity transformation (so user +// coordinates and truesize coordinates agree) and add it about the point +// position to picture dest. +void add(picture dest, picture src, triple position, bool group=true, + bool above=true) +{ + dest.add(new void(picture f, transform3 t) { + f.add(shift(t*position)*src,group,above); + }); +} + +void add(picture src, triple position, bool group=true, bool above=true) +{ + add(currentpicture,src,position,group,above); +} + +// Align an arrow pointing to b from the direction dir. The arrow is +// 'length' PostScript units long. +void arrow(picture pic=currentpicture, Label L="", triple b, triple dir, + real length=arrowlength, align align=NoAlign, + pen p=currentpen, arrowbar3 arrow=Arrow3, margin3 margin=EndMargin3, + light light=nolight, light arrowheadlight=currentlight, + string name="", render render=defaultrender) +{ + Label L=L.copy(); + if(L.defaultposition) L.position(0); + L.align(L.align,dir); + L.p(p); + picture opic; + marginT3 margin=margin(b--b,p); // Extract margin.begin and margin.end + triple a=(margin.begin+length+margin.end)*unit(dir); + draw(opic,L,a--O,align,p,arrow,margin,light,arrowheadlight,name,render); + add(pic,opic,b); +} + +void arrow(picture pic=currentpicture, Label L="", triple b, pair dir, + real length=arrowlength, align align=NoAlign, + pen p=currentpen, arrowbar3 arrow=Arrow3, margin3 margin=EndMargin3, + light light=nolight, light arrowheadlight=currentlight, + string name="", render render=defaultrender, + projection P=currentprojection) +{ + arrow(pic,L,b,invert(dir,b,P),length,align,p,arrow,margin,light, + arrowheadlight,name,render); +} + +triple min3(picture pic, projection P=currentprojection) +{ + return pic.min3(P); +} + +triple max3(picture pic, projection P=currentprojection) +{ + return pic.max3(P); +} + +triple size3(picture pic, bool user=false, projection P=currentprojection) +{ + transform3 t=pic.calculateTransform3(P); + triple M=pic.max(t); + triple m=pic.min(t); + if(!user) return M-m; + t=inverse(t); + return t*M-t*m; +} + +triple point(frame f, triple dir) +{ + triple m=min3(f); + triple M=max3(f); + return m+realmult(rectify(dir),M-m); +} + +triple point(picture pic=currentpicture, triple dir, bool user=true, + projection P=currentprojection) +{ + triple min = pic.userMin(), max = pic.userMax(); + triple v=min+realmult(rectify(dir),max-min); + return user ? v : pic.calculateTransform3(P)*v; +} + +triple truepoint(picture pic=currentpicture, triple dir, bool user=true, + projection P=currentprojection) +{ + transform3 t=pic.calculateTransform3(P); + triple m=pic.min(t); + triple M=pic.max(t); + triple v=m+realmult(rectify(dir),M-m); + return user ? inverse(t)*v : v; +} + +void add(picture dest=currentpicture, object src, pair position=0, pair align=0, + bool group=true, filltype filltype=NoFill, bool above=true) +{ + if(prc()) + label(dest,src,position,align); + else if(settings.render == 0) + plain.add(dest,src,position,align,group,filltype,above); +} + +private struct viewpoint { + triple target,camera,up; + real angle; + void operator init(string s) { + s=replace(s,'\n'," "); + string[] S=split(s); + int pos(string s, string key) { + int pos=find(s,key); + if(pos < 0) return -1; + pos += length(key); + while(substr(s,pos,1) == " ") ++pos; + if(substr(s,pos,1) == "=") + return pos+1; + return -1; + } + triple C2C=X; + real ROO=1; + real ROLL=0; + angle=30; + int pos; + for(int k=0; k < S.length; ++k) { + if((pos=pos(S[k],"COO")) >= 0) + target=((real) substr(S[k],pos),(real) S[++k],(real) S[++k]); + else if((pos=pos(S[k],"C2C")) >= 0) + C2C=((real) substr(S[k],pos),(real) S[++k],(real) S[++k]); + else if((pos=pos(S[k],"ROO")) >= 0) + ROO=(real) substr(S[k],pos); + else if((pos=pos(S[k],"ROLL")) >= 0) + ROLL=(real) substr(S[k],pos); + else if((pos=pos(S[k],"AAC")) >= 0) + angle=(real) substr(S[k],pos); + } + camera=target+ROO*C2C; + triple u=unit(target-camera); + triple w=unit(Z-u.z*u); + up=rotate(ROLL,O,u)*w; + } +} + +projection perspective(string s) +{ + viewpoint v=viewpoint(s); + projection P=perspective(v.camera,v.up,v.target); + P.angle=v.angle; + P.absolute=true; + return P; +} + +projection absorthographic(triple camera=Z, triple target=O, real roll=0) +{ + triple u=unit(target-camera); + triple w=unit(Z-u.z*u); + triple up=rotate(roll,O,u)*w; + projection P= + projection(camera,up,target,1,0,false,false, + new transformation(triple camera, triple up, triple target) + {return transformation(look(camera,up,target));}); + P.absolute=true; + return P; +} + +projection absperspective(triple camera=Z, triple target=O, real roll=0, + real angle=30) +{ + triple u=unit(target-camera); + triple w=unit(Z-u.z*u); + triple up=rotate(roll,O,u)*w; + projection P=perspective(camera,up,target); + P.angle=angle; + P.absolute=true; + return P; +} + +private string Format(real x) +{ + assert(abs(x) < 1e17,"Number too large: "+string(x)); + return format("%.9f",x,"C"); +} + +private string Format(triple v, string sep=" ") +{ + return Format(v.x)+sep+Format(v.y)+sep+Format(v.z); +} + +private string Format(real[] c) +{ + return Format((c[0],c[1],c[2])); +} + +private string format(triple v, string sep=" ") +{ + return string(v.x)+sep+string(v.y)+sep+string(v.z); +} + +private string Format(transform3 t, string sep=" ") +{ + return + Format(t[0][0])+sep+Format(t[1][0])+sep+Format(t[2][0])+sep+ + Format(t[0][1])+sep+Format(t[1][1])+sep+Format(t[2][1])+sep+ + Format(t[0][2])+sep+Format(t[1][2])+sep+Format(t[2][2])+sep+ + Format(t[0][3])+sep+Format(t[1][3])+sep+Format(t[2][3]); +} + +pair viewportmargin(pair lambda) +{ + return maxbound(0.5*(viewportsize-lambda),viewportmargin); +} + +string embed3D(string prefix, string label=prefix, string text=label, + frame f, string format="", + real width=0, real height=0, + string options="", string script="", + light light=currentlight, projection P=currentprojection, + real viewplanesize=0) +{ + if(!prc(format) || Embed == null) return ""; + + if(width == 0) width=settings.paperwidth; + if(height == 0) height=settings.paperheight; + + if(script == "") script=defaultembed3Dscript; + + if(P.infinity) { + if(viewplanesize == 0) { + triple lambda=max3(f)-min3(f); + pair margin=viewportmargin((lambda.x,lambda.y)); + viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/P.zoom; + } + } else + if(!P.absolute) P.angle=2*aTan(Tan(0.5*P.angle)); + + shipout3(prefix,f); + + string name=prefix+".js"; + + if(!settings.inlinetex && !prconly()) + file3.push(prefix+".prc"); + + static transform3 flipxz=xscale3(-1)*zscale3(-1); + transform3 inv=inverse(flipxz*P.T.modelview); + + string options3="3Dlights="+ + (light.on() ? "Headlamp" : "None"); + if(defaultembed3Doptions != "") options3 += ","+defaultembed3Doptions; + + if((settings.render < 0 || !settings.embed) && settings.auto3D) + options3 += ",activate=pagevisible"; + options3 += ",3Dtoolbar="+(settings.toolbar ? "true" : "false")+ + ",label="+label+ + (P.infinity ? ",3Dortho="+Format(1/viewplanesize) : + ",3Daac="+Format(P.angle))+ + ",3Dc2w="+Format(inv)+ + ",3Droo="+Format(abs(P.vector()))+ + ",3Dpsob="+(P.infinity ? "Max" : "H")+ + ",3Dbg="+Format(light.background()); + if(options != "") options3 += ","+options; + if(settings.inlinetex) + prefix=jobname(prefix); + options3 += ",add3Djscript=asylabels.js"; + + return text == "" ? Embed(prefix+".prc","",options3,width,height) : + "\hbox to 0pt{"+text+"\hss}"+Embed(prefix+".prc","\phantom{"+text+"}", + options3); +} + +struct scene +{ + frame f; + transform3 t; + projection P; + bool adjusted; + real width,height; + pair viewportmargin; + transform3 T=identity4; + picture pic2; + bool keepAspect=true; + + void operator init(frame f, real width, real height, + projection P=currentprojection) { + this.f=f; + this.t=identity4; + this.P=P; + this.width=width; + this.height=height; + } + + void operator init(picture pic, real xsize=pic.xsize, real ysize=pic.ysize, + bool keepAspect=pic.keepAspect, bool is3D=true, + projection P=currentprojection) { + real xsize3=pic.xsize3, ysize3=pic.ysize3, zsize3=pic.zsize3; + bool warn=true; + this.keepAspect=keepAspect; + + if(xsize3 == 0 && ysize3 == 0 && zsize3 == 0) { + xsize3=ysize3=zsize3=max(xsize,ysize); + warn=false; + } + + if(P.absolute) + this.P=P.copy(); + else if(P.showtarget) + draw(pic,P.target,nullpen); + + t=pic.scaling(xsize3,ysize3,zsize3,keepAspect,warn); + adjusted=false; + triple m=pic.min(t); + triple M=pic.max(t); + + if(!P.absolute) { + this.P=t*P; + if(this.P.autoadjust || this.P.infinity) + adjusted=adjusted | this.P.adjust(m,M); + if(this.P.center && settings.render != 0) { + triple target=0.5*(m+M); + this.P.target=target; + this.P.calculate(); + } + } + + bool scale=xsize != 0 || ysize != 0; + bool scaleAdjust=scale && this.P.autoadjust; + bool noAdjust=this.P.absolute || !scaleAdjust; + + if(pic.bounds3.exact && noAdjust) + this.P.bboxonly=false; + + f=pic.fit3(t,pic.bounds3.exact ? pic2 : null,this.P); + + if(!pic.bounds3.exact) { + if(noAdjust) + this.P.bboxonly=false; + + transform3 s=pic.scale3(f,xsize3,ysize3,zsize3,keepAspect); + t=s*t; + this.P=s*this.P; + f=pic.fit3(t,pic2,this.P); + } + + if(is3D || scale) { + pic2.bounds.exact=true; + transform s=pic2.scaling(xsize,ysize,keepAspect); + + pair m2=pic2.min(s); + pair M2=pic2.max(s); + pair lambda=M2-m2; + viewportmargin=viewportmargin(lambda); + width=ceil(lambda.x+2*viewportmargin.x); + height=ceil(lambda.y+2*viewportmargin.y); + + if(!this.P.absolute) { + if(scaleAdjust) { + pair v=(s.xx,s.yy); + transform3 T=this.P.t; + pair x=project(X,T); + pair y=project(Y,T); + pair z=project(Z,T); + real f(pair a, pair b) { + return b == 0 ? (0.5*(a.x+a.y)) : + (b.x^2*a.x+b.y^2*a.y)/(b.x^2+b.y^2); + } + transform3 s=keepAspect ? scale3(min(f(v,x),f(v,y),f(v,z))) : + xscale3(f(v,x))*yscale3(f(v,y))*zscale3(f(v,z)); + s=shift(this.P.target)*s*shift(-this.P.target); + t=s*t; + this.P=s*this.P; + this.P.bboxonly=false; + if(!is3D) pic2.erase(); + f=pic.fit3(t,is3D ? null : pic2,this.P); + } + + if(this.P.autoadjust || this.P.infinity) + adjusted=adjusted | this.P.adjust(min3(f),max3(f)); + } + } + } + + // Choose the angle to be just large enough to view the entire image. + real angle(projection P) { + T=identity4; + real h=-0.5*P.target.z; + pair r,R; + real diff=realMax; + pair s; + int i; + do { + r=minratio(f); + R=maxratio(f); + pair lasts=s; + if(P.autoadjust) { + s=r+R; + if(s != 0) { + transform3 t=shift(h*s.x,h*s.y,0); + f=t*f; + T=t*T; + adjusted=true; + } + } + diff=abs(s-lasts); + ++i; + } while (diff > angleprecision && i < maxangleiterations); + real aspect=width > 0 ? height/width : 1; + real rx=-r.x*aspect; + real Rx=R.x*aspect; + real ry=-r.y; + real Ry=R.y; + if(!P.autoadjust) { + if(rx > Rx) Rx=rx; + else rx=Rx; + if(ry > Ry) Ry=ry; + else ry=Ry; + } + return (1+angleprecision)*max(aTan(rx)+aTan(Rx),aTan(ry)+aTan(Ry)); + } +} + +object embed(string prefix=outprefix(), string label=prefix, + string text=label, scene S, string format="", bool view=true, + string options="", string script="", light light=currentlight) +{ + object F; + transform3 modelview; + projection P=S.P; + transform3 tinv=inverse(S.t); + + projection Q; + triple orthoshift; + modelview=P.T.modelview; + transform3 inv; + if(P.absolute) { + Q=modelview*P; + inv=inverse(modelview); + } else { + triple target=P.target; + S.f=modelview*S.f; + P=modelview*P; + Q=P.copy(); + + if(Q.t[2][3] == -1) // PRC can't handle oblique projections + Q=orthographic(P.camera,P.up,P.target,P.zoom,P.viewportshift, + P.showtarget,P.center); + if(P.infinity) { + triple m=min3(S.f); + triple M=max3(S.f); + triple lambda=M-m; + if(S.keepAspect) { + S.viewportmargin=viewportmargin((lambda.x,lambda.y)); + S.width=ceil(lambda.x+2*S.viewportmargin.x); + S.height=ceil(lambda.y+2*S.viewportmargin.y); + } + orthoshift=(-0.5(m.x+M.x),-0.5*(m.y+M.y),0); + S.f=shift(orthoshift)*S.f; // Eye will be at (0,0,0) + inv=inverse(modelview); + } else { + if(P.angle == 0) { + P.angle=S.angle(P); + modelview=S.T*modelview; + if(S.viewportmargin.y != 0) + P.angle=2*aTan(Tan(0.5*P.angle)-S.viewportmargin.y/P.target.z); + } + inv=inverse(modelview); + Q.angle=P.angle; + if(settings.verbose > 0) { + if(S.adjusted) + write("adjusting camera to ",tinv*inv*P.camera); + target=inv*P.target; + } + P=S.T*P; + } + if(settings.verbose > 0) { + if((P.center && settings.render != 0) || (!P.infinity && P.autoadjust)) + write("adjusting target to ",tinv*target); + } + } + light Light=modelview*light; + + if(prefix == "") prefix=outprefix(); + bool prc=prc(format); + bool preview=settings.render > 0 && !prconly(); + if(prc) { + // The media9.sty package cannot handle spaces or dots in filenames. + string dir=stripfile(prefix); + prefix=dir+replace(stripdirectory(prefix), + new string[][]{{" ","_"},{".","_"}}); + if((settings.embed || nativeformat() == "pdf") && !prconly()) + prefix += "+"+(string) file3.length; + } else + preview=false; + if(preview || (!prc && settings.render != 0)) { + frame f=S.f; + triple m,M; + real zcenter; + real r; + if(P.absolute) { + f=modelview*f; + m=min3(f); + M=max3(f); + r=0.5*abs(M-m); + zcenter=0.5*(M.z+m.z); + } else { + m=min3(f); + M=max3(f); + zcenter=P.target.z; + r=P.distance(m,M); + } + M=(M.x,M.y,zcenter+r); + m=(m.x,m.y,zcenter-r); + + if(P.infinity) { + triple margin=(S.viewportmargin.x,S.viewportmargin.y,0); + M += margin; + m -= margin; + } else if(M.z >= 0) abort("camera too close"); + + if(settings.outformat == "html") + format="html"; + + shipout3(prefix,f,preview ? nativeformat() : format, + S.width-defaultrender.margin,S.height-defaultrender.margin, + P.infinity ? 0 : 2aTan(Tan(0.5*P.angle)*P.zoom), + P.zoom,m,M,P.viewportshift,S.viewportmargin, + tinv*inv*shift(0,0,zcenter),Light.background(),Light.position, + Light.diffuse,Light.specular, + view && !preview); + if(!preview) return F; + } + + string image; + if((preview || (prc && settings.render == 0)) && settings.embed) { + image=prefix; + if(settings.inlinetex) image += "_0"; + if(!preview && !S.pic2.empty2()) { + transform T=S.pic2.scaling(S.width,S.height); + _shipout(image,S.pic2.fit(T),newframe,nativeformat(),false,false); + } + + image += "."+nativeformat(); + if(!settings.inlinetex) file3.push(image); + image=graphic(image,"hiresbb"); + } + if(prc) { + if(P.viewportshift != 0) { + if(!P.infinity) + warning("offaxis", + "PRC does not support off-axis projections; use pan instead of +shift"); + + triple lambda=max3(S.f)-min3(S.f); + Q.target -= (P.viewportshift.x*lambda.x/P.zoom, + P.viewportshift.y*lambda.y/P.zoom,0); + } + + real viewplanesize=0; + if(P.absolute) { + if(P.infinity) { + S.f=modelview*S.f; + triple lambda=max3(S.f)-min3(S.f); + pair margin=viewportmargin((lambda.x,lambda.y)); + viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/Q.zoom; + S.f=inv*S.f; + } + Q=inv*Q; + } else { + if(P.infinity) { + triple lambda=max3(S.f)-min3(S.f); + pair margin=viewportmargin((lambda.x,lambda.y)); + viewplanesize=(max(lambda.x+2*margin.x,lambda.y+2*margin.y))/(Q.zoom); + transform3 t=inv*shift(-orthoshift); + Q=t*Q; + S.f=t*S.f; + } else { + Q=inv*Q; + S.f=inv*S.f; + } + } + F.L=embed3D(prefix,label,text=image,S.f,format, + S.width-2,S.height-2,options,script,light,Q,viewplanesize); + } + return F; +} + +object embed(string prefix=outprefix(), string label=prefix, + string text=label, picture pic, string format="", + real xsize=pic.xsize, real ysize=pic.ysize, + bool keepAspect=pic.keepAspect, bool view=true, string options="", + string script="", light light=currentlight, + projection P=currentprojection) +{ + bool is3D=is3D(format); + scene S=scene(pic,xsize,ysize,keepAspect,is3D,P); + if(is3D) + return embed(prefix,label,text,S,format,view,options,script,light); + else { + object F; + transform T=S.pic2.scaling(xsize,ysize,keepAspect); + F.f=pic.fit(scale(S.t[0][0])*T); + add(F.f,S.pic2.fit()); + return F; + } +} + +object embed(string prefix=outprefix(), string label=prefix, + string text=label, + frame f, string format="", real width=0, real height=0, + bool view=true, string options="", string script="", + light light=currentlight, projection P=currentprojection) +{ + if(is3D(format)) + return embed(label,text,prefix,scene(f,width,height,P),format,view,options, + script,light); + else { + object F; + F.f=f; + return F; + } +} + +embed3=new object(string prefix, frame f, string format, string options, + string script, light light, projection P) { + return embed(prefix=prefix,f,format,options,script,light,P); +}; + +frame embedder(object embedder(string prefix, string format), + string prefix, string format, bool view, light light) +{ + frame f; + bool prc=prc(format); + if(!prc && settings.render != 0 && !view) { + static int previewcount=0; + bool keep=prefix != ""; + prefix=outprefix(prefix)+"+"+(string) previewcount; + ++previewcount; + format=nativeformat(); + if(!keep) file3.push(prefix+"."+format); + } + object F=embedder(prefix,format); + if(prc) + label(f,F.L); + else { + if(settings.render == 0) { + add(f,F.f); + if(light.background != nullpen) + box(f,light.background,Fill,above=false); + } else if(!view) + label(f,graphic(prefix,"hiresbb")); + } + return f; +} + +currentpicture.fitter=new frame(string prefix, picture pic, string format, + real xsize, real ysize, bool keepAspect, + bool view, string options, string script, + light light, projection P) { + frame f; + bool empty3=pic.empty3(); + if(!empty3) f=embedder(new object(string prefix, string format) { + return embed(prefix=prefix,pic,format,xsize,ysize,keepAspect,view, + options,script,light,P); + },prefix,format,view,light); + if(is3D(format) || empty3) add(f,pic.fit2(xsize,ysize,keepAspect)); + return f; +}; + +frame embedder(string prefix, frame f, string format, real width, real height, + bool view, string options, string script, light light, + projection P) +{ + return embedder(new object(string prefix, string format) { + return embed(prefix=prefix,f,format,width,height,view,options,script, + light,P); + },prefix,format,view,light); +} + +projection[][] ThreeViewsUS={{TopView}, + {FrontView,RightView}}; + +projection[][] SixViewsUS={{null,TopView}, + {LeftView,FrontView,RightView,BackView}, + {null,BottomView}}; + +projection[][] ThreeViewsFR={{RightView,FrontView}, + {null,TopView}}; + +projection[][] SixViewsFR={{null,BottomView}, + {RightView,FrontView,LeftView,BackView}, + {null,TopView}}; + +projection[][] ThreeViews={{FrontView,TopView,RightView}}; + +projection[][] SixViews={{FrontView,TopView,RightView}, + {BackView,BottomView,LeftView}}; + +void addViews(picture dest, picture src, projection[][] views=SixViewsUS, + bool group=true, filltype filltype=NoFill) +{ + frame[][] F=array(views.length,new frame[]); + pair[][] M=array(views.length,new pair[]); + pair[][] m=array(views.length,new pair[]); + + for(int i=0; i < views.length; ++i) { + projection[] viewsi=views[i]; + frame[] Fi=F[i]; + pair[] Mi=M[i]; + pair[] mi=m[i]; + for(projection P : viewsi) { + if(P != null) { + frame f=src.fit(P); + mi.push(min(f)); + Mi.push(max(f)); + Fi.push(f); + } else { + pair Infinity=(infinity,infinity); + mi.push(Infinity); + Mi.push(-Infinity); + Fi.push(newframe); + } + } + } + + real[] my=new real[views.length]; + real[] My=new real[views.length]; + + int Nj=0; + for(int i=0; i < views.length; ++i) { + my[i]=minbound(m[i]).y; + My[i]=maxbound(M[i]).y; + Nj=max(Nj,views[i].length); + } + + real[] mx=array(Nj,infinity); + real[] Mx=array(Nj,-infinity); + for(int i=0; i < views.length; ++i) { + pair[] mi=m[i]; + pair[] Mi=M[i]; + for(int j=0; j < views[i].length; ++j) { + mx[j]=min(mx[j],mi[j].x); + Mx[j]=max(Mx[j],Mi[j].x); + } + } + + if(group) begingroup(dest); + + real y; + for(int i=0; i < views.length; ++i) { + real x; + pair[] mi=m[i]; + for(int j=0; j < views[i].length; ++j) { + if(size(F[i][j]) != 0) + add(dest,shift(x-mx[j],y+my[i])*F[i][j],filltype); + x += (Mx[j]-mx[j]); + } + y -= (My[i]-my[i]); + } + + if(group) endgroup(dest); +} + +void addViews(picture src, projection[][] views=SixViewsUS, bool group=true, + filltype filltype=NoFill) +{ + addViews(currentpicture,src,views,group,filltype); +} + +void addStereoViews(picture dest, picture src, bool group=true, + filltype filltype=NoFill, real eyetoview=defaulteyetoview, + bool leftright=true, projection P=currentprojection) +{ + triple v=P.vector(); + triple h=0.5*abs(v)*eyetoview*unit(cross(P.up,v)); + projection leftEye=P.copy(); + leftEye.camera -= h; + leftEye.calculate(); + projection rightEye=P.copy(); + rightEye.camera += h; + rightEye.calculate(); + addViews(dest,src,leftright ? + new projection[][] {{leftEye,rightEye}} : + new projection[][] {{rightEye,leftEye}},group,filltype); +} + +void addStereoViews(picture src, bool group=true, + filltype filltype=NoFill, + real eyetoview=defaulteyetoview, bool leftright=true, + projection P=currentprojection) +{ + addStereoViews(currentpicture,src,group,filltype,eyetoview,leftright,P); +} + +// Fit an array of 3D pictures simultaneously using the sizing of picture all. +frame[] fit3(string prefix="", picture[] pictures, picture all, + string format="", bool view=true, string options="", + string script="", light light=currentlight, + projection P=currentprojection) +{ + frame[] out; + scene S=scene(all,P); + triple m=all.min(S.t); + triple M=all.max(S.t); + out=new frame[pictures.length]; + int i=0; + bool reverse=settings.reverse; + settings.animating=true; + + for(picture pic : pictures) { + picture pic2; + frame f=pic.fit3(S.t,pic2,S.P); + if(settings.interrupt) break; + add(f,pic2.fit2()); + draw(f,m,nullpen); + draw(f,M,nullpen); + out[i]=f; + ++i; + } + + while(!settings.interrupt) { + for(int i=settings.reverse ? pictures.length-1 : 0; + i >= 0 && i < pictures.length && !settings.interrupt; + settings.reverse ? --i : ++i) { + frame f=embedder(prefix,out[i],format,S.width,S.height,view,options, + script,light,S.P); + if(!settings.loop) out[i]=f; + } + if(!settings.loop) break; + } + + settings.animating=false; + settings.interrupt=false; + settings.reverse=reverse; + + return out; +} + +// Fit an array of pictures simultaneously using the size of the first picture. +fit=new frame[](string prefix="", picture[] pictures, string format="", + bool view=true, string options="", string script="", + projection P=currentprojection) { + if(pictures.length == 0) + return new frame[]; + + picture all; + size(all,pictures[0]); + for(picture pic : pictures) + add(all,pic); + + return all.empty3() ? fit2(pictures,all) : + fit3(prefix,pictures,all,format,view,options,script,P); +}; + +// Add frame src to picture dest about position. +void add(picture dest=currentpicture, frame src, triple position) +{ + if(is3D(src)) { + dest.add(new void(frame f, transform3 t, picture, projection) { + add(f,shift(t*position)*src); + },true); + } else { + dest.add(new void(frame, transform3 t, picture pic, projection P) { + if(pic != null) { + pic.add(new void(frame f, transform T) { + add(f,T*shift(project(t*position,P))*src); + },true); + } + },true); + } + dest.addBox(position,position,min3(src),max3(src)); +} + +exitfcn currentexitfunction=atexit(); + +void exitfunction() +{ + if(currentexitfunction != null) currentexitfunction(); + if(!settings.keep) + for(int i=0; i < file3.length; ++i) + delete(file3[i]); + file3=new string[]; +} + +atexit(exitfunction); diff --git a/Build/source/utils/asymptote/base/three_arrows.asy b/Build/source/utils/asymptote/base/three_arrows.asy new file mode 100644 index 00000000000..b0369e6beab --- /dev/null +++ b/Build/source/utils/asymptote/base/three_arrows.asy @@ -0,0 +1,725 @@ +// A transformation that bends points along a path +transform3 bend(path3 g, real t) +{ + triple dir=dir(g,t); + triple a=point(g,0), b=postcontrol(g,0); + triple c=precontrol(g,1), d=point(g,1); + triple dir1=b-a; + triple dir2=c-b; + triple dir3=d-c; + + triple u = unit(cross(dir1,dir3)); + real eps=1000*realEpsilon; + if(abs(u) < eps) { + u = unit(cross(dir1,dir2)); + if(abs(u) < eps) { + u = unit(cross(dir2,dir3)); + if(abs(u) < eps) + // linear segment: use any direction perpendicular to initial direction + u = perp(dir1); + } + } + u = unit(perp(u,dir)); + + triple w=cross(dir,u); + triple q=point(g,t); + return new real[][] { + {u.x,w.x,dir.x,q.x}, + {u.y,w.y,dir.y,q.y}, + {u.z,w.z,dir.z,q.z}, + {0,0,0,1} + }; +} + +// bend a point along a path; assumes that p.z is in [0,scale] +triple bend(triple p, path3 g, real scale) +{ + return bend(g,arctime(g,arclength(g)+p.z-scale))*(p.x,p.y,0); +} + +void bend(surface s, path3 g, real L) +{ + for(patch p : s.s) { + for(int i=0; i < 4; ++i) { + for(int j=0; j < 4; ++j) { + p.P[i][j]=bend(p.P[i][j],g,L); + } + } + } +} + +// Refine a noncyclic path3 g so that it approaches its endpoint in +// geometrically spaced steps. +path3 approach(path3 p, int n, real radix=3) +{ + guide3 G; + real L=length(p); + real tlast=0; + real r=1/radix; + for(int i=1; i < n; ++i) { + real t=L*(1-r^i); + G=G&subpath(p,tlast,t); + tlast=t; + } + return G&subpath(p,tlast,L); +} + +struct arrowhead3 +{ + arrowhead arrowhead2=DefaultHead; + real size(pen p)=arrowsize; + real arcsize(pen p)=arcarrowsize; + real gap=1; + real size; + bool splitpath=false; + + surface head(path3 g, position position=EndPoint, + pen p=currentpen, real size=0, real angle=arrowangle, + filltype filltype=null, bool forwards=true, + projection P=currentprojection); + + static surface surface(path3 g, position position, real size, + path[] h, pen p, filltype filltype, + triple normal, projection P) { + bool relative=position.relative; + real position=position.position.x; + if(relative) position=reltime(g,position); + path3 r=subpath(g,position,0); + path3 s=subpath(r,arctime(r,size),0); + if(filltype == null) filltype=FillDraw(p); + bool draw=filltype.type != filltype.Fill; + triple v=point(s,length(s)); + triple N=normal == O ? P.normal : normal; + triple w=unit(v-point(s,0)); + transform3 t=transform3(w,unit(cross(w,N))); + path3[] H=t*path3(h); + surface s; + real width=linewidth(p); + if(filltype != NoFill && filltype.type != filltype.UnFill && + filltype.type != filltype.Draw) { + triple n=0.5*width*unit(t*Z); + s=surface(shift(n)*H,planar=true); + s.append(surface(shift(-n)*H,planar=true)); + if(!draw) + for(path g : h) + s.append(shift(-n)*t*extrude(g,width*Z)); + } + if(draw) + for(path3 g : H) { + tube T=tube(g,width); + for(surface S : T.s) + s.append(S); + } + return shift(v)*s; + } + + static path project(path3 g, bool forwards, projection P) { + path h=project(forwards ? g : reverse(g),P); + return shift(-point(h,length(h)))*h; + } + + static path[] align(path H, path h) { + static real fuzz=1000*realEpsilon; + real[][] t=intersections(H,h,fuzz*max(abs(max(h)),abs(min(h)))); + return t.length >= 2 ? + rotate(-degrees(point(H,t[0][0])-point(H,t[1][0]),warn=false))*H : 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) { + path3 q=subpath(s,i,i+1); + if(remainL > 0) { + real l=arclength(q); + real w=remainL*aspect; + surface segment=scale(w,w,l)*unitcylinder; + if(first) { // add base + first=false; + segment.append(scale(w,w,1)*unitdisk); + } + for(patch p : segment.s) { + for(int i=0; i < 4; ++i) { + for(int j=0; j < 4; ++j) { + real k=1-p.P[i][j].z/remainL; + p.P[i][j]=bend((k*p.P[i][j].x,k*p.P[i][j].y,p.P[i][j].z),q,l); + } + } + } + head.append(segment); + remainL -= l; + } + } + } + return head; +}; + +arrowhead3 HookHead3(real dir=arrowdir, real barb=arrowbarb) +{ + arrowhead3 a; + a.head=new surface(path3 g, position position=EndPoint, + pen p=currentpen, real size=0, real angle=arrowangle, + filltype filltype=null, bool forwards=true, + projection P=currentprojection) { + if(size == 0) size=a.size(p); + + bool relative=position.relative; + real position=position.position.x; + if(relative) position=reltime(g,position); + + path3 r=subpath(g,position,0); + path3 s=subpath(r,arctime(r,size),0); + bool straight1=length(s) == 1 && straight(g,0); + path3 H=path3(HookHead(dir,barb).head((0,0)--(0,size),p,size,angle), + YZplane); + surface head=surface(O,reverse(approach(subpath(H,1,0),7,1.5))& + approach(subpath(H,1,2),4,2),Z); + + if(straight1) { + triple v=point(s,0); + triple u=point(s,1)-v; + return shift(v)*align(unit(u))*head; + } else { + bend(head,s,size); + return head; + } + }; + a.arrowhead2=HookHead; + a.gap=0.7; + return a; +} +arrowhead3 HookHead3=HookHead3(); + +arrowhead3 TeXHead3; +TeXHead3.size=TeXHead.size; +TeXHead3.arcsize=TeXHead.size; +TeXHead3.arrowhead2=TeXHead; +TeXHead3.head=new surface(path3 g, position position=EndPoint, + pen p=currentpen, real size=0, real angle=arrowangle, + filltype filltype=null, bool forwards=true, + projection P=currentprojection) +{ + real texsize=TeXHead3.size(p); + if(size == 0) size=texsize; + bool relative=position.relative; + real position=position.position.x; + if(relative) position=reltime(g,position); + + path3 r=subpath(g,position,0); + path3 s=subpath(r,arctime(r,size),0); + bool straight1=length(s) == 1 && straight(g,0); + + surface head=surface(O,approach(subpath(path3(TeXHead.head((0,0)--(0,1),p, + size), + YZplane),5,0),8,1.5),Z); + if(straight1) { + triple v=point(s,0); + triple u=point(s,1)-v; + return shift(v)*align(unit(u))*head; + } else { + path3 s=subpath(r,arctime(r,size/texsize*arrowsize(p)),0); + bend(head,s,size); + return head; + } +}; + +path3 arrowbase(path3 r, triple y, real t, real size) +{ + triple perp=2*size*perp(dir(r,t)); + return size == 0 ? y : y+perp--y-perp; +} + +arrowhead3 DefaultHead2(triple normal=O) { + arrowhead3 a; + a.head=new surface(path3 g, position position=EndPoint, + pen p=currentpen, real size=0, + real angle=arrowangle, + filltype filltype=null, bool forwards=true, + projection P=currentprojection) { + if(size == 0) size=a.size(p); + path h=a.project(g,forwards,P); + a.size=min(size,arclength(h)); + path[] H=a.align(DefaultHead.head(h,p,size,angle),h); + H=forwards ? yscale(-1)*H : H; + return a.surface(g,position,size,H,p,filltype,normal,P); + }; + a.gap=1.005; + return a; +} +arrowhead3 DefaultHead2=DefaultHead2(); + +arrowhead3 HookHead2(real dir=arrowdir, real barb=arrowbarb, triple normal=O) +{ + arrowhead3 a; + a.head=new surface(path3 g, position position=EndPoint, + pen p=currentpen, real size=0, real angle=arrowangle, + filltype filltype=null, bool forwards=true, + projection P=currentprojection) { + if(size == 0) size=a.size(p); + path h=a.project(g,forwards,P); + a.size=min(size,arclength(h)); + path[] H=a.align(HookHead.head(h,p,size,angle),h); + H=forwards ? yscale(-1)*H : H; + return a.surface(g,position,size,H,p,filltype,normal,P); + }; + a.arrowhead2=HookHead; + a.gap=1.005; + a.splitpath=true; + return a; +} +arrowhead3 HookHead2=HookHead2(); + +arrowhead3 TeXHead2(triple normal=O) { + arrowhead3 a; + a.head=new surface(path3 g, position position=EndPoint, + pen p=currentpen, real size=0, + real angle=arrowangle, filltype filltype=null, + bool forwards=true, projection P=currentprojection) { + if(size == 0) size=a.size(p); + path h=a.project(g,forwards,P); + a.size=min(size,arclength(h)); + h=rotate(-degrees(dir(h,length(h)),warn=false))*h; + path[] H=TeXHead.head(h,p,size,angle); + H=forwards ? yscale(-1)*H : H; + return a.surface(g,position,size,H,p, + filltype == null ? TeXHead.defaultfilltype(p) : filltype, + normal,P); + }; + a.arrowhead2=TeXHead; + a.size=TeXHead.size; + a.gap=1.005; + return a; +} +arrowhead3 TeXHead2=TeXHead2(); + +private real position(position position, real size, path3 g, bool center) +{ + bool relative=position.relative; + real position=position.position.x; + if(relative) { + position *= arclength(g); + if(center) position += 0.5*size; + position=arctime(g,position); + } else if(center) + position=arctime(g,arclength(subpath(g,0,position))+0.5*size); + return position; +} + +void drawarrow(picture pic, arrowhead3 arrowhead=DefaultHead3, + path3 g, material p=currentpen, material arrowheadpen=nullpen, + real size=0, real angle=arrowangle, position position=EndPoint, + filltype filltype=null, bool forwards=true, + margin3 margin=NoMargin3, bool center=false, light light=nolight, + light arrowheadlight=currentlight, + projection P=currentprojection) +{ + pen q=(pen) p; + if(filltype != null) { + if(arrowheadpen == nullpen && filltype != null) + arrowheadpen=filltype.fillpen; + if(arrowheadpen == nullpen && filltype != null) + arrowheadpen=filltype.drawpen; + } + if(arrowheadpen == nullpen) arrowheadpen=p; + if(size == 0) size=arrowhead.size(q); + size=min(arrowsizelimit*arclength(g),size); + real position=position(position,size,g,center); + + g=margin(g,q).g; + int L=length(g); + if(!forwards) { + g=reverse(g); + position=L-position; + } + path3 r=subpath(g,position,0); + size=min(arrowsizelimit*arclength(r),size); + surface head=arrowhead.head(g,position,q,size,angle,filltype,forwards,P); + if(arrowhead.size > 0) size=arrowhead.size; + bool endpoint=position > L-sqrtEpsilon; + if(arrowhead.splitpath || endpoint) { + if(position > 0) { + real Size=size*arrowhead.gap; + draw(pic,subpath(r,arctime(r,Size),length(r)),p,light); + } + if(!endpoint) + draw(pic,subpath(g,position,L),p,light); + } else draw(pic,g,p,light); + draw(pic,head,arrowheadpen,arrowheadlight); +} + +void drawarrow2(picture pic, arrowhead3 arrowhead=DefaultHead3, + path3 g, material p=currentpen, material arrowheadpen=nullpen, + real size=0, real angle=arrowangle, filltype filltype=null, + margin3 margin=NoMargin3, light light=nolight, + light arrowheadlight=currentlight, + projection P=currentprojection) +{ + pen q=(pen) p; + if(filltype != null) { + if(arrowheadpen == nullpen && filltype != null) + arrowheadpen=filltype.fillpen; + if(arrowheadpen == nullpen && filltype != null) + arrowheadpen=filltype.drawpen; + } + if(arrowheadpen == nullpen) arrowheadpen=p; + if(size == 0) size=arrowhead.size(q); + g=margin(g,q).g; + size=min(arrow2sizelimit*arclength(g),size); + + path3 r=reverse(g); + int L=length(g); + real Size=size*arrowhead.gap; + draw(pic,subpath(r,arctime(r,Size),L-arctime(g,Size)),p,light); + draw(pic,arrowhead.head(g,L,q,size,angle,filltype,forwards=true,P), + arrowheadpen,arrowheadlight); + draw(pic,arrowhead.head(r,L,q,size,angle,filltype,forwards=false,P), + arrowheadpen,arrowheadlight); +} + +// Add to picture an estimate of the bounding box contribution of arrowhead +// using the local slope at endpoint. +void addArrow(picture pic, arrowhead3 arrowhead, path3 g, pen p, real size, + real angle, filltype filltype, real position) +{ + triple v=point(g,position); + path3 g=v-(size+linewidth(p))*dir(g,position)--v; + surface s=arrowhead.head(g,position,p,size,angle); + if(s.s.length > 0) { + pic.addPoint(v,min(s)-v); + pic.addPoint(v,max(s)-v); + } else pic.addPoint(v); +} + +picture arrow(arrowhead3 arrowhead=DefaultHead3, + path3 g, material p=currentpen, material arrowheadpen=p, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=EndPoint, + bool forwards=true, margin3 margin=NoMargin3, + bool center=false, light light=nolight, + light arrowheadlight=currentlight) +{ + pen q=(pen) p; + if(size == 0) size=arrowhead.size(q); + picture pic; + if(is3D()) + pic.add(new void(frame f, transform3 t, picture pic2, projection P) { + picture opic; + drawarrow(opic,arrowhead,t*g,p,arrowheadpen,size,angle,position, + filltype,forwards,margin,center,light,arrowheadlight,P); + add(f,opic.fit3(identity4,pic2,P)); + }); + + addPath(pic,g,q); + + real position=position(position,size,g,center); + path3 G; + if(!forwards) { + G=reverse(g); + position=length(g)-position; + } else G=g; + addArrow(pic,arrowhead,G,q,size,angle,filltype,position); + + return pic; +} + +picture arrow2(arrowhead3 arrowhead=DefaultHead3, + path3 g, material p=currentpen, material arrowheadpen=p, + real size=0, real angle=arrowangle, filltype filltype=null, + margin3 margin=NoMargin3, light light=nolight, + light arrowheadlight=currentlight) +{ + pen q=(pen) p; + if(size == 0) size=arrowhead.size(q); + picture pic; + + if(is3D()) + pic.add(new void(frame f, transform3 t, picture pic2, projection P) { + picture opic; + drawarrow2(opic,arrowhead,t*g,p,arrowheadpen,size,angle,filltype, + margin,light,arrowheadlight,P); + add(f,opic.fit3(identity4,pic2,P)); + }); + + addPath(pic,g,q); + + int L=length(g); + addArrow(pic,arrowhead,g,q,size,angle,filltype,L); + addArrow(pic,arrowhead,reverse(g),q,size,angle,filltype,L); + + return pic; +} + +void add(picture pic, arrowhead3 arrowhead, real size, real angle, + filltype filltype, position position, material arrowheadpen, + path3 g, material p, bool forwards=true, margin3 margin, + bool center=false, light light, light arrowheadlight) +{ + add(pic,arrow(arrowhead,g,p,arrowheadpen,size,angle,filltype,position, + forwards,margin,center,light,arrowheadlight)); + if(!is3D()) { + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + if(pic != null) { + pen q=(pen) p; + path3 G=t*g; + marginT3 m=margin(G,q); + add(pic,arrow(arrowhead.arrowhead2,project(G,P),q,size,angle, + filltype == null ? + arrowhead.arrowhead2.defaultfilltype + ((pen) arrowheadpen) : filltype,position, + forwards,TrueMargin(m.begin,m.end),center)); + } + },true); + } +} + +void add2(picture pic, arrowhead3 arrowhead, real size, real angle, + filltype filltype, material arrowheadpen, path3 g, material p, + margin3 margin, light light, light arrowheadlight) +{ + add(pic,arrow2(arrowhead,g,p,arrowheadpen,size,angle,filltype,margin,light, + arrowheadlight)); + if(!is3D()) { + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + if(pic != null) { + pen q=(pen) p; + path3 G=t*g; + marginT3 m=margin(G,q); + add(pic,arrow2(arrowhead.arrowhead2,project(G,P),q,size,angle, + filltype == null ? + arrowhead.arrowhead2.defaultfilltype + ((pen) arrowheadpen) : filltype, + TrueMargin(m.begin,m.end))); + } + },true); + } +} + +void bar(picture pic, triple a, triple d, triple perp=O, + material p=currentpen, light light=nolight) +{ + d *= 0.5; + perp *= 0.5; + pic.add(new void(frame f, transform3 t, picture pic2, projection P) { + picture opic; + triple A=t*a; + triple v=d == O ? abs(perp)*unit(cross(P.normal,perp)) : d; + draw(opic,A-v--A+v,p,light); + add(f,opic.fit3(identity4,pic2,P)); + }); + triple v=d == O ? cross(currentprojection.normal,perp) : d; + pen q=(pen) p; + triple m=min3(q); + triple M=max3(q); + pic.addPoint(a,-v-m); + pic.addPoint(a,-v+m); + pic.addPoint(a,v-M); + pic.addPoint(a,v+M); +} + +picture bar(triple a, triple dir, triple perp=O, material p=currentpen) +{ + picture pic; + bar(pic,a,dir,perp,p); + return pic; +} + +typedef bool arrowbar3(picture, path3, material, margin3, light, light); + +bool Blank(picture, path3, material, margin3, light, light) +{ + return false; +} + +bool None(picture, path3, material, margin3, light, light) +{ + return true; +} + +arrowbar3 BeginArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=BeginPoint, + material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p, + forwards=false,margin,light,arrowheadlight); + return false; + }; +} + +arrowbar3 Arrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=EndPoint, + material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p,margin, + light,arrowheadlight); + return false; + }; +} + +arrowbar3 EndArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arrowangle, + filltype filltype=null, position position=EndPoint, + material arrowheadpen=nullpen)=Arrow3; + +arrowbar3 MidArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arrowangle, + filltype filltype=null, material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + add(pic,arrowhead,size,angle,filltype,MidPoint, + arrowheadpen,g,p,margin,center=true,light,arrowheadlight); + return false; + }; +} + +arrowbar3 Arrows3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arrowangle, + filltype filltype=null, material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + add2(pic,arrowhead,size,angle,filltype,arrowheadpen,g,p,margin,light, + arrowheadlight); + return false; + }; +} + +arrowbar3 BeginArcArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arcarrowangle, + filltype filltype=null, position position=BeginPoint, + material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + real size=size == 0 ? arrowhead.arcsize((pen) p) : size; + add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p, + forwards=false,margin,light,arrowheadlight); + return false; + }; +} + +arrowbar3 ArcArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arcarrowangle, + filltype filltype=null, position position=EndPoint, + material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + real size=size == 0 ? arrowhead.arcsize((pen) p) : size; + add(pic,arrowhead,size,angle,filltype,position,arrowheadpen,g,p,margin, + light,arrowheadlight); + return false; + }; +} + +arrowbar3 EndArcArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arcarrowangle, + filltype filltype=null, + position position=EndPoint, + material arrowheadpen=nullpen)=ArcArrow3; + + +arrowbar3 MidArcArrow3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arcarrowangle, + filltype filltype=null, material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + real size=size == 0 ? arrowhead.arcsize((pen) p) : size; + add(pic,arrowhead,size,angle,filltype,MidPoint,arrowheadpen,g,p,margin, + center=true,light,arrowheadlight); + return false; + }; +} + +arrowbar3 ArcArrows3(arrowhead3 arrowhead=DefaultHead3, + real size=0, real angle=arcarrowangle, + filltype filltype=null, material arrowheadpen=nullpen) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, + light light, light arrowheadlight) { + real size=size == 0 ? arrowhead.arcsize((pen) p) : size; + add2(pic,arrowhead,size,angle,filltype,arrowheadpen,g,p,margin,light, + arrowheadlight); + return false; + }; +} + +arrowbar3 BeginBar3(real size=0, triple dir=O) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, light light, + light) { + real size=size == 0 ? barsize((pen) p) : size; + bar(pic,point(g,0),size*unit(dir),size*dir(g,0),p,light); + return true; + }; +} + +arrowbar3 Bar3(real size=0, triple dir=O) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, light light, + light) { + int L=length(g); + real size=size == 0 ? barsize((pen) p) : size; + bar(pic,point(g,L),size*unit(dir),size*dir(g,L),p,light); + return true; + }; +} + +arrowbar3 EndBar3(real size=0, triple dir=O)=Bar3; + +arrowbar3 Bars3(real size=0, triple dir=O) +{ + return new bool(picture pic, path3 g, material p, margin3 margin, light light, + light) { + real size=size == 0 ? barsize((pen) p) : size; + BeginBar3(size,dir)(pic,g,p,margin,light,nolight); + EndBar3(size,dir)(pic,g,p,margin,light,nolight); + return true; + }; +} + +arrowbar3 BeginArrow3=BeginArrow3(), +MidArrow3=MidArrow3(), +Arrow3=Arrow3(), +EndArrow3=Arrow3(), +Arrows3=Arrows3(), +BeginArcArrow3=BeginArcArrow3(), +MidArcArrow3=MidArcArrow3(), +ArcArrow3=ArcArrow3(), +EndArcArrow3=ArcArrow3(), +ArcArrows3=ArcArrows3(), +BeginBar3=BeginBar3(), +Bar3=Bar3(), +EndBar3=Bar3(), +Bars3=Bars3(); diff --git a/Build/source/utils/asymptote/base/three_light.asy b/Build/source/utils/asymptote/base/three_light.asy new file mode 100644 index 00000000000..46b0fef7d0e --- /dev/null +++ b/Build/source/utils/asymptote/base/three_light.asy @@ -0,0 +1,133 @@ +struct material { + pen[] p; // diffusepen,emissivepen,specularpen + real opacity; + real shininess; + real metallic; + real fresnel0; // Reflectance rate at a perfect normal angle. + + void operator init(pen diffusepen=black, + pen emissivepen=black, pen specularpen=mediumgray, + real opacity=opacity(diffusepen), + real shininess=defaultshininess, + real metallic=defaultmetallic, + real fresnel0=defaultfresnel0) { + + p=new pen[] {diffusepen,emissivepen,specularpen}; + this.opacity=opacity; + this.shininess=shininess; + this.metallic=metallic; + this.fresnel0=fresnel0; + } + void operator init(material m) { + p=copy(m.p); + opacity=m.opacity; + shininess=m.shininess; + metallic=m.metallic; + fresnel0=m.fresnel0; + } + pen diffuse() {return p[0];} + pen emissive() {return p[1];} + pen specular() {return p[2];} + + void diffuse(pen q) {p[0]=q;} + void emissive(pen q) {p[1]=q;} + void specular(pen q) {p[2]=q;} +} + +material operator init() +{ + return material(); +} + +void write(file file, string s="", material x, suffix suffix=none) +{ + write(file,s); + write(file,"{"); + write(file,"diffuse=",x.diffuse()); + write(file,", emissive=",x.emissive()); + write(file,", specular=",x.specular()); + write(file,", opacity=",x.opacity); + write(file,", shininess=",x.shininess); + write(file,", metallic=",x.metallic); + write(file,", F0=",x.fresnel0); + 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.metallic == n.metallic && + m.fresnel0 == n.fresnel0; +} + +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, bool colors=false) +{ + return material(black+opacity(m.opacity),colors ? m.emissive() : m.diffuse()+m.emissive(),black,m.opacity,1); +} + +pen color(triple normal, material m, light light, transform3 T=light.T) { + triple[] position=light.position; + if(invisible((pen) m)) return invisible; + if(position.length == 0) return m.diffuse(); + normal=unit(transpose(inverse(shiftless(T)))*normal); + if(settings.twosided) normal *= sgn(normal.z); + real s=m.shininess*128; + real[] Diffuse=rgba(m.diffuse()); + real[] Specular=rgba(m.specular()); + real[] p=rgba(m.emissive()); + real[] diffuse={0,0,0,0}; + real[] specular={0,0,0,0}; + for(int i=0; i < position.length; ++i) { + triple L=position[i]; + real dotproduct=abs(dot(normal,L)); + diffuse += dotproduct*light.diffuse[i]; + dotproduct=abs(dot(normal,unit(L+Z))); + // Phong-Blinn model of specular reflection + specular += dotproduct^s*light.specular[i]; + } + p += diffuse*Diffuse; + // Apply specularfactor to partially compensate non-pixel-based rendering. + p += specular*Specular*light.specularfactor; + return rgb(p[0],p[1],p[2])+opacity(opacity(m.diffuse())); +} + +light operator * (transform3 t, light light) +{ + light light=light(light); + return light; +} + +light operator cast(triple v) {return light(v);} + +light Viewport=light(specularfactor=3,(0.25,-0.25,1)); + +light White=light(new pen[] {rgb(0.38,0.38,0.45),rgb(0.6,0.6,0.67), + rgb(0.5,0.5,0.57)},specularfactor=3, + new triple[] {(-2,-1.5,-0.5),(2,1.1,-2.5),(-0.5,0,2)}); + +light Headlamp=light(white,specular=gray(0.7), + specularfactor=3,dir(42,48)); + +currentlight=Headlamp; + +light nolight; diff --git a/Build/source/utils/asymptote/base/three_margins.asy b/Build/source/utils/asymptote/base/three_margins.asy new file mode 100644 index 00000000000..b6f286ee4ff --- /dev/null +++ b/Build/source/utils/asymptote/base/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/Build/source/utils/asymptote/base/three_surface.asy b/Build/source/utils/asymptote/base/three_surface.asy new file mode 100644 index 00000000000..7d64ad22d72 --- /dev/null +++ b/Build/source/utils/asymptote/base/three_surface.asy @@ -0,0 +1,2458 @@ +import bezulate; +private import interpolate; + +int nslice=12; +real camerafactor=1.2; + +string meshname(string name) {return name+" mesh";} + +private real Fuzz=10.0*realEpsilon; +private real nineth=1/9; + +// Return the default Coons interior control point for a Bezier triangle +// based on the cyclic path3 external. +triple coons3(path3 external) { + return 0.25*(precontrol(external,0)+postcontrol(external,0)+ + precontrol(external,1)+postcontrol(external,1)+ + precontrol(external,2)+postcontrol(external,2))- + (point(external,0)+point(external,1)+point(external,2))/6; +} + +struct patch { + triple[][] P; + pen[] colors; // Optionally specify 4 corner colors. + bool straight; // Patch is based on a piecewise straight external path. + bool3 planar; // Patch is planar. + bool triangular; // Patch is a Bezier triangle. + + path3 external() { + return straight ? P[0][0]--P[3][0]--P[3][3]--P[0][3]--cycle : + P[0][0]..controls P[1][0] and P[2][0].. + P[3][0]..controls P[3][1] and P[3][2].. + P[3][3]..controls P[2][3] and P[1][3].. + P[0][3]..controls P[0][2] and P[0][1]..cycle; + } + + path3 externaltriangular() { + return + P[0][0]..controls P[1][0] and P[2][0].. + P[3][0]..controls P[3][1] and P[3][2].. + P[3][3]..controls P[2][2] and P[1][1]..cycle; + } + + triple[] internal() { + return new triple[] {P[1][1],P[2][1],P[2][2],P[1][2]}; + } + + triple[] internaltriangular() { + return new triple[] {P[2][1]}; + } + + triple cornermean() { + return 0.25*(P[0][0]+P[0][3]+P[3][0]+P[3][3]); + } + + triple cornermeantriangular() { + return (P[0][0]+P[3][0]+P[3][3])/3; + } + + triple[] corners() {return new triple[] {P[0][0],P[3][0],P[3][3],P[0][3]};} + triple[] cornerstriangular() {return new triple[] {P[0][0],P[3][0],P[3][3]};} + + real[] map(real f(triple)) { + return new real[] {f(P[0][0]),f(P[3][0]),f(P[3][3]),f(P[0][3])}; + } + + real[] maptriangular(real f(triple)) { + return new real[] {f(P[0][0]),f(P[3][0]),f(P[3][3])}; + } + + triple Bu(int j, real u) {return bezier(P[0][j],P[1][j],P[2][j],P[3][j],u);} + triple BuP(int j, real u) { + return bezierP(P[0][j],P[1][j],P[2][j],P[3][j],u); + } + + path3 uequals(real u) { + triple z0=Bu(0,u); + triple z1=Bu(3,u); + return path3(new triple[] {z0,Bu(2,u)},new triple[] {z0,z1}, + new triple[] {Bu(1,u),z1},new bool[] {straight,false},false); + } + + triple Bv(int i, real v) {return bezier(P[i][0],P[i][1],P[i][2],P[i][3],v);} + triple BvP(int i, real v) { + return bezierP(P[i][0],P[i][1],P[i][2],P[i][3],v); + } + + path3 vequals(real v) { + triple z0=Bv(0,v); + triple z1=Bv(3,v); + return path3(new triple[] {z0,Bv(2,v)},new triple[] {z0,z1}, + new triple[] {Bv(1,v),z1},new bool[] {straight,false},false); + } + + triple point(real u, real v) { + return bezier(Bu(0,u),Bu(1,u),Bu(2,u),Bu(3,u),v); + } + + static real fuzz=1000*realEpsilon; + + triple normal(triple left3, triple left2, triple left1, triple middle, + triple right1, triple right2, triple right3) { + real epsilon=fuzz*change2(P); + + triple lp=3.0*(left1-middle); + triple rp=3.0*(right1-middle); + + triple n=cross(rp,lp); + if(abs(n) > epsilon) + return n; + + // Return one-half of the second derivative of the Bezier curve defined + // by a,b,c,d at 0. + triple bezierPP(triple a, triple b, triple c) { + return 3.0*(a+c-2.0*b); + } + + triple lpp=bezierPP(middle,left1,left2); + triple rpp=bezierPP(middle,right1,right2); + + n=cross(rpp,lp)+cross(rp,lpp); + if(abs(n) > epsilon) + return n; + + // Return one-sixth of the third derivative of the Bezier curve defined + // by a,b,c,d at 0. + triple bezierPPP(triple a, triple b, triple c, triple d) { + return d-a+3.0*(b-c); + } + + triple lppp=bezierPPP(middle,left1,left2,left3); + triple rppp=bezierPPP(middle,right1,right2,right3); + + n=cross(rpp,lpp)+cross(rppp,lp)+cross(rp,lppp); + if(abs(n) > epsilon) + return n; + + n=cross(rppp,lpp)+cross(rpp,lppp); + if(abs(n) > epsilon) + return n; + + return cross(rppp,lppp); + } + + triple partialu(real u, real v) { + return bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v); + } + + triple partialv(real u, real v) { + return bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u); + } + + triple normal00() { + return normal(P[0][3],P[0][2],P[0][1],P[0][0],P[1][0],P[2][0],P[3][0]); + } + + triple normal10() { + return normal(P[0][0],P[1][0],P[2][0],P[3][0],P[3][1],P[3][2],P[3][3]); + } + + triple normal11() { + return normal(P[3][0],P[3][1],P[3][2],P[3][3],P[2][3],P[1][3],P[0][3]); + } + + triple normal01() { + return normal(P[3][3],P[2][3],P[1][3],P[0][3],P[0][2],P[0][1],P[0][0]); + } + + triple normal(real u, real v) { + if(u == 0) { + if(v == 0) return normal00(); + if(v == 1) return normal01(); + } + if(u == 1) { + if(v == 0) return normal10(); + if(v == 1) return normal11(); + } + return cross(partialu(u,v),partialv(u,v)); + } + + triple pointtriangular(real u, real v) { + real w=1-u-v; + return w^2*(w*P[0][0]+3*(u*P[1][0]+v*P[1][1]))+ + u^2*(3*(w*P[2][0]+v*P[3][1])+u*P[3][0])+ + 6*u*v*w*P[2][1]+v^2*(3*(w*P[2][2]+u*P[3][2])+v*P[3][3]); + } + + triple partialutriangular(real u, real v) { + // Compute one-third of the directional derivative of a Bezier triangle + // in the u direction at (u,v). + real w=1-u-v; + return -w^2*P[0][0]+w*(w-2*u)*P[1][0]-2*w*v*P[1][1]+u*(2*w-u)*P[2][0]+ + 2*v*(w-u)*P[2][1]-v^2*P[2][2]+u^2*P[3][0]+2*u*v*P[3][1]+v^2*P[3][2]; + } + + triple partialvtriangular(real u, real v) { + // Compute one-third of the directional derivative of a Bezier triangle + // in the v direction at (u,v). + real w=1-u-v; + return -w^2*P[0][0]-2*u*w*P[1][0]+w*(w-2*v)*P[1][1]-u^2*P[2][0]+ + 2*u*(w-v)*P[2][1]+v*(2*w-v)*P[2][2]+u*u*P[3][1]+2*u*v*P[3][2]+ + v^2*P[3][3]; + } + + triple normal00triangular() { + return normal(P[3][3],P[2][2],P[1][1],P[0][0],P[1][0],P[2][0],P[3][0]); + } + + triple normal10triangular() { + return normal(P[0][0],P[1][0],P[2][0],P[3][0],P[3][1],P[3][2],P[3][3]); + } + + triple normal01triangular() { + return normal(P[3][0],P[3][1],P[3][2],P[3][3],P[2][2],P[1][1],P[0][0]); + } + + // Compute the normal vector of a Bezier triangle at (u,v) + triple normaltriangular(real u, real v) { + if(u == 0) { + if(v == 0) return normal00triangular(); + if(v == 1) return normal01triangular(); + } + if(u == 1 && v == 0) return normal10triangular(); + return cross(partialutriangular(u,v),partialvtriangular(u,v)); + } + + pen[] colors(material m, light light=currentlight) { + bool nocolors=colors.length == 0; + if(planar) { + triple normal=normal(0.5,0.5); + return new pen[] {color(normal,nocolors ? m : colors[0],light), + color(normal,nocolors ? m : colors[1],light), + color(normal,nocolors ? m : colors[2],light), + color(normal,nocolors ? m : colors[3],light)}; + } + return new pen[] {color(normal00(),nocolors ? m : colors[0],light), + color(normal10(),nocolors ? m : colors[1],light), + color(normal11(),nocolors ? m : colors[2],light), + color(normal01(),nocolors ? m : colors[3],light)}; + } + + pen[] colorstriangular(material m, light light=currentlight) { + bool nocolors=colors.length == 0; + if(planar) { + triple normal=normal(1/3,1/3); + return new pen[] {color(normal,nocolors ? m : colors[0],light), + color(normal,nocolors ? m : colors[1],light), + color(normal,nocolors ? m : colors[2],light)}; + } + return new pen[] {color(normal00(),nocolors ? m : colors[0],light), + color(normal10(),nocolors ? m : colors[1],light), + color(normal01(),nocolors ? m : colors[2],light)}; + } + + triple min3,max3; + bool havemin3,havemax3; + + void init() { + havemin3=false; + havemax3=false; + if(triangular) { + external=externaltriangular; + internal=internaltriangular; + cornermean=cornermeantriangular; + corners=cornerstriangular; + map=maptriangular; + point=pointtriangular; + normal=normaltriangular; + normal00=normal00triangular; + normal10=normal10triangular; + normal01=normal01triangular; + colors=colorstriangular; + uequals=new path3(real u) {return nullpath3;}; + vequals=new path3(real u) {return nullpath3;}; + } + } + + triple min(triple bound=P[0][0]) { + if(havemin3) return minbound(min3,bound); + havemin3=true; + return min3=minbezier(P,bound); + } + + triple max(triple bound=P[0][0]) { + if(havemax3) return maxbound(max3,bound); + havemax3=true; + return max3=maxbezier(P,bound); + } + + triple center() { + return 0.5*(this.min()+this.max()); + } + + pair min(projection P, pair bound=project(this.P[0][0],P.t)) { + triple[][] Q=P.T.modelview*this.P; + if(P.infinity) + return xypart(minbezier(Q,(bound.x,bound.y,0))); + real d=P.T.projection[3][2]; + return maxratio(Q,d*bound)/d; // d is negative + } + + pair max(projection P, pair bound=project(this.P[0][0],P.t)) { + triple[][] Q=P.T.modelview*this.P; + if(P.infinity) + return xypart(maxbezier(Q,(bound.x,bound.y,0))); + real d=P.T.projection[3][2]; + return minratio(Q,d*bound)/d; // d is negative + } + + void operator init(triple[][] P, + pen[] colors=new pen[], bool straight=false, + bool3 planar=default, bool triangular=false, + bool copy=true) { + this.P=copy ? copy(P) : P; + if(colors.length != 0) + this.colors=copy(colors); + this.straight=straight; + this.planar=planar; + this.triangular=triangular; + init(); + } + + void operator init(pair[][] P, triple plane(pair)=XYplane, + bool straight=false, bool triangular=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,triangular); + } + + void operator init(patch s) { + operator init(s.P,s.colors,s.straight,s.planar,s.triangular); + } + + // A constructor for a cyclic path3 of length 3 with a specified + // internal point, corner normals, and pens (rendered as a Bezier triangle). + void operator init(path3 external, triple internal, pen[] colors=new pen[], + bool3 planar=default) { + triangular=true; + this.planar=planar; + init(); + if(colors.length != 0) + this.colors=copy(colors); + + P=new triple[][] { + {point(external,0)}, + {postcontrol(external,0),precontrol(external,0)}, + {precontrol(external,1),internal,postcontrol(external,2)}, + {point(external,1),postcontrol(external,1),precontrol(external,2), + point(external,2)} + }; + } + + // A constructor for a convex cyclic path3 of length <= 4 with optional + // arrays of internal points (4 for a Bezier patch, 1 for a Bezier + // triangle), and pens. + void operator init(path3 external, triple[] internal=new triple[], + pen[] colors=new pen[], bool3 planar=default) { + if(internal.length == 0 && planar == default) + this.planar=normal(external) != O; + else this.planar=planar; + + int L=length(external); + + if(L == 3) { + operator init(external,internal.length == 1 ? internal[0] : + coons3(external),colors,this.planar); + straight=piecewisestraight(external); + return; + } + + 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])); + } else if(L == 2) { + external=external--cycle--cycle; + if(colors.length > 0) colors.append(array(2,colors[0])); + } + + init(); + 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)); + } + + P=new triple[][] { + {point(external,0),precontrol(external,0),postcontrol(external,3), + point(external,3)}, + {postcontrol(external,0),internal[0],internal[3],precontrol(external,3)}, + {precontrol(external,1),internal[1],internal[2],postcontrol(external,2)}, + {point(external,1),postcontrol(external,1),precontrol(external,2), + point(external,2)} + }; + } + + // A constructor for a convex quadrilateral. + void operator init(triple[] external, triple[] internal=new triple[], + pen[] colors=new pen[], bool3 planar=default) { + init(); + + if(internal.length == 0 && planar == default) + this.planar=normal(external) != O; + else this.planar=planar; + + if(colors.length != 0) + this.colors=copy(colors); + + if(internal.length == 0) { + internal=new triple[4]; + for(int j=0; j < 4; ++j) + internal[j]=nineth*(4*external[j]+2*external[(j+1)%4]+ + external[(j+2)%4]+2*external[(j+3)%4]); + } + + straight=true; + + triple delta[]=new triple[4]; + for(int j=0; j < 4; ++j) + delta[j]=(external[(j+1)% 4]-external[j])/3; + + P=new triple[][] { + {external[0],external[0]-delta[3],external[3]+delta[3],external[3]}, + {external[0]+delta[0],internal[0],internal[3],external[3]-delta[2]}, + {external[1]-delta[0],internal[1],internal[2],external[2]+delta[2]}, + {external[1],external[1]+delta[1],external[2]-delta[1],external[2]} + }; + } +} + +patch operator * (transform3 t, patch s) +{ + patch S; + S.P=new triple[s.P.length][]; + for(int i=0; i < s.P.length; ++i) { + triple[] si=s.P[i]; + triple[] Si=S.P[i]; + for(int j=0; j < si.length; ++j) + Si[j]=t*si[j]; + } + + S.colors=copy(s.colors); + S.planar=s.planar; + S.straight=s.straight; + S.triangular=s.triangular; + S.init(); + return S; +} + +patch reverse(patch s) +{ + assert(!s.triangular); + patch S; + S.P=transpose(s.P); + if(s.colors.length > 0) + S.colors=new pen[] {s.colors[0],s.colors[3],s.colors[2],s.colors[1]}; + S.straight=s.straight; + S.planar=s.planar; + return S; +} + +// Return a degenerate tensor patch representation of a Bezier triangle. +patch tensor(patch s) { + if(!s.triangular) return patch(s); + triple[][] P=s.P; + return patch(new triple[][] {{P[0][0],P[0][0],P[0][0],P[0][0]}, + {P[1][0],P[1][0]*2/3+P[1][1]/3,P[1][0]/3+P[1][1]*2/3,P[1][1]}, + {P[2][0],P[2][0]/3+P[2][1]*2/3,P[2][1]*2/3+P[2][2]/3,P[2][2]}, + {P[3][0],P[3][1],P[3][2],P[3][3]}}, + s.colors.length > 0 ? new pen[] {s.colors[0],s.colors[1],s.colors[2],s.colors[0]} : new pen[], + s.straight,s.planar,false,false); +} + +// Return the tensor product patch control points corresponding to path p +// and points internal. +pair[][] tensor(path p, pair[] internal) +{ + return new pair[][] { + {point(p,0),precontrol(p,0),postcontrol(p,3),point(p,3)}, + {postcontrol(p,0),internal[0],internal[3],precontrol(p,3)}, + {precontrol(p,1),internal[1],internal[2],postcontrol(p,2)}, + {point(p,1),postcontrol(p,1),precontrol(p,2),point(p,2)} + }; +} + +// Return the Coons patch control points corresponding to path p. +pair[][] coons(path p) +{ + int L=length(p); + if(L == 1) + p=p--cycle--cycle--cycle; + else if(L == 2) + p=p--cycle--cycle; + else if(L == 3) + p=p--cycle; + + pair[] internal=new pair[4]; + for(int j=0; j < 4; ++j) { + internal[j]=nineth*(-4*point(p,j) + +6*(precontrol(p,j)+postcontrol(p,j)) + -2*(point(p,j-1)+point(p,j+1)) + +3*(precontrol(p,j-1)+postcontrol(p,j+1)) + -point(p,j+2)); + } + return tensor(p,internal); +} + +// Decompose a possibly nonconvex cyclic path into an array of paths that +// yield nondegenerate Coons patches. +path[] regularize(path p, bool checkboundary=true) +{ + path[] s; + + if(!cyclic(p)) + abort("cyclic path expected"); + + int L=length(p); + + if(L > 4) { + for(path g : bezulate(p)) + s.append(regularize(g,checkboundary)); + return s; + } + + bool straight=piecewisestraight(p); + if(L <= 3 && straight) { + return new path[] {p}; + } + + // Split p along the angle bisector at t. + bool split(path p, real t) { + pair dir=dir(p,t); + if(dir != 0) { + path g=subpath(p,t,t+length(p)); + int L=length(g); + pair z=point(g,0); + real[] T=intersections(g,z,z+I*dir); + for(int i=0; i < T.length; ++i) { + real cut=T[i]; + if(cut > sqrtEpsilon && cut < L-sqrtEpsilon) { + pair w=point(g,cut); + if(!inside(p,0.5*(z+w),zerowinding)) continue; + pair delta=sqrtEpsilon*(w-z); + if(intersections(g,z-delta--w+delta).length != 2) continue; + s.append(regularize(subpath(g,0,cut)--cycle,checkboundary)); + s.append(regularize(subpath(g,cut,L)--cycle,checkboundary)); + return true; + } + } + } + return false; + } + + // Ensure that all interior angles are less than 180 degrees. + real fuzz=1e-4; + int sign=sgn(windingnumber(p,inside(p,zerowinding))); + for(int i=0; i < L; ++i) { + if(sign*(conj(dir(p,i,-1))*dir(p,i,1)).y < -fuzz) { + if(split(p,i)) return s; + } + } + + if(straight) + return new path[] {p}; + + pair[][] P=coons(p); + + // Check for degeneracy. + pair[][] U=new pair[3][4]; + pair[][] V=new pair[4][3]; + + for(int i=0; i < 3; ++i) { + for(int j=0; j < 4; ++j) + U[i][j]=P[i+1][j]-P[i][j]; + } + + for(int i=0; i < 4; ++i) { + for(int j=0; j < 3; ++j) + V[i][j]=P[i][j+1]-P[i][j]; + } + + int[] choose2={1,2,1}; + int[] choose3={1,3,3,1}; + + real T[][]=new real[6][6]; + for(int p=0; p < 6; ++p) { + int kstart=max(p-2,0); + int kstop=min(p,3); + real[] Tp=T[p]; + for(int q=0; q < 6; ++q) { + real Tpq; + int jstop=min(q,3); + int jstart=max(q-2,0); + for(int k=kstart; k <= kstop; ++k) { + int choose3k=choose3[k]; + for(int j=jstart; j <= jstop; ++j) { + int i=p-k; + int l=q-j; + Tpq += (conj(U[i][j])*V[k][l]).y* + choose2[i]*choose3k*choose3[j]*choose2[l]; + } + } + Tp[q]=Tpq; + } + } + + bool3 aligned=default; + bool degenerate=false; + + for(int p=0; p < 6; ++p) { + for(int q=0; q < 6; ++q) { + if(aligned == default) { + if(T[p][q] > sqrtEpsilon) aligned=true; + if(T[p][q] < -sqrtEpsilon) aligned=false; + } else { + if((T[p][q] > sqrtEpsilon && aligned == false) || + (T[p][q] < -sqrtEpsilon && aligned == true)) degenerate=true; + } + } + } + + if(!degenerate) { + if(aligned == (sign >= 0)) + return new path[] {p}; + return s; + } + + if(checkboundary) { + // Polynomial coefficients of (B_i'' B_j + B_i' B_j')/3. + static real[][][] fpv0={ + {{5, -20, 30, -20, 5}, + {-3, 24, -54, 48, -15}, + {0, -6, 27, -36, 15}, + {0, 0, -3, 8, -5}}, + {{-7, 36, -66, 52, -15}, + {3, -36, 108, -120, 45}, + {0, 6, -45, 84, -45}, + {0, 0, 3, -16, 15}}, + {{2, -18, 45, -44, 15}, + {0, 12, -63, 96, -45}, + {0, 0, 18, -60, 45}, + {0, 0, 0, 8, -15}}, + {{0, 2, -9, 12, -5}, + {0, 0, 9, -24, 15}, + {0, 0, 0, 12, -15}, + {0, 0, 0, 0, 5}} + }; + + // Compute one-ninth of the derivative of the Jacobian along the boundary. + real[][] c=array(4,array(5,0.0)); + for(int i=0; i < 4; ++i) { + real[][] fpv0i=fpv0[i]; + for(int j=0; j < 4; ++j) { + real[] w=fpv0i[j]; + c[0] += w*(conj(P[i][0])*(P[j][1]-P[j][0])).y; // v=0 + c[1] += w*(conj(P[3][j]-P[2][j])*P[3][i]).y; // u=1 + c[2] += w*(conj(P[i][3])*(P[j][3]-P[j][2])).y; // v=1 + c[3] += w*(conj(P[0][j]-P[1][j])*P[0][i]).y; // u=0 + } + } + + pair BuP(int j, real u) { + return bezierP(P[0][j],P[1][j],P[2][j],P[3][j],u); + } + pair BvP(int i, real v) { + return bezierP(P[i][0],P[i][1],P[i][2],P[i][3],v); + } + real normal(real u, real v) { + return (conj(bezier(BuP(0,u),BuP(1,u),BuP(2,u),BuP(3,u),v))* + bezier(BvP(0,v),BvP(1,v),BvP(2,v),BvP(3,v),u)).y; + } + + // Use Rolle's theorem to check for degeneracy on the boundary. + real M=0; + real cut; + for(int i=0; i < 4; ++i) { + if(!straight(p,i)) { + real[] ci=c[i]; + pair[] R=quarticroots(ci[4],ci[3],ci[2],ci[1],ci[0]); + for(pair r : R) { + if(fabs(r.y) < sqrtEpsilon) { + real t=r.x; + if(0 <= t && t <= 1) { + real[] U={t,1,t,0}; + real[] V={0,t,1,t}; + real[] T={t,t,1-t,1-t}; + real N=sign*normal(U[i],V[i]); + if(N < M) { + M=N; cut=i+T[i]; + } + } + } + } + } + } + + // Split at the worst boundary degeneracy. + if(M < 0 && split(p,cut)) return s; + } + + // Split arbitrarily to resolve any remaining (internal) degeneracy. + checkboundary=false; + for(int i=0; i < L; ++i) + if(!straight(p,i) && split(p,i+0.5)) return s; + + while(true) + for(int i=0; i < L; ++i) + if(!straight(p,i) && split(p,i+unitrand())) return s; + + return s; +} + +typedef void drawfcn(frame f, transform3 t=identity4, material[] m, + light light=currentlight, render render=defaultrender); + +struct surface { + patch[] s; + int index[][];// Position of patch corresponding to major U,V parameter in s. + bool vcyclic; + transform3 T=identity4; + + drawfcn draw; + bool PRCprimitive=true; // True unless no PRC primitive is available. + + bool empty() { + return s.length == 0; + } + + void operator init(int n) { + s=new patch[n]; + } + + void operator init(... patch[] s) { + this.s=s; + } + + void operator init(surface s) { + this.s=new patch[s.s.length]; + for(int i=0; i < s.s.length; ++i) + this.s[i]=patch(s.s[i]); + this.index=copy(s.index); + this.vcyclic=s.vcyclic; + } + + void operator init(triple[][][] P, pen[][] colors=new pen[][], + bool3 planar=default, bool triangular=false) { + s=sequence(new patch(int i) { + return patch(P[i],colors.length == 0 ? new pen[] : colors[i],planar, + triangular); + },P.length); + } + + void colors(pen[][] palette) { + for(int i=0; i < s.length; ++i) + s[i].colors=copy(palette[i]); + } + + triple[][] corners() { + triple[][] a=new triple[s.length][]; + for(int i=0; i < s.length; ++i) + a[i]=s[i].corners(); + return a; + } + + real[][] map(real f(triple)) { + real[][] a=new real[s.length][]; + for(int i=0; i < s.length; ++i) + a[i]=s[i].map(f); + return a; + } + + triple[] cornermean() { + return sequence(new triple(int i) {return s[i].cornermean();},s.length); + } + + triple point(real u, real v) { + int U=floor(u); + int V=floor(v); + int index=index.length == 0 ? U+V : index[U][V]; + return s[index].point(u-U,v-V); + } + + triple normal(real u, real v) { + int U=floor(u); + int V=floor(v); + int index=index.length == 0 ? U+V : index[U][V]; + return s[index].normal(u-U,v-V); + } + + void ucyclic(bool f) + { + index.cyclic=f; + } + + void vcyclic(bool f) + { + for(int[] i : index) + i.cyclic=f; + vcyclic=f; + } + + bool ucyclic() + { + return index.cyclic; + } + + bool vcyclic() + { + return vcyclic; + } + + path3 uequals(real u) { + if(index.length == 0) return nullpath3; + int U=floor(u); + int[] index=index[U]; + path3 g; + for(int i : index) + g=g&s[i].uequals(u-U); + return vcyclic() ? g&cycle : g; + } + + path3 vequals(real v) { + if(index.length == 0) return nullpath3; + int V=floor(v); + path3 g; + for(int[] i : index) + g=g&s[i[V]].vequals(v-V); + return ucyclic() ? g&cycle : g; + } + + // A constructor for a possibly nonconvex simple cyclic path in a given + // plane. + void operator init(path p, triple plane(pair)=XYplane) { + for(path g : regularize(p)) { + if(length(g) == 3) { + path3 G=path3(g,plane); + s.push(patch(G,coons3(G),planar=true)); + } else + s.push(patch(coons(g),plane,piecewisestraight(g))); + } + } + + 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[], + 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,colors,planar)); + return; + } + + // Construct a surface from a possibly nonconvex planar cyclic path3. + if(planar != false && internal.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,colors,planar)); + return; + } + + // Path is not planar; split into patches. + real factor=1/L; + pen[] p; + triple[] n; + bool nocolors=colors.length == 0; + triple center; + for(int i=0; i < L; ++i) + center += point(external,i); + center *= factor; + if(!nocolors) + p=new pen[] {mean(colors)}; + // 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, + nocolors ? p : concat(colors[i:end+1],p),planar)); + i=end; + } + s.push(patch(subpath(external,i,L)--center--cycle, + nocolors ? p : concat(colors[i:],colors[0:1],p),planar)); + } + + void operator init(path3 external, triple[] internal=new triple[], + pen[] colors=new pen[], bool3 planar=default) { + s=new patch[]; + construct(external,internal,colors,planar); + } + + void operator init(explicit path3[] external, + triple[][] internal=new triple[][], + pen[][] colors=new pen[][], bool3 planar=default) { + s=new patch[]; + if(planar == true) {// Assume all path3 elements share a common normal. + if(external.length != 0) { + triple n=normal(external[0]); + if(n != O) { + transform3 T=align(n); + external=transpose(T)*external; + T *= shift(0,0,point(external[0],0).z); + path[] g=sequence(new path(int i) {return path(external[i]);}, + external.length); + for(patch p : surface(g).s) + s.push(T*p); + return; + } + } + } + + for(int i=0; i < external.length; ++i) + construct(external[i], + internal.length == 0 ? new triple[] : internal[i], + colors.length == 0 ? new pen[] : colors[i],planar); + } + + void push(path3 external, triple[] internal=new triple[], + pen[] colors=new pen[], bool3 planar=default) { + s.push(patch(external,internal,colors,planar)); + } + + // Construct the surface of rotation generated by rotating g + // from angle1 to angle2 sampled n times about the line c--c+axis. + // An optional surface pen color(int i, real j) may be specified + // to override the color at vertex(i,j). + void operator init(triple c, path3 g, triple axis, int n=nslice, + real angle1=0, real angle2=360, + pen color(int i, real j)=null) { + axis=unit(axis); + real w=(angle2-angle1)/n; + int L=length(g); + s=new patch[L*n]; + index=new int[n][L]; + int m=-1; + transform3[] T=new transform3[n+1]; + transform3 t=rotate(w,c,c+axis); + T[0]=rotate(angle1,c,c+axis); + for(int k=1; k <= n; ++k) + T[k]=T[k-1]*t; + + typedef pen colorfcn(int i, real j); + bool defaultcolors=(colorfcn) color == null; + + for(int i=0; i < L; ++i) { + path3 h=subpath(g,i,i+1); + path3 r=reverse(h); + path3 H=shift(-c)*h; + real M=0; + triple perp; + void test(real[] t) { + for(int i=0; i < 3; ++i) { + triple v=point(H,t[i]); + triple V=v-dot(v,axis)*axis; + real a=abs(V); + if(a > M) {M=a; perp=V;} + } + } + test(maxtimes(H)); + test(mintimes(H)); + + perp=unit(perp); + triple normal=unit(cross(axis,perp)); + triple dir(real j) {return Cos(j)*normal-Sin(j)*perp;} + real j=angle1; + transform3 Tk=T[0]; + triple dirj=dir(j); + for(int k=0; k < n; ++k, j += w) { + transform3 Tp=T[k+1]; + triple dirp=dir(j+w); + path3 G=reverse(Tk*h{dirj}..{dirp}Tp*r{-dirp}..{-dirj}cycle); + Tk=Tp; + dirj=dirp; + s[++m]=defaultcolors ? patch(G) : + patch(G,new pen[] {color(i,j),color(i,j+w),color(i+1,j+w), + color(i+1,j)}); + index[k][i]=m; + } + ucyclic((angle2-angle1) % 360 == 0); + vcyclic(cyclic(g)); + } + } + + void push(patch s) { + this.s.push(s); + } + + void append(surface s) { + this.s.append(s.s); + } + + void operator init(... surface[] s) { + for(surface S : s) + this.s.append(S.s); + } +} + +surface operator * (transform3 t, surface s) +{ + surface S; + S.s=new patch[s.s.length]; + for(int i=0; i < s.s.length; ++i) + S.s[i]=t*s.s[i]; + S.index=copy(s.index); + S.vcyclic=(bool) s.vcyclic; + S.T=t*s.T; + S.draw=s.draw; + S.PRCprimitive=s.PRCprimitive; + + return S; +} + +private string nullsurface="null surface"; + +triple min(surface s) +{ + if(s.s.length == 0) + abort(nullsurface); + triple bound=s.s[0].min(); + for(int i=1; i < s.s.length; ++i) + bound=s.s[i].min(bound); + return bound; +} + +triple max(surface s) +{ + if(s.s.length == 0) + abort(nullsurface); + triple bound=s.s[0].max(); + for(int i=1; i < s.s.length; ++i) + bound=s.s[i].max(bound); + return bound; +} + +pair min(surface s, projection P) +{ + if(s.s.length == 0) + abort(nullsurface); + pair bound=s.s[0].min(P); + for(int i=1; i < s.s.length; ++i) + bound=s.s[i].min(P,bound); + return bound; +} + +pair max(surface s, projection P) +{ + if(s.s.length == 0) + abort(nullsurface); + pair bound=s.s[0].max(P); + for(int i=1; i < s.s.length; ++i) + bound=s.s[i].max(P,bound); + return bound; +} + +private triple[] split(triple z0, triple c0, triple c1, triple z1, real t=0.5) +{ + triple m0=interp(z0,c0,t); + triple m1=interp(c0,c1,t); + triple m2=interp(c1,z1,t); + triple m3=interp(m0,m1,t); + triple m4=interp(m1,m2,t); + triple m5=interp(m3,m4,t); + + return new triple[] {m0,m3,m5,m4,m2}; +} + +// Return the control points of the subpatches +// produced by a horizontal split of P +triple[][][] hsplit(triple[][] P, real v=0.5) +{ + // get control points in rows + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + triple[] c0=split(P0[0],P0[1],P0[2],P0[3],v); + triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v); + triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v); + triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v); + // bottom, top + return new triple[][][] { + {{P0[0],c0[0],c0[1],c0[2]}, + {P1[0],c1[0],c1[1],c1[2]}, + {P2[0],c2[0],c2[1],c2[2]}, + {P3[0],c3[0],c3[1],c3[2]}}, + {{c0[2],c0[3],c0[4],P0[3]}, + {c1[2],c1[3],c1[4],P1[3]}, + {c2[2],c2[3],c2[4],P2[3]}, + {c3[2],c3[3],c3[4],P3[3]}} + }; +} + +// Return the control points of the subpatches +// produced by a vertical split of P +triple[][][] vsplit(triple[][] P, real u=0.5) +{ + // get control points in rows + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + triple[] c0=split(P0[0],P1[0],P2[0],P3[0],u); + triple[] c1=split(P0[1],P1[1],P2[1],P3[1],u); + triple[] c2=split(P0[2],P1[2],P2[2],P3[2],u); + triple[] c3=split(P0[3],P1[3],P2[3],P3[3],u); + // left, right + return new triple[][][] { + {{P0[0],P0[1],P0[2],P0[3]}, + {c0[0],c1[0],c2[0],c3[0]}, + {c0[1],c1[1],c2[1],c3[1]}, + {c0[2],c1[2],c2[2],c3[2]}}, + {{c0[2],c1[2],c2[2],c3[2]}, + {c0[3],c1[3],c2[3],c3[3]}, + {c0[4],c1[4],c2[4],c3[4]}, + {P3[0],P3[1],P3[2],P3[3]}} + }; +} + +// Return a 2D array of the control point arrays of the subpatches +// produced by horizontal and vertical splits of P at u and v +triple[][][][] split(triple[][] P, real u=0.5, real v=0.5) +{ + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + // slice horizontally + triple[] c0=split(P0[0],P0[1],P0[2],P0[3],v); + triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v); + triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v); + triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v); + + // bottom patch + triple[] c4=split(P0[0],P1[0],P2[0],P3[0],u); + triple[] c5=split(c0[0],c1[0],c2[0],c3[0],u); + triple[] c6=split(c0[1],c1[1],c2[1],c3[1],u); + triple[] c7=split(c0[2],c1[2],c2[2],c3[2],u); + + // top patch + triple[] c8=split(c0[3],c1[3],c2[3],c3[3],u); + triple[] c9=split(c0[4],c1[4],c2[4],c3[4],u); + triple[] cA=split(P0[3],P1[3],P2[3],P3[3],u); + + // {{bottom-left, top-left}, {bottom-right, top-right}} + return new triple[][][][] { + {{{P0[0],c0[0],c0[1],c0[2]}, + {c4[0],c5[0],c6[0],c7[0]}, + {c4[1],c5[1],c6[1],c7[1]}, + {c4[2],c5[2],c6[2],c7[2]}}, + {{c0[2],c0[3],c0[4],P0[3]}, + {c7[0],c8[0],c9[0],cA[0]}, + {c7[1],c8[1],c9[1],cA[1]}, + {c7[2],c8[2],c9[2],cA[2]}}}, + {{{c4[2],c5[2],c6[2],c7[2]}, + {c4[3],c5[3],c6[3],c7[3]}, + {c4[4],c5[4],c6[4],c7[4]}, + {P3[0],c3[0],c3[1],c3[2]}}, + {{c7[2],c8[2],c9[2],cA[2]}, + {c7[3],c8[3],c9[3],cA[3]}, + {c7[4],c8[4],c9[4],cA[4]}, + {c3[2],c3[3],c3[4],P3[3]}}} + }; +} + +// Return the control points for a subpatch of P on [u,1] x [v,1]. +triple[][] subpatchend(triple[][] P, real u, real v) +{ + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + triple[] c0=split(P0[0],P0[1],P0[2],P0[3],v); + triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v); + triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v); + triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v); + + triple[] c7=split(c0[2],c1[2],c2[2],c3[2],u); + triple[] c8=split(c0[3],c1[3],c2[3],c3[3],u); + triple[] c9=split(c0[4],c1[4],c2[4],c3[4],u); + triple[] cA=split(P0[3],P1[3],P2[3],P3[3],u); + + return new triple[][] { + {c7[2],c8[2],c9[2],cA[2]}, + {c7[3],c8[3],c9[3],cA[3]}, + {c7[4],c8[4],c9[4],cA[4]}, + {c3[2],c3[3],c3[4],P3[3]}}; +} + +// Return the control points for a subpatch of P on [0,u] x [0,v]. +triple[][] subpatchbegin(triple[][] P, real u, real v) +{ + triple[] P0=P[0]; + triple[] P1=P[1]; + triple[] P2=P[2]; + triple[] P3=P[3]; + + triple[] c0=split(P0[0],P0[1],P0[2],P0[3],v); + triple[] c1=split(P1[0],P1[1],P1[2],P1[3],v); + triple[] c2=split(P2[0],P2[1],P2[2],P2[3],v); + triple[] c3=split(P3[0],P3[1],P3[2],P3[3],v); + + triple[] c4=split(P0[0],P1[0],P2[0],P3[0],u); + triple[] c5=split(c0[0],c1[0],c2[0],c3[0],u); + triple[] c6=split(c0[1],c1[1],c2[1],c3[1],u); + triple[] c7=split(c0[2],c1[2],c2[2],c3[2],u); + + return new triple[][] { + {P0[0],c0[0],c0[1],c0[2]}, + {c4[0],c5[0],c6[0],c7[0]}, + {c4[1],c5[1],c6[1],c7[1]}, + {c4[2],c5[2],c6[2],c7[2]}}; +} + +triple[][] subpatch(triple[][] P, pair a, pair b) +{ + return subpatchend(subpatchbegin(P,b.x,b.y),a.x/b.x,a.y/b.y); +} + +patch subpatch(patch s, pair a, pair b) +{ + assert(a.x >= 0 && a.y >= 0 && b.x <= 1 && b.y <= 1 && + a.x < b.x && a.y < b.y && !s.triangular); + return patch(subpatch(s.P,a,b),s.straight,s.planar); +} + +private string triangular= + "Intersection of path3 with Bezier triangle is not yet implemented"; + +// return an array containing the times for one intersection of path p and +// patch s. +real[] intersect(path3 p, patch s, real fuzz=-1) +{ + if(s.triangular) abort(triangular); + return intersect(p,s.P,fuzz); +} + +// return an array containing the times for one intersection of path p and +// surface s. +real[] intersect(path3 p, surface s, real fuzz=-1) +{ + for(int i=0; i < s.s.length; ++i) { + real[] T=intersect(p,s.s[i],fuzz); + if(T.length > 0) return T; + } + return new real[]; +} + +// return an array containing all intersection times of path p and patch s. +real[][] intersections(path3 p, patch s, real fuzz=-1) +{ + if(s.triangular) abort(triangular); + 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],fuzz)) + T.push(s); + + static real Fuzz=1000*realEpsilon; + real fuzz=max(10*fuzz,Fuzz*max(abs(min(s)),abs(max(s)))); + + // Remove intrapatch duplicate points. + for(int i=0; i < T.length; ++i) { + triple v=point(p,T[i][0]); + for(int j=i+1; j < T.length;) { + if(abs(v-point(p,T[j][0])) < fuzz) + T.delete(j); + else ++j; + } + } + return sort(T); +} + +// return an array containing all intersection points of path p and surface s. +triple[] intersectionpoints(path3 p, patch s, real fuzz=-1) +{ + real[][] t=intersections(p,s,fuzz); + return sequence(new triple(int i) {return point(p,t[i][0]);},t.length); +} + +// return an array containing all intersection points of path p and surface s. +triple[] intersectionpoints(path3 p, surface s, real fuzz=-1) +{ + real[][] t=intersections(p,s,fuzz); + return sequence(new triple(int i) {return point(p,t[i][0]);},t.length); +} + +// Return true iff the control point bounding boxes of patches p and q overlap. +bool overlap(triple[][] p, triple[][] q, real fuzz=-1) +{ + triple pmin=minbound(p); + triple pmax=maxbound(p); + triple qmin=minbound(q); + triple qmax=maxbound(q); + + if(fuzz == -1) + fuzz=1000*realEpsilon*max(abs(pmin),abs(pmax),abs(qmin),abs(qmax)); + + return + pmax.x+fuzz >= qmin.x && + pmax.y+fuzz >= qmin.y && + pmax.z+fuzz >= qmin.z && + qmax.x+fuzz >= pmin.x && + qmax.y+fuzz >= pmin.y && + qmax.z+fuzz >= pmin.z; // Overlapping bounding boxes? +} + +triple point(patch s, real u, real v) +{ + return s.point(u,v); +} + +struct interaction +{ + int type; + bool targetsize; + void operator init(int type, bool targetsize=false) { + this.type=type; + this.targetsize=targetsize; + } +} + +restricted interaction Embedded=interaction(0); +restricted interaction Billboard=interaction(1); + +interaction LabelInteraction() +{ + return settings.autobillboard ? Billboard : Embedded; +} + +material material(material m, light light, bool colors=false) +{ + return light.on() || invisible((pen) m) ? m : emissive(m,colors); +} + +void draw3D(frame f, patch s, triple center=O, material m, + light light=currentlight, interaction interaction=Embedded, + bool primitive=false) +{ + bool straight=s.straight && s.planar; + + // Planar Bezier surfaces require extra precision in WebGL + int digits=s.planar && !straight ? 12 : settings.digits; + + if(s.colors.length > 0) { + if(prc() && light.on()) + straight=false; // PRC vertex colors (for quads only) ignore lighting + m.diffuse(mean(s.colors)); + } + m=material(m,light,s.colors.length > 0); + + (s.triangular ? drawbeziertriangle : draw) + (f,s.P,center,straight,m.p,m.opacity,m.shininess, + m.metallic,m.fresnel0,s.colors,interaction.type,digits,primitive); +} + +void _draw(frame f, path3 g, triple center=O, material m, + light light=currentlight, interaction interaction=Embedded) +{ + if(!prc()) m=material(m,light); + _draw(f,g,center,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0, + interaction.type); +} + +int computeNormals(triple[] v, int[][] vi, triple[] n, int[][] ni) +{ + triple lastnormal=O; + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + int[] nii=ni[i]; + triple normal=normal(new triple[] {v[vii[0]],v[vii[1]],v[vii[2]]}); + if(normal != lastnormal || n.length == 0) { + n.push(normal); + lastnormal=normal; + } + nii[0]=nii[1]=nii[2]=n.length-1; + } + return ni.length; +} + +// Draw triangles on a frame. +void draw(frame f, triple[] v, int[][] vi, + triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={}, + int[][] pi={}, light light=currentlight) +{ + bool normals=n.length > 0; + if(!normals) { + ni=new int[vi.length][3]; + normals=computeNormals(v,vi,n,ni) > 0; + } + if(p.length > 0) + m=mean(p); + m=material(m,light); + draw(f,v,vi,n,ni,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0,p,pi); +} + +// Draw triangles on a picture. +void draw(picture pic=currentpicture, triple[] v, int[][] vi, + triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={}, + int[][] pi={}, light light=currentlight) +{ + bool prc=prc(); + bool normals=n.length > 0; + if(!normals) { + ni=new int[vi.length][3]; + normals=computeNormals(v,vi,n,ni) > 0; + } + bool colors=pi.length > 0; + + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + triple[] v=t*v; + triple[] n=t*n; + + if(is3D()) { + draw(f,v,vi,n,ni,m,p,pi,light); + if(pic != null) { + for(int[] vii : vi) + for(int viij : vii) + pic.addPoint(project(v[viij],P)); + } + } else if(pic != null) { + static int[] edges={0,0,1}; + if(colors) { + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + int[] pii=pi[i]; + gouraudshade(pic,project(v[vii[0]],P)--project(v[vii[1]],P)-- + project(v[vii[2]],P)--cycle, + new pen[] {p[pii[0]],p[pii[1]],p[pii[2]]},edges); + } + } else { + if(normals) { + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + int[] nii=ni[i]; + gouraudshade(pic,project(v[vii[0]],P)--project(v[vii[1]],P)-- + project(v[vii[2]],P)--cycle, + new pen[] {color(n[nii[0]],m,light), + color(n[nii[1]],m,light), + color(n[nii[2]],m,light)},edges); + } + } else { + for(int i=0; i < vi.length; ++i) { + int[] vii=vi[i]; + path g=project(v[vii[0]],P)--project(v[vii[1]],P)-- + project(v[vii[2]],P)--cycle; + pen p=color(n[ni[i][0]],m,light); + fill(pic,g,p); + if(prc && opacity(m.diffuse()) == 1) // Fill subdivision cracks + draw(pic,g,p); + } + } + } + } + },true); + + for(int[] vii : vi) + for(int viij : vii) + pic.addPoint(v[viij]); +} + +void tensorshade(transform t=identity(), frame f, patch s, + material m, light light=currentlight, projection P) +{ + pen[] p; + if(s.triangular) { + p=s.colorstriangular(m,light); + p.push(p[0]); + s=tensor(s); + } else p=s.colors(m,light); + path g=t*project(s.external(),P,1); + pair[] internal=t*project(s.internal(),P); + pen fillrule=m.diffuse(); + if(inside(g,internal[0],fillrule) && inside(g,internal[1],fillrule) && + inside(g,internal[2],fillrule) && inside(g,internal[3],fillrule)) { + if(p[0] == p[1] && p[1] == p[2] && p[2] == p[3]) + fill(f,g,fillrule+p[0]); + else + tensorshade(f,g,fillrule,p,internal); + } else { + tensorshade(f,box(t*s.min(P),t*s.max(P)),fillrule,p,g,internal); + } +} + +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=nolight, string name="", + render render=defaultrender, projection P=currentprojection) +{ + bool is3D=is3D(); + if(is3D) { + bool prc=prc(); + if(s.draw != null && (settings.outformat == "html" || + (prc && s.PRCprimitive))) { + for(int k=0; k < s.s.length; ++k) + draw3D(f,s.s[k],surfacepen[k],light,primitive=true); + s.draw(f,s.T,surfacepen,light,render); + } else { + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "surface" : name,render); + + // 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[] {dot(P.normal,camera-s.s[i].cornermean()),i}; + + depth=sort(depth); + + for(int p=depth.length-1; p >= 0; --p) { + real[] a=depth[p]; + int k=round(a[1]); + draw3D(f,s.s[k],surfacepen[k],light); + } + + if(group) + endgroup3(f); + + pen modifiers=thin()+squarecap; + for(int p=depth.length-1; p >= 0; --p) { + real[] a=depth[p]; + int k=round(a[1]); + patch S=s.s[k]; + pen meshpen=meshpen[k]; + if(!invisible(meshpen) && !S.triangular) { + if(group) + begingroup3(f,meshname(name),render); + meshpen=modifiers+meshpen; + real step=nu == 0 ? 0 : 1/nu; + for(int i=0; i <= nu; ++i) + draw(f,S.uequals(i*step),meshpen,meshlight,partname(i,render), + render); + step=nv == 0 ? 0 : 1/nv; + for(int j=0; j <= nv; ++j) + draw(f,S.vequals(j*step),meshpen,meshlight,partname(j,render), + render); + if(group) + endgroup3(f); + } + } + } + } + if(!is3D || settings.render == 0) { + begingroup(f); + // Sort patches by mean distance from camera + triple camera=P.camera; + if(P.infinity) { + triple m=min(s); + triple M=max(s); + camera=P.target+camerafactor*(abs(M-m)+abs(m-P.target))*unit(P.vector()); + } + + real[][] depth=new real[s.s.length][]; + for(int i=0; i < depth.length; ++i) + depth[i]=new real[] {dot(P.normal,camera-s.s[i].cornermean()),i}; + + depth=sort(depth); + + light.T=shiftless(P.T.modelview); + + // Draw from farthest to nearest + for(int p=depth.length-1; p >= 0; --p) { + real[] a=depth[p]; + int k=round(a[1]); + tensorshade(t,f,s.s[k],surfacepen[k],light,P); + pen meshpen=meshpen[k]; + if(!invisible(meshpen)) + draw(f,t*project(s.s[k].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=nolight, string name="", + render render=defaultrender, projection P=currentprojection) +{ + material[] surfacepen={surfacepen}; + pen[] meshpen={meshpen}; + surfacepen.cyclic=true; + meshpen.cyclic=true; + draw(t,f,s,nu,nv,surfacepen,meshpen,light,meshlight,name,render,P); +} + +void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1, + material[] surfacepen, pen[] meshpen=nullpens, + light light=currentlight, light meshlight=nolight, string name="", + render render=defaultrender) +{ + if(s.empty()) return; + + bool cyclic=surfacepen.cyclic; + surfacepen=copy(surfacepen); + surfacepen.cyclic=cyclic; + cyclic=meshpen.cyclic; + meshpen=copy(meshpen); + meshpen.cyclic=cyclic; + + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + surface S=t*s; + if(is3D()) + draw(f,S,nu,nv,surfacepen,meshpen,light,meshlight,name,render); + if(pic != null) { + pic.add(new void(frame f, transform T) { + draw(T,f,S,nu,nv,surfacepen,meshpen,light,meshlight,P); + },true); + pic.addPoint(min(S,P)); + pic.addPoint(max(S,P)); + } + },true); + pic.addPoint(min(s)); + pic.addPoint(max(s)); + + pen modifiers; + if(is3D()) modifiers=thin()+squarecap; + for(int k=0; k < s.s.length; ++k) { + patch S=s.s[k]; + pen meshpen=meshpen[k]; + if(!invisible(meshpen) && !S.triangular) { + 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=nolight, string name="", + render render=defaultrender) +{ + material[] surfacepen={surfacepen}; + pen[] meshpen={meshpen}; + surfacepen.cyclic=true; + meshpen.cyclic=true; + draw(pic,s,nu,nv,surfacepen,meshpen,light,meshlight,name,render); +} + +void draw(picture pic=currentpicture, surface s, int nu=1, int nv=1, + material[] surfacepen, pen meshpen, + light light=currentlight, light meshlight=nolight, string name="", + render render=defaultrender) +{ + pen[] meshpen={meshpen}; + meshpen.cyclic=true; + draw(pic,s,nu,nv,surfacepen,meshpen,light,meshlight,name,render); +} + +surface extrude(path3 p, path3 q) +{ + static patch[] allocate; + return surface(...sequence(new patch(int i) { + return patch(subpath(p,i,i+1)--subpath(q,i+1,i)--cycle); + },length(p))); +} + +surface extrude(path3 p, triple axis=Z) +{ + return extrude(p,shift(axis)*p); +} + +surface extrude(path p, triple plane(pair)=XYplane, triple axis=Z) +{ + return extrude(path3(p,plane),axis); +} + +surface extrude(explicit path[] p, triple axis=Z) +{ + surface s; + for(path g:p) + s.append(extrude(g,axis)); + return s; +} + +triple rectify(triple dir) +{ + real scale=max(abs(dir.x),abs(dir.y),abs(dir.z)); + if(scale != 0) dir *= 0.5/scale; + dir += (0.5,0.5,0.5); + return dir; +} + +path3[] align(path3[] g, transform3 t=identity4, triple position, + triple align, pen p=currentpen) +{ + if(determinant(t) == 0 || g.length == 0) return g; + triple m=min(g); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(g)-m); + return shift(position+align*labelmargin(p))*t*shift(-a)*g; +} + +surface align(surface s, transform3 t=identity4, triple position, + triple align, pen p=currentpen) +{ + if(determinant(t) == 0 || s.s.length == 0) return s; + triple m=min(s); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(s)-m); + return shift(position+align*labelmargin(p))*t*shift(-a)*s; +} + +surface surface(Label L, triple position=O, bool bbox=false) +{ + surface s=surface(texpath(L,bbox=bbox)); + return L.align.is3D ? align(s,L.T3,position,L.align.dir3,L.p) : + shift(position)*L.T3*s; +} + +private path[] path(Label L, pair z=0, projection P) +{ + path[] g=texpath(L,bbox=P.bboxonly); + return L.align.is3D ? align(g,z,project(L.align.dir3,P)-project(O,P),L.p) : + shift(z)*g; +} + +transform3 alignshift(path3[] g, transform3 t=identity4, triple position, + triple align) +{ + if(determinant(t) == 0) return identity4; + triple m=min(g); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(g)-m); + return shift(-a); +} + +transform3 alignshift(surface s, transform3 t=identity4, triple position, + triple align) +{ + if(determinant(t) == 0) return identity4; + triple m=min(s); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(s)-m); + return shift(-a); +} + +transform3 aligntransform(path3[] g, transform3 t=identity4, triple position, + triple align, pen p=currentpen) +{ + if(determinant(t) == 0) return identity4; + triple m=min(g); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(g)-m); + return shift(position+align*labelmargin(p))*t*shift(-a); +} + +transform3 aligntransform(surface s, transform3 t=identity4, triple position, + triple align, pen p=currentpen) +{ + if(determinant(t) == 0) return identity4; + triple m=min(s); + triple dir=rectify(inverse(t)*-align); + triple a=m+realmult(dir,max(s)-m); + return shift(position+align*labelmargin(p))*t*shift(-a); +} + +void label(frame f, Label L, triple position, align align=NoAlign, + pen p=currentpen, light light=nolight, + string name="", render render=defaultrender, + interaction interaction=LabelInteraction(), + projection P=currentprojection) +{ + bool prc=prc(); + Label L=L.copy(); + L.align(align); + L.p(p); + if(interaction.targetsize && settings.render != 0) + L.T=L.T*scale(abs(P.camera-position)/abs(P.vector())); + transform3 T=transform3(P); + if(L.defaulttransform3) + L.T3=T; + + if(is3D()) { + bool lighton=light.on(); + if(name == "") name=L.s; + if(prc() && interaction.type == Billboard.type) { + surface s=surface(texpath(L)); + transform3 centering=L.align.is3D ? + alignshift(s,L.T3,position,L.align.dir3) : identity4; + transform3 positioning= + shift(L.align.is3D ? position+L.align.dir3*labelmargin(L.p) : position); + frame f1,f2,f3; + begingroup3(f1,name,render); + if(L.defaulttransform3) + begingroup3(f3,render,position,interaction.type); + else { + begingroup3(f2,render,position,interaction.type); + begingroup3(f3,render,position); + } + for(patch S : s.s) { + S=centering*S; + draw3D(f3,S,position,L.p,light,interaction); + // Fill subdivision cracks + if(prc && render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f3,S.external(),position,L.p,light,interaction); + } + endgroup3(f3); + if(L.defaulttransform3) + add(f1,T*f3); + else { + add(f2,inverse(T)*L.T3*f3); + endgroup3(f2); + add(f1,T*f2); + } + endgroup3(f1); + add(f,positioning*f1); + } else { + begingroup3(f,name,render); + for(patch S : surface(L,position).s) { + triple V=L.align.is3D ? position+L.align.dir3*labelmargin(L.p) : + position; + draw3D(f,S,V,L.p,light,interaction); + // Fill subdivision cracks + if(prc && render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f,S.external(),V,L.p,light,interaction); + } + endgroup3(f); + } + } else { + pen p=color(L.T3*Z,L.p,light,shiftless(P.T.modelview)); + if(L.defaulttransform3) { + if(L.filltype == NoFill) + fill(f,path(L,project(position,P.t),P),p); + else { + frame d; + fill(d,path(L,project(position,P.t),P),p); + add(f,d,L.filltype); + } + } else + for(patch S : surface(L,position).s) + fill(f,project(S.external(),P,1),p); + } +} + +void label(picture pic=currentpicture, Label L, triple position, + align align=NoAlign, pen p=currentpen, + light light=nolight, string name="", + render render=defaultrender, + interaction interaction=LabelInteraction()) +{ + Label L=L.copy(); + L.align(align); + L.p(p); + L.position(0); + + pic.add(new void(frame f, transform3 t, picture pic2, projection P) { + // Handle relative projected 3D alignments. + bool prc=prc(); + Label L=L.copy(); + triple v=t*position; + if(!align.is3D && L.align.relative && L.align.dir3 != O && + determinant(P.t) != 0) + L.align(L.align.dir*unit(project(v+L.align.dir3,P.t)-project(v,P.t))); + + if(interaction.targetsize && settings.render != 0) + L.T=L.T*scale(abs(P.camera-v)/abs(P.vector())); + transform3 T=transform3(P); + if(L.defaulttransform3) + L.T3=T; + + if(is3D()) { + bool lighton=light.on(); + if(name == "") name=L.s; + if(prc && interaction.type == Billboard.type) { + surface s=surface(texpath(L,bbox=P.bboxonly)); + if(s.s.length > 0) { + transform3 centering=L.align.is3D ? + alignshift(s,L.T3,v,L.align.dir3) : identity4; + transform3 positioning= + shift(L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v); + frame f1,f2,f3; + begingroup3(f1,name,render); + if(L.defaulttransform3) + begingroup3(f3,render,v,interaction.type); + else { + begingroup3(f2,render,v,interaction.type); + begingroup3(f3,render,v); + } + for(patch S : s.s) { + S=centering*S; + draw3D(f3,S,v,L.p,light,interaction); + // Fill subdivision cracks + if(prc && render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f3,S.external(),v,L.p,light,interaction); + } + endgroup3(f3); + if(L.defaulttransform3) + add(f1,T*f3); + else { + add(f2,inverse(T)*L.T3*f3); + endgroup3(f2); + add(f1,T*f2); + } + endgroup3(f1); + add(f,positioning*f1); + } + } else { + begingroup3(f,name,render); + for(patch S : surface(L,v,bbox=P.bboxonly).s) { + triple V=L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v; + draw3D(f,S,V,L.p,light,interaction); + // Fill subdivision cracks + if(prc && render.labelfill && opacity(L.p) == 1 && !lighton) + _draw(f,S.external(),V,L.p,light,interaction); + } + endgroup3(f); + } + } + + if(pic2 != null) { + pen p=color(L.T3*Z,L.p,light,shiftless(P.T.modelview)); + if(L.defaulttransform3) { + if(L.filltype == NoFill) + fill(project(v,P.t),pic2,path(L,P),p); + else { + picture d; + fill(project(v,P.t),d,path(L,P),p); + add(pic2,d,L.filltype); + } + } else + pic2.add(new void(frame f, transform T) { + for(patch S : surface(L,v).s) + fill(f,T*project(S.external(),P,1),p); + }); + } + + },!L.defaulttransform3); + + Label L=L.copy(); + + if(interaction.targetsize && settings.render != 0) + L.T=L.T*scale(abs(currentprojection.camera-position)/ + abs(currentprojection.vector())); + path[] g=texpath(L,bbox=true); + if(g.length == 0 || (g.length == 1 && size(g[0]) == 0)) return; + if(L.defaulttransform3) + L.T3=transform3(currentprojection); + path3[] G=path3(g); + G=L.align.is3D ? align(G,L.T3,O,L.align.dir3,L.p) : L.T3*G; + pic.addBox(position,position,min(G),max(G)); +} + +void label(picture pic=currentpicture, Label L, path3 g, align align=NoAlign, + pen p=currentpen, light light=nolight, string name="", + interaction interaction=LabelInteraction()) +{ + Label L=L.copy(); + L.align(align); + L.p(p); + bool relative=L.position.relative; + real position=L.position.position.x; + if(L.defaultposition) {relative=true; position=0.5;} + if(relative) position=reltime(g,position); + if(L.align.default) { + align a; + a.init(-I*(position <= sqrtEpsilon ? S : + position >= length(g)-sqrtEpsilon ? N : E),relative=true); + a.dir3=dir(g,position); // Pass 3D direction via unused field. + L.align(a); + } + label(pic,L,point(g,position),light,name,interaction); +} + +surface extrude(Label L, triple axis=Z) +{ + Label L=L.copy(); + path[] g=texpath(L); + surface S=extrude(g,axis); + surface s=surface(g); + S.append(s); + S.append(shift(axis)*s); + return S; +} + +restricted surface nullsurface; + +// Embed a Label onto a surface. +surface surface(Label L, surface s, real uoffset, real voffset, + real height=0, bool bottom=true, bool top=true) +{ + int nu=s.index.length; + int nv; + if(nu == 0) nu=nv=1; + else { + nv=s.index[0].length; + if(nv == 0) nv=1; + } + + path[] g=texpath(L); + pair m=min(g); + pair M=max(g); + pair lambda=inverse(L.T*scale(nu-epsilon,nv-epsilon))*(M-m); + lambda=(abs(lambda.x),abs(lambda.y)); + path[] G=bezulate(g); + + path3 transpath(path p, real height) { + return path3(unstraighten(p),new triple(pair z) { + real u=uoffset+(z.x-m.x)/lambda.x; + real v=voffset+(z.y-m.y)/lambda.y; + if(((u < 0 || u >= nu) && !s.ucyclic()) || + ((v < 0 || v >= nv) && !s.vcyclic())) { + warning("cannotfit","cannot fit string to surface"); + u=v=0; + } + return s.point(u,v)+height*unit(s.normal(u,v)); + }); + } + + surface s; + for(path p : G) { + for(path g : regularize(p)) { + path3 b; + bool extrude=height > 0; + if(bottom || extrude) + b=transpath(g,0); + if(bottom) s.s.push(patch(b)); + if(top || extrude) { + path3 h=transpath(g,height); + if(top) s.s.push(patch(h)); + if(extrude) s.append(extrude(b,h)); + } + } + } + return s; +} + +private real a=4/3*(sqrt(2)-1); + +private transform3 t1=rotate(90,O,Z); +private transform3 t2=t1*t1; +private transform3 t3=t2*t1; +private transform3 i=xscale3(-1)*zscale3(-1); + +// Degenerate first octant +restricted patch octant1x=patch(X{Y}..{-X}Y{Z}..{-Y}Z..Z{X}..{-Z}cycle, + new triple[] {(1,a,a),(a,1,a),(a^2,a,1), + (a,a^2,1)}); + +surface octant1(real transition) +{ + private triple[][][] P=hsplit(octant1x.P,transition); + private patch P0=patch(P[0]); + private patch P1=patch(P[1][0][0]..controls P[1][1][0] and P[1][2][0].. + P[1][3][0]..controls P[1][3][1] and P[1][3][2].. + P[1][3][3]..controls P[1][0][2] and P[1][0][1].. + cycle,O); + + // Set internal control point of P1 to match normals at P0.point(1/2,1). + triple n=P0.normal(1/2,1); + triple[][] P=P1.P; + triple u=-P[0][0]-P[1][0]+P[2][0]+P[3][0]; + triple v=-P[0][0]-2*P[1][0]+P[1][1]-P[2][0]+P[3][1]; + triple w=cross(u,v+(0,0,2)); + real i=0.5*(n.z*w.x/n.x-w.z)/(u.x-u.y); + P1.P[2][1]=(i,i,1); + return surface(P0,P1); +} + +// Nondegenerate first octant +restricted surface octant1=octant1(0.95); + +restricted surface unithemisphere=surface(octant1,t1*octant1,t2*octant1, + t3*octant1); +restricted surface unitsphere=surface(octant1,t1*octant1,t2*octant1,t3*octant1, + i*octant1,i*t1*octant1,i*t2*octant1, + i*t3*octant1); + +unitsphere.draw= + new void(frame f, transform3 t=identity4, material[] m, + light light=currentlight, render render=defaultrender) + { + material m=material(m[0],light); + drawSphere(f,t,half=false,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0, + render.sphere); + }; + +unithemisphere.draw= + new void(frame f, transform3 t=identity4, material[] m, + light light=currentlight, render render=defaultrender) + { + material m=material(m[0],light); + drawSphere(f,t,half=true,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0, + render.sphere); + }; + +restricted patch unitfrustum1(real ta, real tb) +{ + real s1=interp(ta,tb,1/3); + real s2=interp(ta,tb,2/3); + return patch(interp(Z,X,tb){Y}..{-X}interp(Z,Y,tb)--interp(Z,Y,ta){X}..{-Y} + interp(Z,X,ta)--cycle, + new triple[] {(s2,s2*a,1-s2),(s2*a,s2,1-s2),(s1*a,s1,1-s1), + (s1,s1*a,1-s1)}); +} + +restricted surface unitfrustum(real ta, real tb) +{ + patch p=unitfrustum1(ta,tb); + return surface(p,t1*p,t2*p,t3*p); +} + +restricted surface unitcone=surface(unitfrustum(0,1)); +restricted surface unitsolidcone=surface(patch(unitcircle3)...unitcone.s); + +// Construct an approximate cone over an arbitrary base. +surface cone(path3 base, triple vertex) {return extrude(base,vertex--cycle);} + +private patch unitcylinder1=patch(X{Y}..{-X}Y--Y+Z{X}..{-Y}X+Z--cycle); + +restricted surface unitcylinder=surface(unitcylinder1,t1*unitcylinder1, + t2*unitcylinder1,t3*unitcylinder1); + +drawfcn unitcylinderDraw(bool core) { + return new void(frame f, transform3 t=identity4, material[] m, + light light=currentlight, render render=defaultrender) + { + material m=material(m[0],light); + drawCylinder(f,t,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0, + m.opacity == 1 ? core : false); + }; +} + +unitcylinder.draw=unitcylinderDraw(false); + +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); + +unitdisk.draw= + new void(frame f, transform3 t=identity4, material[] m, + light light=currentlight, render render=defaultrender) + { + material m=material(m[0],light); + drawDisk(f,t,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0); + }; + +void dot(frame f, triple v, material p=currentpen, + light light=nolight, string name="", + render render=defaultrender, projection P=currentprojection) +{ + if(name == "" && render.defaultnames) name="dot"; + pen q=(pen) p; + real size=0.5*linewidth(dotsize(q)+q); + transform3 T=shift(v)*scale3(size); + draw(f,T*unitsphere,p,light,name,render,P); +} + +void dot(frame f, triple[] v, material p=currentpen, light light=nolight, + string name="", render render=defaultrender, + projection P=currentprojection) +{ + if(v.length > 0) { + // Remove duplicate points. + v=sort(v,lexorder); + + triple last=v[0]; + dot(f,last,p,light,name,render,P); + for(int i=1; i < v.length; ++i) { + triple V=v[i]; + if(V != last) { + dot(f,V,p,light,name,render,P); + last=V; + } + } + } +} + +void dot(frame f, path3 g, material p=currentpen, light light=nolight, + string name="", render render=defaultrender, + projection P=currentprojection) +{ + dot(f,sequence(new triple(int i) {return point(g,i);},size(g)), + p,light,name,render,P); +} + +void dot(frame f, path3[] g, material p=currentpen, light light=nolight, + string name="", render render=defaultrender, + projection P=currentprojection) +{ + int sum; + for(path3 G : g) + sum += size(G); + int i,j; + dot(f,sequence(new triple(int) { + while(j >= size(g[i])) { + ++i; + j=0; + } + triple v=point(g[i],j); + ++j; + return v; + },sum),p,light,name,render,P); +} + +void dot(picture pic=currentpicture, triple v, material p=currentpen, + light light=nolight, string name="", render render=defaultrender) +{ + pen q=(pen) p; + real size=0.5*linewidth(dotsize(q)+q); + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + triple V=t*v; + dot(f,V,p,light,name,render,P); + if(pic != null) + dot(pic,project(V,P.t),q); + },true); + triple R=size*(1,1,1); + pic.addBox(v,v,-R,R); +} + +void dot(picture pic=currentpicture, triple[] v, material p=currentpen, + light light=nolight, string name="", render render=defaultrender) +{ + if(v.length > 0) { + // Remove duplicate points. + v=sort(v,lexorder); + + triple last=v[0]; + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(pic,name == "" ? "dots" : name,render); + dot(pic,last,p,light,partname(0,render),render); + int k=0; + for(int i=1; i < v.length; ++i) { + triple V=v[i]; + if(V != last) { + dot(pic,V,p,light,partname(++k,render),render); + last=V; + } + } + if(group) + endgroup3(pic); + } +} + +void dot(picture pic=currentpicture, explicit path3 g, material p=currentpen, + light light=nolight, string name="", + render render=defaultrender) +{ + dot(pic,sequence(new triple(int i) {return point(g,i);},size(g)), + p,light,name,render); +} + +void dot(picture pic=currentpicture, path3[] g, material p=currentpen, + light light=nolight, string name="", render render=defaultrender) +{ + int sum; + for(path3 G : g) + sum += size(G); + int i,j; + dot(pic,sequence(new triple(int) { + while(j >= size(g[i])) { + ++i; + j=0; + } + triple v=point(g[i],j); + ++j; + return v; + },sum),p,light,name,render); +} + +void dot(picture pic=currentpicture, Label L, triple v, align align=NoAlign, + string format=defaultformat, material p=currentpen, + light light=nolight, string name="", render render=defaultrender) +{ + Label L=L.copy(); + if(L.s == "") { + if(format == "") format=defaultformat; + L.s="("+format(format,v.x)+","+format(format,v.y)+","+ + format(format,v.z)+")"; + } + L.align(align,E); + L.p((pen) p); + dot(pic,v,p,light,name,render); + label(pic,L,v,render); +} + +void pixel(picture pic=currentpicture, triple v, pen p=currentpen, + real width=1) +{ + real h=0.5*width; + pic.add(new void(frame f, transform3 t, picture pic, projection P) { + triple V=t*v; + if(is3D()) + drawpixel(f,V,p,width); + if(pic != null) { + triple R=h*unit(cross(unit(P.vector()),P.up)); + pair z=project(V,P.t); + real h=0.5*abs(project(V+R,P.t)-project(V-R,P.t)); + pair r=h*(1,1)/mm; + fill(pic,box(z-r,z+r),p,false); + } + },true); + triple R=h*(1,1,1); + pic.addBox(v,v,-R,R); +} + +pair minbound(triple[] A, projection P) +{ + pair b=project(A[0],P); + for(triple v : A) + b=minbound(b,project(v,P.t)); + return b; +} + +pair maxbound(triple[] A, projection P) +{ + pair b=project(A[0],P); + for(triple v : A) + b=maxbound(b,project(v,P.t)); + return b; +} + +pair minbound(triple[][] A, projection P) +{ + pair b=project(A[0][0],P); + for(triple[] a : A) { + for(triple v : a) { + b=minbound(b,project(v,P.t)); + } + } + return b; +} + +pair maxbound(triple[][] A, projection P) +{ + pair b=project(A[0][0],P); + for(triple[] a : A) { + for(triple v : a) { + b=maxbound(b,project(v,P.t)); + } + } + return b; +} + +triple[][] operator / (triple[][] a, real[][] b) +{ + triple[][] A=new triple[a.length][]; + for(int i=0; i < a.length; ++i) { + triple[] ai=a[i]; + real[] bi=b[i]; + A[i]=sequence(new triple(int j) {return ai[j]/bi[j];},ai.length); + } + return A; +} + +// Draw a NURBS curve. +void draw(picture pic=currentpicture, triple[] P, real[] knot, + real[] weights=new real[], pen p=currentpen, string name="", + render render=defaultrender) +{ + P=copy(P); + knot=copy(knot); + weights=copy(weights); + pic.add(new void(frame f, transform3 t, picture pic, projection Q) { + if(is3D()) { + triple[] P=t*P; + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "curve" : name,render); + draw(f,P,knot,weights,p); + if(group) + endgroup3(f); + if(pic != null) + pic.addBox(minbound(P,Q),maxbound(P,Q)); + } + },true); + pic.addBox(minbound(P),maxbound(P)); +} + +// Draw a NURBS surface. +void draw(picture pic=currentpicture, triple[][] P, real[] uknot, real[] vknot, + real[][] weights=new real[][], material m=currentpen, + pen[] colors=new pen[], light light=currentlight, string name="", + render render=defaultrender) +{ + if(colors.length > 0) + m=mean(colors); + m=material(m,light); + bool lighton=light.on(); + P=copy(P); + uknot=copy(uknot); + vknot=copy(vknot); + weights=copy(weights); + colors=copy(colors); + pic.add(new void(frame f, transform3 t, picture pic, projection Q) { + if(is3D()) { + bool group=name != "" || render.defaultnames; + if(group) + begingroup3(f,name == "" ? "surface" : name,render); + triple[][] P=t*P; + draw(f,P,uknot,vknot,weights,m.p,m.opacity,m.shininess,m.metallic, + m.fresnel0,colors); + if(group) + endgroup3(f); + if(pic != null) + pic.addBox(minbound(P,Q),maxbound(P,Q)); + } + },true); + pic.addBox(minbound(P),maxbound(P)); +} diff --git a/Build/source/utils/asymptote/base/three_tube.asy b/Build/source/utils/asymptote/base/three_tube.asy new file mode 100644 index 00000000000..60085a7d512 --- /dev/null +++ b/Build/source/utils/asymptote/base/three_tube.asy @@ -0,0 +1,234 @@ +struct rmf { + triple p,r,t,s; + void operator init(triple p, triple r, triple t) { + this.p=p; + this.r=r; + this.t=t; + s=cross(t,r); + } + + transform3 transform() { + return transform3(r,s,t); + } +} + +// Rotation minimizing frame +// http://www.cs.hku.hk/research/techreps/document/TR-2007-07.pdf +rmf[] rmf(path3 g, real[] t, triple perp=O) +{ + triple T=dir(g,0); + triple Tp=abs(perp) < sqrtEpsilon ? perp(T) : unit(perp); + rmf[] R=new rmf[t.length]; + R[0]=rmf(point(g,0),Tp,T); + for(int i=1; i < t.length; ++i) { + rmf Ri=R[i-1]; + real t=t[i]; + triple p=point(g,t); + triple v1=p-Ri.p; + if(v1 != O) { + triple r=Ri.r; + triple u1=unit(v1); + triple ti=Ri.t; + triple tp=ti-2*dot(u1,ti)*u1; + ti=dir(g,t); + triple rp=r-2*dot(u1,r)*u1; + triple u2=unit(ti-tp); + rp=rp-2*dot(u2,rp)*u2; + R[i]=rmf(p,unit(rp),unit(ti)); + } else + R[i]=R[i-1]; + } + return R; +} + +rmf[] rmf(triple z0, triple c0, triple c1, triple z1, real[] t, triple perp=O) +{ + static triple s0; + + real norm=sqrtEpsilon*max(abs(z0),abs(c0),abs(c1),abs(z1)); + + // Special case of dir for t in (0,1]. + triple dir(real t) { + if(t == 1) { + triple dir=z1-c1; + if(abs(dir) > norm) return unit(dir); + dir=2.0*c1-c0-z1; + if(abs(dir) > norm) return unit(dir); + return unit(z1-z0+3.0*(c0-c1)); + } + triple a=z1-z0+3.0*(c0-c1); + triple b=2.0*(z0+c1)-4.0*c0; + triple c=c0-z0; + triple dir=a*t*t+b*t+c; + if(abs(dir) > norm) return unit(dir); + dir=2.0*a*t+b; + if(abs(dir) > norm) return unit(dir); + return unit(a); + } + + triple T=c0-z0; + if(abs(T) < norm) { + T=z0-2*c0+c1; + if(abs(T) < norm) + T=z1-z0+3.0*(c0-c1); + } + T=unit(T); + triple Tp=perp == O ? cross(s0,T) : perp; + Tp=abs(Tp) < sqrtEpsilon ? perp(T) : unit(Tp); + rmf[] R=new rmf[t.length]; + R[0]=rmf(z0,Tp,T); + + for(int i=1; i < t.length; ++i) { + rmf Ri=R[i-1]; + real t=t[i]; + triple p=bezier(z0,c0,c1,z1,t); + triple v1=p-Ri.p; + if(v1 != O) { + triple r=Ri.r; + triple u1=unit(v1); + triple ti=Ri.t; + triple tp=ti-2*dot(u1,ti)*u1; + ti=dir(t); + triple rp=r-2*dot(u1,r)*u1; + triple u2=unit(ti-tp); + rp=rp-2*dot(u2,rp)*u2; + R[i]=rmf(p,unit(rp),unit(ti)); + } else + R[i]=R[i-1]; + } + s0=R[t.length-1].s; + return R; +} + +surface tube(triple z0, triple c0, triple c1, triple z1, real w) +{ + surface s; + static real[] T={0,1/3,2/3,1}; + rmf[] rmf=rmf(z0,c0,c1,z1,T); + + real aw=a*w; + triple[] arc={(w,0,0),(w,aw,0),(aw,w,0),(0,w,0)}; + triple[] g={z0,c0,c1,z1}; + + void f(transform3 R) { + triple[][] P=new triple[4][]; + for(int i=0; i < 4; ++i) { + transform3 T=shift(g[i])*rmf[i].transform()*R; + P[i]=new triple[] {T*arc[0],T*arc[1],T*arc[2],T*arc[3]}; + } + s.push(patch(P,copy=false)); + } + + f(identity4); + f(t1); + f(t2); + f(t3); + + s.PRCprimitive=false; + s.draw=new void(frame f, transform3 t=identity4, material[] m, + light light=currentlight, render render=defaultrender) + { + material m=material(m[0],light); + drawTube(f,t*g,w,m.p,m.opacity,m.shininess,m.metallic,m.fresnel0, + t*min(s),t*max(s),m.opacity == 1); + }; + return s; +} + +real tubethreshold=20; + +// Note: casting an array of surfaces to a single surface will disable +// primitive compression. +surface operator cast(surface[] s) { + surface S; + for(surface p : s) + S.append(p); + return S; +} + +struct tube +{ + surface[] s; + path3 center; // tube axis + + void Null(transform3) {} + void Null(transform3, bool) {} + + surface[] render(path3 g, real r) { + triple z0=point(g,0); + triple c0=postcontrol(g,0); + triple c1=precontrol(g,1); + triple z1=point(g,1); + real norm=sqrtEpsilon*max(abs(z0),abs(c0),abs(c1),abs(z1),r); + surface[] s; + void Split(triple z0, triple c0, triple c1, triple z1, + int depth=mantissaBits) { + if(depth > 0) { + pair threshold(triple z0, triple c0, triple c1) { + triple u=c1-z0; + triple v=c0-z0; + real x=abs(v); + return (x,abs(u*x^2-dot(u,v)*v)); + } + + pair a0=threshold(z0,c0,c1); + pair a1=threshold(z1,c1,c0); + real rL=r*arclength(z0,c0,c1,z1)*tubethreshold; + if((a0.x >= norm && rL*a0.y^2 > a0.x^8) || + (a1.x >= norm && rL*a1.y^2 > a1.x^8)) { + 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); + --depth; + Split(z0,m0,m3,m5,depth); + Split(m5,m4,m2,z1,depth); + return; + } + } + + s.push(tube(z0,c0,c1,z1,r)); + } + Split(z0,c0,c1,z1); + return s; + } + + void operator init(path3 p, real width) { + center=p; + real r=0.5*width; + + void generate(path3 p) { + int n=length(p); + for(int i=0; i < n; ++i) { + if(straight(p,i)) { + triple v=point(p,i); + triple u=point(p,i+1)-v; + transform3 t=shift(v)*align(unit(u))*scale(r,r,abs(u)); + // Draw opaque surfaces with core for better small-scale rendering. + surface unittube=t*unitcylinder; + unittube.draw=unitcylinderDraw(core=true); + s.push(unittube); + } else + s.append(render(subpath(p,i,i+1),r)); + } + } + + transform3 t=scale3(r); + bool cyclic=cyclic(p); + int begin=0; + int n=length(p); + for(int i=cyclic ? 0 : 1; i < n; ++i) + if(abs(dir(p,i,1)-dir(p,i,-1)) > sqrtEpsilon) { + generate(subpath(p,begin,i)); + triple dir=dir(p,i,-1); + transform3 T=t*align(dir); + s.push(shift(point(p,i))*T*(straight(p,i-1) && straight(p,i) ? + unithemisphere : unitsphere)); + begin=i; + } + path3 g=subpath(p,begin,n); + generate(g); + } +} diff --git a/Build/source/utils/asymptote/base/tree.asy b/Build/source/utils/asymptote/base/tree.asy new file mode 100644 index 00000000000..1e603ec4b6b --- /dev/null +++ b/Build/source/utils/asymptote/base/tree.asy @@ -0,0 +1,86 @@ +/***** + * treedef.asy + * Andy Hammerlindl 2003/10/25 + * + * Implements a dynamic binary search tree. + *****/ + +struct tree +{ + tree left; + tree right; + int key = 0; + int value = 0; +} + +tree newtree() +{ + return null; +} + +tree add(tree t, int key, int value) +{ + if (t == null) { + tree tt; + tt.key = key; tt.value = value; + return tt; + } + else if (key == t.key) { + return t; + } + else if (key < t.key) { + tree tt; + tt.left = add(t.left, key, value); + tt.key = t.key; + tt.value = t.value; + tt.right = t.right; + return tt; + } + else { + tree tt; + tt.left = t.left; + tt.key = t.key; + tt.value = t.value; + tt.right = add(t.right, key, value); + return tt; + } +} + +bool contains(tree t, int key) +{ + if (t == null) + return false; + else if (key == t.key) + return true; + else if (key < t.key) + return contains(t.left, key); + else + return contains(t.right, key); +} + +int lookup(tree t, int key) +{ + if (t == null) + return 0; + else if (key == t.key) + return t.value; + else if (key < t.key) + return lookup(t.left, key); + else + return lookup(t.right, key); +} + +void write(file out=stdout, tree t) +{ + if (t != null) { + if(t.left != null) { + write(out,t.left); + } + write(out,t.key); + write(out,"->"); + write(out,t.value,endl); + if (t.right != null) { + write(out,t.right); + } + } +} diff --git a/Build/source/utils/asymptote/base/trembling.asy b/Build/source/utils/asymptote/base/trembling.asy new file mode 100644 index 00000000000..f9f0d6a7474 --- /dev/null +++ b/Build/source/utils/asymptote/base/trembling.asy @@ -0,0 +1,199 @@ +// Copyright(c) 2008, Philippe Ivaldi. +// Simplified by John Bowman 02Feb2011 +// http: //www.piprime.fr/ +// trembling.asy: handwriting package for the software Asymptote. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +//(at your option) any later version. + +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +// COMMENTARY: + +// THANKS: + +// BUGS: +// magnetic points are experimental... + +// CODE: + +real magneticRadius=1; // unit is bp in postscript coordinates. +real trembleFuzz(){return min(1e-3,magneticRadius/10);} + +real trembleAngle=4, trembleFrequency=0.5, trembleRandom=2; + +struct tremble +{ + static real test=5; + + real angle,frequency,random,fuzz; + + pair[] single(pair[] P) + { + pair[] op; + bool allow; + for(int i=0; i < P.length-1; ++i) { + allow=true; + for(int j=i+1; j < P.length; ++j) { + if(abs(P[i]-P[j]) < magneticRadius) { + allow=false; + break; + } + } + if(allow) op.push(P[i]); + } + if(P.length > 0) op.push(P[P.length-1]); + return op; + } + + real atime(pair m, path g, real fuzz=trembleFuzz()) + {// Return the time of the point on path g nearest to m, within fuzz. + if(length(g) == 0) return 0.0; + real[] t=intersect(m,g,fuzz); + if(t.length > 0) return t[1]; + real ot; + static real eps=sqrt(realEpsilon); + real abmax=abs(max(g)-m), abmin=abs(min(g)-m); + real initr=abs(m-midpoint(g)); + real maxR=2*max(abmax,abmin), step=eps, r=initr; + real shx=1e-4; + transform T=shift(m); + path ig; + if(t.length > 0) ot=t[1]; + real rm=0, rM=r; + while(rM-rm > eps) { + r=(rm+rM)/2; + t=intersect(T*scale(r)*unitcircle,g,fuzz); + if(t.length <= 0) { + rm=r; + } else { + rM=r; + ot=t[1]; + } + } + return ot; + } + + path addnode(path g, real t) + {// Add a node to 'g' at point(g,t). + real l=length(g); + real rt=t % 1; + if(l == 0 || (t > l && !cyclic(g)) || rt == 0) return g; + if(cyclic(g)) t=t % l; + int t0=floor(t); + int t1=t0+1; + pair z0=point(g,t0), z1=point(g,t1), + c0=postcontrol(g,t0), c1=precontrol(g,t1), + m0=(1-rt)*z0+rt*c0, m1=(1-rt)*c0+rt*c1, + m2=(1-rt)*c1+rt*z1, m3=(1-rt)*m0+rt*m1, + m4=(1-rt)*m1+rt*m2; + guide og=subpath(g,0,t0)..controls m0 and m3..point(g,t); + if(cyclic(g)) { + if(t1 < l) + og=og..controls m4 and m2..subpath(g,t1,l)&cycle; + else og=og..controls m4 and m2..cycle; + } else og=og..controls m4 and m2..subpath(g,t1,l); + return og; + } + + path addnodes(path g, real fuzz=trembleFuzz()...pair[] P) + { + pair[] P=single(P); + if(length(g) == 0 || P.length == 0 || magneticRadius <= 0) return g; + path og=g; + for(pair tp: P) { + real t=atime(tp,og,fuzz); + real d=abs(tp-point(og,t)); + if(d < magneticRadius) og=addnode(og,t); + } + return og; + } + + path addnodes(path g, int n) + {// Add 'n' nodes between each node of 'g'. + 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; + } + + void operator init(real angle=trembleAngle, real frequency=trembleFrequency, + real random=trembleRandom, real fuzz=trembleFuzz()) { + this.angle=angle; + this.frequency=frequency; + this.random=random; + this.fuzz=fuzz; + } + + path deform(path g...pair[] magneticPoints) { + /* Return g as it was handwriting. + The postcontrols and precontrols of the nodes of g will be rotated + by an angle proportional to 'angle'(in degrees). + If frequency < 1, floor(1/frequency) nodes will be added to g to + increase the control points. + If frequency>= 1, one point for floor(frequency) will be used to deform + the path. + 'random' controls the randomized coefficient which will be multiplied + by 'angle'. + random is 0 means don't use randomized coefficient; + The higher 'random' is, the more the trembling is randomized. */ + if(length(g) == 0) return g; + g=addnodes(g,fuzz*abs(max(g)-min(g))...magneticPoints); + path tg=g; + frequency=abs(frequency); + int f=abs(floor(1/frequency)-1); + tg=addnodes(tg,f); + int frequency=floor(frequency); + int tf=(frequency == 0) ? 1 : frequency; + int l=length(tg); + guide og=point(tg,0); + random=abs(random); + int rsgn(real x){ + int d2=floor(100*x)-10*floor(10*x); + if(d2 == 0) return 1; + return 2 % d2 == 0 ? 1 : -1; + } + real randf() + { + real or; + if(random != 0) { + if(1 % tf != 0) or=0; + else { + real ur=unitrand(); + or=rsgn(ur)*angle*(1+ur^(1/random)); + } + } else or=rsgn(unitrand())*1.5*angle; + return or; + } + + real first=randf(); + for(int i=1; i <= l; ++i) { + pair P=point(tg,i); + real a=randf(); + pair post=rotate(a,point(tg,i-1))*postcontrol(tg,i-1); + pair pre=rotate((a+randf())/2,P)*precontrol(tg,i); + if(i == l && (cyclic(tg))) + og=og..controls post and pre..cycle; + else + og=og..controls post and pre..P; + } + return og; + } +} diff --git a/Build/source/utils/asymptote/base/tube.asy b/Build/source/utils/asymptote/base/tube.asy new file mode 100644 index 00000000000..756eeabdda8 --- /dev/null +++ b/Build/source/utils/asymptote/base/tube.asy @@ -0,0 +1,189 @@ +// Author: Philippe Ivaldi +// http://www.piprime.fr/ +// Based on this paper: +// http://www.cs.hku.hk/research/techreps/document/TR-2007-07.pdf +// Note: the additional rotation for a cyclic smooth spine curve is not +// yet properly determined. +// TODO: Implement variational principles for RMF with boundary conditions: +// minimum total angular speed OR minimum total squared angular speed + +import three; + +real tubegranularity=1e-7; + +void render(path3 s, real r, void f(path3, real)) +{ + void Split(triple z0, triple c0, triple c1, triple z1, real t0=0, real t1=1, + int depth=mantissaBits) { + if(depth > 0) { + real S=straightness(z0,c0,c1,z1); + if(S > max(tubegranularity*max(abs(z0),abs(c0),abs(c1),abs(z1),r))) { + --depth; + 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)); +} + +// A 3D version of roundedpath(path, real). +path3 roundedpath(path3 A, real r) +{ + // Author of this routine: Jens Schwaiger + guide3 rounded; + triple before, after, indir, outdir; + int len=length(A); + bool cyclic=cyclic(A); + if(len < 2) {return A;}; + if(cyclic) {rounded=point(point(A,0)--point(A,1),r);} + else {rounded=point(A,0);} + for(int i=1; i < len; i=i+1) { + before=point(point(A,i)--point(A,i-1),r); + after=point(point(A,i)--point(A,i+1),r); + indir=dir(point(A,i-1)--point(A,i),1); + outdir=dir(point(A,i)--point(A,i+1),1); + rounded=rounded--before{indir}..{outdir}after; + } + if(cyclic) { + before=point(point(A,0)--point(A,len-1),r); + indir=dir(point(A,len-1)--point(A,0),1); + outdir=dir(point(A,0)--point(A,1),1); + rounded=rounded--before{indir}..{outdir}cycle; + } else rounded=rounded--point(A,len); + + return rounded; +} + +real[] sample(path3 g, real r, real relstep=0) +{ + real[] t; + int n=length(g); + if(relstep <= 0) { + for(int i=0; i < n; ++i) + render(subpath(g,i,i+1),r,new void(path3, real s) {t.push(i+s);}); + t.push(n); + } else { + int nb=ceil(1/relstep); + relstep=n/nb; + for(int i=0; i <= nb; ++i) + t.push(i*relstep); + } + return t; +} + +real degrees(rmf a, rmf b) +{ + real d=degrees(acos1(dot(a.r,b.r))); + real dt=dot(cross(a.r,b.r),a.t); + d=dt > 0 ? d : 360-d; + return d%360; +} + +restricted int coloredNodes=1; +restricted int coloredSegments=2; + +struct coloredpath +{ + path p; + pen[] pens(real); + bool usepens=false; + int colortype=coloredSegments; + + void operator init(path p, pen[] pens=new pen[] {currentpen}, + int colortype=coloredSegments) + { + this.p=p; + this.pens=new pen[] (real t) {return pens;}; + this.usepens=true; + this.colortype=colortype; + } + + void operator init(path p, pen[] pens(real), int colortype=coloredSegments) + { + this.p=p; + this.pens=pens; + this.usepens=true; + this.colortype=colortype; + } + + void operator init(path p, pen pen(real)) + { + this.p=p; + this.pens=new pen[] (real t) {return new pen[] {pen(t)};}; + this.usepens=true; + this.colortype=coloredSegments; + } +} + +coloredpath operator cast(path p) +{ + coloredpath cp=coloredpath(p); + cp.usepens=false; + return cp; +} + +coloredpath operator cast(guide p) +{ + return coloredpath(p); +} + +private surface surface(rmf[] R, real[] t, coloredpath cp, transform T(real), + bool cyclic) +{ + path g=cp.p; + int l=length(g); + bool[] planar; + for(int i=0; i < l; ++i) + planar[i]=straight(g,i); + + surface s; + path3 sec=path3(T(t[0]/l)*g); + real adjust=0; + if(cyclic) adjust=-degrees(R[0],R[R.length-1])/(R.length-1); + path3 sec1=shift(R[0].p)*transform3(R[0].r,R[0].s,R[0].t)*sec, + sec2; + + for(int i=1; i < R.length; ++i) { + sec=path3(T(t[i]/l)*g); + sec2=shift(R[i].p)*transform3(R[i].r,cross(R[i].t,R[i].r),R[i].t)* + rotate(i*adjust,Z)*sec; + for(int j=0; j < l; ++j) { + surface st=surface(subpath(sec1,j,j+1)--subpath(sec2,j+1,j)--cycle, + planar=planar[j]); + if(cp.usepens) { + pen[] tp1=cp.pens(t[i-1]/l), tp2=cp.pens(t[i]/l); + tp1.cyclic=true; tp2.cyclic=true; + if(cp.colortype == coloredSegments) { + st.colors(new pen[][] {{tp1[j],tp1[j],tp2[j],tp2[j]}}); + } else { + st.colors(new pen[][] {{tp1[j],tp1[j+1],tp2[j+1],tp2[j]}}); + } + } + s.append(st); + } + sec1=sec2; + } + return s; +} + +surface tube(path3 g, coloredpath section, + transform T(real)=new transform(real t) {return identity();}, + real corner=1, real relstep=0) +{ + pair M=max(section.p), m=min(section.p); + real[] t=sample(g,max(M.x-m.x,M.y-m.y)/max(realEpsilon,abs(corner)), + min(abs(relstep),1)); + bool cyclic=cyclic(g); + t.cyclic=cyclic; + return surface(rmf(g,t),t,section,T,cyclic); +} diff --git a/Build/source/utils/asymptote/base/webgl/asygl.js b/Build/source/utils/asymptote/base/webgl/asygl.js new file mode 100644 index 00000000000..db8810f260f --- /dev/null +++ b/Build/source/utils/asymptote/base/webgl/asygl.js @@ -0,0 +1,39 @@ +/*@license + AsyGL: Render Bezier patches and triangles via subdivision with WebGL. + Copyright 2019-2020: John C. Bowman and Supakorn "Jamie" Rassameemasmuang + University of Alberta + +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, see <http://www.gnu.org/licenses/>. +*/ +/*@license for gl-matrix mat3 and mat4 functions: +Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.*/ +let vertex="\nattribute vec3 position;\n#ifdef WIDTH\nattribute float width;\n#endif\n#ifdef NORMAL\nattribute vec3 normal;\n#endif\nattribute float materialIndex;\n#ifdef COLOR\nattribute vec4 color;\n#endif\n\nuniform mat3 normMat;\nuniform mat4 viewMat;\nuniform mat4 projViewMat;\n\n#ifdef NORMAL\n#ifndef ORTHOGRAPHIC\nvarying vec3 ViewPosition;\n#endif\nvarying vec3 Normal;\n#endif\nvarying vec4 diffuse;\nvarying vec3 specular;\nvarying float roughness,metallic,fresnel0;\nvarying vec4 emissive;\n\nstruct Material {\n vec4 diffuse,emissive,specular;\n vec4 parameters;\n};\n\nuniform Material Materials[Nmaterials];\n\nvoid main(void)\n{\n vec4 v=vec4(position,1.0);\n gl_Position=projViewMat*v;\n#ifdef NORMAL\n#ifndef ORTHOGRAPHIC\n ViewPosition=(viewMat*v).xyz;\n#endif \n Normal=normalize(normal*normMat);\n \n Material m;\n#ifdef TRANSPARENT\n m=Materials[int(abs(materialIndex))-1];\n emissive=m.emissive;\n if(materialIndex >= 0.0) {\n diffuse=m.diffuse;\n } else {\n diffuse=color;\n#if nlights == 0\n emissive += color;\n#endif\n }\n#else\n m=Materials[int(materialIndex)];\n emissive=m.emissive;\n#ifdef COLOR\n diffuse=color;\n#if nlights == 0\n emissive += color;\n#endif\n#else\n diffuse=m.diffuse;\n#endif\n#endif\n specular=m.specular.rgb;\n vec4 parameters=m.parameters;\n roughness=1.0-parameters[0];\n metallic=parameters[1];\n fresnel0=parameters[2];\n#else\n emissive=Materials[int(materialIndex)].emissive;\n#endif\n#ifdef WIDTH\n gl_PointSize=width;\n#endif\n}\n",fragment="\n#ifdef NORMAL\n#ifndef ORTHOGRAPHIC\nvarying vec3 ViewPosition;\n#endif\nvarying vec3 Normal;\nvarying vec4 diffuse;\nvarying vec3 specular;\nvarying float roughness,metallic,fresnel0;\n\nfloat Roughness2;\nvec3 normal;\n\nstruct Light {\n vec3 direction;\n vec3 color;\n};\n\nuniform Light Lights[Nlights];\n\nfloat NDF_TRG(vec3 h)\n{\n float ndoth=max(dot(normal,h),0.0);\n float alpha2=Roughness2*Roughness2;\n float denom=ndoth*ndoth*(alpha2-1.0)+1.0;\n return denom != 0.0 ? alpha2/(denom*denom) : 0.0;\n}\n \nfloat GGX_Geom(vec3 v)\n{\n float ndotv=max(dot(v,normal),0.0);\n float ap=1.0+Roughness2;\n float k=0.125*ap*ap;\n return ndotv/((ndotv*(1.0-k))+k);\n}\n \nfloat Geom(vec3 v, vec3 l)\n{\n return GGX_Geom(v)*GGX_Geom(l);\n}\n \nfloat Fresnel(vec3 h, vec3 v, float fresnel0)\n{\n float a=1.0-max(dot(h,v),0.0);\n float b=a*a;\n return fresnel0+(1.0-fresnel0)*b*b*a;\n}\n \n// physical based shading using UE4 model.\nvec3 BRDF(vec3 viewDirection, vec3 lightDirection)\n{\n vec3 lambertian=diffuse.rgb;\n vec3 h=normalize(lightDirection+viewDirection);\n \n float omegain=max(dot(viewDirection,normal),0.0);\n float omegali=max(dot(lightDirection,normal),0.0);\n \n float D=NDF_TRG(h);\n float G=Geom(viewDirection,lightDirection);\n float F=Fresnel(h,viewDirection,fresnel0);\n \n float denom=4.0*omegain*omegali;\n float rawReflectance=denom > 0.0 ? (D*G)/denom : 0.0;\n \n vec3 dielectric=mix(lambertian,rawReflectance*specular,F);\n vec3 metal=rawReflectance*diffuse.rgb;\n \n return mix(dielectric,metal,metallic);\n}\n#endif\nvarying vec4 emissive;\n \nvoid main(void)\n{\n#if defined(NORMAL) && nlights > 0\n normal=normalize(Normal);\n normal=gl_FrontFacing ? normal : -normal;\n#ifdef ORTHOGRAPHIC\n vec3 viewDir=vec3(0.0,0.0,1.0);\n#else\n vec3 viewDir=-normalize(ViewPosition);\n#endif\n Roughness2=roughness*roughness;\n vec3 color=emissive.rgb;\n for(int i=0; i < nlights; ++i) {\n Light Li=Lights[i];\n vec3 L=Li.direction;\n float cosTheta=max(dot(normal,L),0.0);\n vec3 radiance=cosTheta*Li.color;\n color += BRDF(viewDir,L)*radiance;\n }\n gl_FragColor=vec4(color,diffuse.a);\n#else\n gl_FragColor=emissive;\n#endif\n}\n";!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var i=e();for(var a in i)("object"==typeof exports?exports:t)[a]=i[a]}}("undefined"!=typeof self?self:this,(function(){return function(t){var e={};function i(a){if(e[a])return e[a].exports;var r=e[a]={i:a,l:!1,exports:{}};return t[a].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=e,i.d=function(t,e,a){i.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:a})},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=1)}([function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.setMatrixArrayType=function(t){e.ARRAY_TYPE=t},e.toRadian=function(t){return t*r},e.equals=function(t,e){return Math.abs(t-e)<=a*Math.max(1,Math.abs(t),Math.abs(e))};var a=e.EPSILON=1e-6;e.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,e.RANDOM=Math.random;var r=Math.PI/180},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.mat4=e.mat3=void 0;var a=n(i(2)),r=n(i(3));function n(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}e.mat3=a,e.mat4=r},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.create=function(){var t=new a.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat4=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[4],t[4]=e[5],t[5]=e[6],t[6]=e[8],t[7]=e[9],t[8]=e[10],t},e.invert=function(t,e){var i=e[0],a=e[1],r=e[2],n=e[3],s=e[4],o=e[5],h=e[6],l=e[7],c=e[8],d=c*s-o*l,m=-c*n+o*h,f=l*n-s*h,u=i*d+a*m+r*f;if(!u)return null;return u=1/u,t[0]=d*u,t[1]=(-c*a+r*l)*u,t[2]=(o*a-r*s)*u,t[3]=m*u,t[4]=(c*i-r*h)*u,t[5]=(-o*i+r*n)*u,t[6]=f*u,t[7]=(-l*i+a*h)*u,t[8]=(s*i-a*n)*u,t};var a=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}(i(0))},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.create=function(){var t=new a.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.invert=function(t,e){var i=e[0],a=e[1],r=e[2],n=e[3],s=e[4],o=e[5],h=e[6],l=e[7],c=e[8],d=e[9],m=e[10],f=e[11],u=e[12],p=e[13],v=e[14],g=e[15],x=i*o-a*s,w=i*h-r*s,M=i*l-n*s,b=a*h-r*o,S=a*l-n*o,P=r*l-n*h,A=c*p-d*u,y=c*v-m*u,T=c*g-f*u,R=d*v-m*p,D=d*g-f*p,I=m*g-f*v,z=x*I-w*D+M*R+b*T-S*y+P*A;if(!z)return null;return z=1/z,t[0]=(o*I-h*D+l*R)*z,t[1]=(r*D-a*I-n*R)*z,t[2]=(p*P-v*S+g*b)*z,t[3]=(m*S-d*P-f*b)*z,t[4]=(h*T-s*I-l*y)*z,t[5]=(i*I-r*T+n*y)*z,t[6]=(v*M-u*P-g*w)*z,t[7]=(c*P-m*M+f*w)*z,t[8]=(s*D-o*T+l*A)*z,t[9]=(a*T-i*D-n*A)*z,t[10]=(u*S-p*M+g*x)*z,t[11]=(d*M-c*S-f*x)*z,t[12]=(o*y-s*R-h*A)*z,t[13]=(i*R-a*y+r*A)*z,t[14]=(p*w-u*b-v*x)*z,t[15]=(c*b-d*w+m*x)*z,t},e.multiply=r,e.translate=function(t,e,i){var a=i[0],r=i[1],n=i[2],s=void 0,o=void 0,h=void 0,l=void 0,c=void 0,d=void 0,m=void 0,f=void 0,u=void 0,p=void 0,v=void 0,g=void 0;e===t?(t[12]=e[0]*a+e[4]*r+e[8]*n+e[12],t[13]=e[1]*a+e[5]*r+e[9]*n+e[13],t[14]=e[2]*a+e[6]*r+e[10]*n+e[14],t[15]=e[3]*a+e[7]*r+e[11]*n+e[15]):(s=e[0],o=e[1],h=e[2],l=e[3],c=e[4],d=e[5],m=e[6],f=e[7],u=e[8],p=e[9],v=e[10],g=e[11],t[0]=s,t[1]=o,t[2]=h,t[3]=l,t[4]=c,t[5]=d,t[6]=m,t[7]=f,t[8]=u,t[9]=p,t[10]=v,t[11]=g,t[12]=s*a+c*r+u*n+e[12],t[13]=o*a+d*r+p*n+e[13],t[14]=h*a+m*r+v*n+e[14],t[15]=l*a+f*r+g*n+e[15]);return t},e.rotate=function(t,e,i,r){var n,s,o,h,l,c,d,m,f,u,p,v,g,x,w,M,b,S,P,A,y,T,R,D,I=r[0],z=r[1],L=r[2],N=Math.sqrt(I*I+z*z+L*L);if(Math.abs(N)<a.EPSILON)return null;I*=N=1/N,z*=N,L*=N,n=Math.sin(i),s=Math.cos(i),o=1-s,h=e[0],l=e[1],c=e[2],d=e[3],m=e[4],f=e[5],u=e[6],p=e[7],v=e[8],g=e[9],x=e[10],w=e[11],M=I*I*o+s,b=z*I*o+L*n,S=L*I*o-z*n,P=I*z*o-L*n,A=z*z*o+s,y=L*z*o+I*n,T=I*L*o+z*n,R=z*L*o-I*n,D=L*L*o+s,t[0]=h*M+m*b+v*S,t[1]=l*M+f*b+g*S,t[2]=c*M+u*b+x*S,t[3]=d*M+p*b+w*S,t[4]=h*P+m*A+v*y,t[5]=l*P+f*A+g*y,t[6]=c*P+u*A+x*y,t[7]=d*P+p*A+w*y,t[8]=h*T+m*R+v*D,t[9]=l*T+f*R+g*D,t[10]=c*T+u*R+x*D,t[11]=d*T+p*R+w*D,e!==t&&(t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]);return t},e.fromTranslation=function(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=e[0],t[13]=e[1],t[14]=e[2],t[15]=1,t},e.fromRotation=function(t,e,i){var r,n,s,o=i[0],h=i[1],l=i[2],c=Math.sqrt(o*o+h*h+l*l);if(Math.abs(c)<a.EPSILON)return null;return o*=c=1/c,h*=c,l*=c,r=Math.sin(e),n=Math.cos(e),s=1-n,t[0]=o*o*s+n,t[1]=h*o*s+l*r,t[2]=l*o*s-h*r,t[3]=0,t[4]=o*h*s-l*r,t[5]=h*h*s+n,t[6]=l*h*s+o*r,t[7]=0,t[8]=o*l*s+h*r,t[9]=h*l*s-o*r,t[10]=l*l*s+n,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.frustum=function(t,e,i,a,r,n,s){var o=1/(i-e),h=1/(r-a),l=1/(n-s);return t[0]=2*n*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*n*h,t[6]=0,t[7]=0,t[8]=(i+e)*o,t[9]=(r+a)*h,t[10]=(s+n)*l,t[11]=-1,t[12]=0,t[13]=0,t[14]=s*n*2*l,t[15]=0,t},e.ortho=function(t,e,i,a,r,n,s){var o=1/(e-i),h=1/(a-r),l=1/(n-s);return t[0]=-2*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*h,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*l,t[11]=0,t[12]=(e+i)*o,t[13]=(r+a)*h,t[14]=(s+n)*l,t[15]=1,t};var a=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}(i(0));function r(t,e,i){var a=e[0],r=e[1],n=e[2],s=e[3],o=e[4],h=e[5],l=e[6],c=e[7],d=e[8],m=e[9],f=e[10],u=e[11],p=e[12],v=e[13],g=e[14],x=e[15],w=i[0],M=i[1],b=i[2],S=i[3];return t[0]=w*a+M*o+b*d+S*p,t[1]=w*r+M*h+b*m+S*v,t[2]=w*n+M*l+b*f+S*g,t[3]=w*s+M*c+b*u+S*x,w=i[4],M=i[5],b=i[6],S=i[7],t[4]=w*a+M*o+b*d+S*p,t[5]=w*r+M*h+b*m+S*v,t[6]=w*n+M*l+b*f+S*g,t[7]=w*s+M*c+b*u+S*x,w=i[8],M=i[9],b=i[10],S=i[11],t[8]=w*a+M*o+b*d+S*p,t[9]=w*r+M*h+b*m+S*v,t[10]=w*n+M*l+b*f+S*g,t[11]=w*s+M*c+b*u+S*x,w=i[12],M=i[13],b=i[14],S=i[15],t[12]=w*a+M*o+b*d+S*p,t[13]=w*r+M*h+b*m+S*v,t[14]=w*n+M*l+b*f+S*g,t[15]=w*s+M*c+b*u+S*x,t}}])}));let canvasWidth,canvasHeight,canvasWidth0,canvasHeight0,b,B,angle,Zoom0,zoom0,viewportmargin,zoomFactor,zoomPinchFactor,zoomPinchCap,zoomStep,shiftHoldDistance,shiftWaitTime,vibrateTime,embedded,canvas,gl,alpha,offscreen,context,maxMaterials,halfCanvasWidth,halfCanvasHeight,Zoom,maxViewportWidth,maxViewportHeight,P=[],Materials=[],Lights=[],Centers=[],Background=[1,1,1,1],absolute=!1,viewportshift=[0,0],nlights=0,Nmaterials=2,materials=[],pixel=.75,zoomRemeshFactor=1.5,FillFactor=.1;const windowTrim=10;let lastZoom,H,zmin,zmax,size2,ArcballFactor,third=1/3,rotMat=mat4.create(),projMat=mat4.create(),viewMat=mat4.create(),projViewMat=mat4.create(),normMat=mat3.create(),viewMat3=mat3.create(),cjMatInv=mat4.create(),T=mat4.create(),center={x:0,y:0,z:0},shift={x:0,y:0},viewParam={xmin:0,xmax:0,ymin:0,ymax:0,zmin:0,zmax:0},remesh=!0,wireframe=0,mouseDownOrTouchActive=!1,lastMouseX=null,lastMouseY=null,touchID=null,Positions=[],Normals=[],Colors=[],Indices=[];class Material{constructor(t,e,i,a,r,n){this.diffuse=t,this.emissive=e,this.specular=i,this.shininess=a,this.metallic=r,this.fresnel0=n}setUniform(t,e){let i=i=>gl.getUniformLocation(t,"Materials["+e+"]."+i);gl.uniform4fv(i("diffuse"),new Float32Array(this.diffuse)),gl.uniform4fv(i("emissive"),new Float32Array(this.emissive)),gl.uniform4fv(i("specular"),new Float32Array(this.specular)),gl.uniform4f(i("parameters"),this.shininess,this.metallic,this.fresnel0,0)}}let indexExt,TRIANGLES,material0Data,material1Data,materialData,colorData,transparentData,triangleData,materialIndex,enumPointLight=1,enumDirectionalLight=2;class Light{constructor(t,e){this.direction=t,this.color=e}setUniform(t,e){let i=i=>gl.getUniformLocation(t,"Lights["+e+"]."+i);gl.uniform3fv(i("direction"),new Float32Array(this.direction)),gl.uniform3fv(i("color"),new Float32Array(this.color))}}function initShaders(){let t=gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);maxMaterials=Math.floor((t-14)/4),Nmaterials=Math.min(Math.max(Nmaterials,Materials.length),maxMaterials),pixelShader=initShader(["WIDTH"]),materialShader=initShader(["NORMAL"]),colorShader=initShader(["NORMAL","COLOR"]),transparentShader=initShader(["NORMAL","COLOR","TRANSPARENT"])}function deleteShaders(){gl.deleteProgram(transparentShader),gl.deleteProgram(colorShader),gl.deleteProgram(materialShader),gl.deleteProgram(pixelShader)}function noGL(){gl||alert("Could not initialize WebGL")}function saveAttributes(){let t=window.top.document.asygl[alpha];t.gl=gl,t.nlights=Lights.length,t.Nmaterials=Nmaterials,t.maxMaterials=maxMaterials,t.pixelShader=pixelShader,t.materialShader=materialShader,t.colorShader=colorShader,t.transparentShader=transparentShader}function restoreAttributes(){let t=window.top.document.asygl[alpha];gl=t.gl,nlights=t.nlights,Nmaterials=t.Nmaterials,maxMaterials=t.maxMaterials,pixelShader=t.pixelShader,materialShader=t.materialShader,colorShader=t.colorShader,transparentShader=t.transparentShader}function initGL(){if(alpha=Background[3]<1,embedded){let t=window.top.document;null==t.asygl&&(t.asygl=Array(2)),context=canvas.getContext("2d"),offscreen=t.offscreen,offscreen||(offscreen=t.createElement("canvas"),t.offscreen=offscreen),t.asygl[alpha]&&t.asygl[alpha].gl?(restoreAttributes(),(Lights.length!=nlights||Math.min(Materials.length,maxMaterials)>Nmaterials)&&(initShaders(),saveAttributes())):(gl=offscreen.getContext("webgl",{alpha:alpha}),gl||noGL(),initShaders(),t.asygl[alpha]={},saveAttributes())}else gl=canvas.getContext("webgl",{alpha:alpha}),gl||noGL(),initShaders();indexExt=gl.getExtension("OES_element_index_uint"),TRIANGLES=gl.TRIANGLES,material0Data=new vertexBuffer(gl.POINTS),material1Data=new vertexBuffer(gl.LINES),materialData=new vertexBuffer,colorData=new vertexBuffer,transparentData=new vertexBuffer,triangleData=new vertexBuffer}function getShader(t,e,i,a=[]){let r=`#version 100\n#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n#else\n precision mediump float;\n#endif\n #define nlights ${0==wireframe?Lights.length:0}\n\n const int Nlights=${Math.max(Lights.length,1)};\n\n #define Nmaterials ${Nmaterials}\n`;orthographic&&(r+="#define ORTHOGRAPHIC\n"),a.forEach(t=>r+="#define "+t+"\n");let n=t.createShader(i);return t.shaderSource(n,r+e),t.compileShader(n),t.getShaderParameter(n,t.COMPILE_STATUS)?n:(alert(t.getShaderInfoLog(n)),null)}function registerBuffer(t,e,i,a=gl.ARRAY_BUFFER){return t.length>0&&(0==e&&(e=gl.createBuffer(),i=!0),gl.bindBuffer(a,e),i&&gl.bufferData(a,t,gl.STATIC_DRAW)),e}function drawBuffer(t,e,i=t.indices){if(0==t.indices.length)return;let a=e!=pixelShader;setUniforms(t,e);let r=remesh||t.partial||!t.rendered;t.verticesBuffer=registerBuffer(new Float32Array(t.vertices),t.verticesBuffer,r),gl.vertexAttribPointer(positionAttribute,3,gl.FLOAT,!1,a?24:16,0),a&&Lights.length>0?gl.vertexAttribPointer(normalAttribute,3,gl.FLOAT,!1,24,12):pixel&&gl.vertexAttribPointer(widthAttribute,1,gl.FLOAT,!1,16,12),t.materialsBuffer=registerBuffer(new Int16Array(t.materialIndices),t.materialsBuffer,r),gl.vertexAttribPointer(materialAttribute,1,gl.SHORT,!1,2,0),e!=colorShader&&e!=transparentShader||(t.colorsBuffer=registerBuffer(new Uint8Array(t.colors),t.colorsBuffer,r),gl.vertexAttribPointer(colorAttribute,4,gl.UNSIGNED_BYTE,!0,0,0)),t.indicesBuffer=registerBuffer(indexExt?new Uint32Array(i):new Uint16Array(i),t.indicesBuffer,r,gl.ELEMENT_ARRAY_BUFFER),t.rendered=!0,gl.drawElements(a?wireframe?gl.LINES:t.type:gl.POINTS,i.length,indexExt?gl.UNSIGNED_INT:gl.UNSIGNED_SHORT,0)}class vertexBuffer{constructor(t){this.type=t||TRIANGLES,this.verticesBuffer=0,this.materialsBuffer=0,this.colorsBuffer=0,this.indicesBuffer=0,this.rendered=!1,this.partial=!1,this.clear()}clear(){this.vertices=[],this.materialIndices=[],this.colors=[],this.indices=[],this.nvertices=0,this.materials=[],this.materialTable=[]}vertex(t,e){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.vertices.push(e[0]),this.vertices.push(e[1]),this.vertices.push(e[2]),this.materialIndices.push(materialIndex),this.nvertices++}Vertex(t,e,i=[0,0,0,0]){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.vertices.push(e[0]),this.vertices.push(e[1]),this.vertices.push(e[2]),this.materialIndices.push(materialIndex),this.colors.push(i[0]),this.colors.push(i[1]),this.colors.push(i[2]),this.colors.push(i[3]),this.nvertices++}vertex0(t,e){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.vertices.push(e),this.materialIndices.push(materialIndex),this.nvertices++}iVertex(t,e,i,a=[0,0,0,0]){let r=6*t;this.vertices[r]=e[0],this.vertices[r+1]=e[1],this.vertices[r+2]=e[2],this.vertices[r+3]=i[0],this.vertices[r+4]=i[1],this.vertices[r+5]=i[2],this.materialIndices[t]=materialIndex;let n=4*t;this.colors[n]=a[0],this.colors[n+1]=a[1],this.colors[n+2]=a[2],this.colors[n+3]=a[3],this.indices.push(t)}append(t){append(this.vertices,t.vertices),append(this.materialIndices,t.materialIndices),append(this.colors,t.colors),appendOffset(this.indices,t.indices,this.nvertices),this.nvertices+=t.nvertices}}function append(t,e){let i=t.length,a=e.length;t.length+=a;for(let r=0;r<a;++r)t[i+r]=e[r]}function appendOffset(t,e,i){let a=t.length,r=e.length;t.length+=e.length;for(let n=0;n<r;++n)t[a+n]=e[n]+i}class Geometry{constructor(){this.data=new vertexBuffer,this.Onscreen=!1,this.m=[]}offscreen(t){let e=projViewMat,i=t[0],a=i[0],r=i[1],n=i[2],s=1/(e[3]*a+e[7]*r+e[11]*n+e[15]);this.x=this.X=(e[0]*a+e[4]*r+e[8]*n+e[12])*s,this.y=this.Y=(e[1]*a+e[5]*r+e[9]*n+e[13])*s;for(let i=1,a=t.length;i<a;++i){let a=t[i],r=a[0],n=a[1],s=a[2],o=1/(e[3]*r+e[7]*n+e[11]*s+e[15]),h=(e[0]*r+e[4]*n+e[8]*s+e[12])*o,l=(e[1]*r+e[5]*n+e[9]*s+e[13])*o;h<this.x?this.x=h:h>this.X&&(this.X=h),l<this.y?this.y=l:l>this.Y&&(this.Y=l)}return(this.X<-1.01||this.x>1.01||this.Y<-1.01||this.y>1.01)&&(this.Onscreen=!1,!0)}T(t){let e=this.c[0],i=this.c[1],a=this.c[2],r=t[0]-e,n=t[1]-i,s=t[2]-a;return[r*normMat[0]+n*normMat[3]+s*normMat[6]+e,r*normMat[1]+n*normMat[4]+s*normMat[7]+i,r*normMat[2]+n*normMat[5]+s*normMat[8]+a]}Tcorners(t,e){return[this.T(t),this.T([t[0],t[1],e[2]]),this.T([t[0],e[1],t[2]]),this.T([t[0],e[1],e[2]]),this.T([e[0],t[1],t[2]]),this.T([e[0],t[1],e[2]]),this.T([e[0],e[1],t[2]]),this.T(e)]}setMaterial(t,e){null==t.materialTable[this.MaterialIndex]&&(t.materials.length>=Nmaterials&&(t.partial=!0,e()),t.materialTable[this.MaterialIndex]=t.materials.length,t.materials.push(Materials[this.MaterialIndex])),materialIndex=t.materialTable[this.MaterialIndex]}render(){let t;if(this.setMaterialIndex(),0==this.CenterIndex?t=corners(this.Min,this.Max):(this.c=Centers[this.CenterIndex-1],t=this.Tcorners(this.Min,this.Max)),this.offscreen(t))return this.data.clear(),void this.notRendered();let e,i=this.controlpoints;if(0==this.CenterIndex){if(!remesh&&this.Onscreen)return void this.append();e=i}else{let t=i.length;e=Array(t);for(let a=0;a<t;++a)e[a]=this.T(i[a])}let a=orthographic?1:this.Min[2]/B[2],r=pixel*Math.hypot(a*(viewParam.xmax-viewParam.xmin),a*(viewParam.ymax-viewParam.ymin))/size2;this.res2=r*r,this.Epsilon=FillFactor*r,this.data.clear(),this.notRendered(),this.Onscreen=!0,this.process(e)}}class BezierPatch extends Geometry{constructor(t,e,i,a,r,n){super(),this.controlpoints=t,this.Min=a,this.Max=r,this.color=n,this.CenterIndex=e;let s=t.length;if(n){let t=n[0][3]+n[1][3]+n[2][3];this.transparent=16==s||4==s?t+n[3][3]<1020:t<765}else this.transparent=Materials[i].diffuse[3]<1;this.MaterialIndex=i,this.vertex=this.transparent?this.data.Vertex.bind(this.data):this.data.vertex.bind(this.data),this.L2norm(this.controlpoints)}setMaterialIndex(){this.transparent?this.setMaterial(transparentData,drawTransparent):this.color?this.setMaterial(colorData,drawColor):this.setMaterial(materialData,drawMaterial)}L2norm(t){let e=t[0];this.epsilon=0;let i=t.length;for(let a=1;a<i;++a)this.epsilon=Math.max(this.epsilon,abs2([t[a][0]-e[0],t[a][1]-e[1],t[a][2]-e[2]]));this.epsilon*=Number.EPSILON}processTriangle(t){let e=t[0],i=t[1],a=t[2],r=unit(cross([i[0]-e[0],i[1]-e[1],i[2]-e[2]],[a[0]-e[0],a[1]-e[1],a[2]-e[2]]));if(!this.offscreen([e,i,a])){let t,n,s;this.color?(t=this.data.Vertex(e,r,this.color[0]),n=this.data.Vertex(i,r,this.color[1]),s=this.data.Vertex(a,r,this.color[2])):(t=this.vertex(e,r),n=this.vertex(i,r),s=this.vertex(a,r)),0==wireframe?(this.data.indices.push(t),this.data.indices.push(n),this.data.indices.push(s)):(this.data.indices.push(t),this.data.indices.push(n),this.data.indices.push(n),this.data.indices.push(s),this.data.indices.push(s),this.data.indices.push(t)),this.append()}}processQuad(t){let e=t[0],i=t[1],a=t[2],r=t[3],n=cross([i[0]-e[0],i[1]-e[1],i[2]-e[2]],[a[0]-i[0],a[1]-i[1],a[2]-i[2]]),s=cross([a[0]-r[0],a[1]-r[1],a[2]-r[2]],[r[0]-e[0],r[1]-e[1],r[2]-e[2]]),o=unit([n[0]+s[0],n[1]+s[1],n[2]+s[2]]);if(!this.offscreen([e,i,a,r])){let t,n,s,h;this.color?(t=this.data.Vertex(e,o,this.color[0]),n=this.data.Vertex(i,o,this.color[1]),s=this.data.Vertex(a,o,this.color[2]),h=this.data.Vertex(r,o,this.color[3])):(t=this.vertex(e,o),n=this.vertex(i,o),s=this.vertex(a,o),h=this.vertex(r,o)),0==wireframe?(this.data.indices.push(t),this.data.indices.push(n),this.data.indices.push(s),this.data.indices.push(t),this.data.indices.push(s),this.data.indices.push(h)):(this.data.indices.push(t),this.data.indices.push(n),this.data.indices.push(n),this.data.indices.push(s),this.data.indices.push(s),this.data.indices.push(h),this.data.indices.push(h),this.data.indices.push(t)),this.append()}}curve(t,e,i,a,r){new BezierCurve([t[e],t[i],t[a],t[r]],0,materialIndex,this.Min,this.Max).render()}process(t){if(this.transparent&&1!=wireframe&&(materialIndex=this.color?-1-materialIndex:1+materialIndex),10==t.length)return this.process3(t);if(3==t.length)return this.processTriangle(t);if(4==t.length)return this.processQuad(t);if(1==wireframe)return this.curve(t,0,4,8,12),this.curve(t,12,13,14,15),this.curve(t,15,11,7,3),void this.curve(t,3,2,1,0);let e=t[0],i=t[3],a=t[12],r=t[15],n=this.normal(i,t[2],t[1],e,t[4],t[8],a);abs2(n)<this.epsilon&&(n=this.normal(i,t[2],t[1],e,t[13],t[14],r),abs2(n)<this.epsilon&&(n=this.normal(r,t[11],t[7],i,t[4],t[8],a)));let s=this.normal(e,t[4],t[8],a,t[13],t[14],r);abs2(s)<this.epsilon&&(s=this.normal(e,t[4],t[8],a,t[11],t[7],i),abs2(s)<this.epsilon&&(s=this.normal(i,t[2],t[1],e,t[13],t[14],r)));let o=this.normal(a,t[13],t[14],r,t[11],t[7],i);abs2(o)<this.epsilon&&(o=this.normal(a,t[13],t[14],r,t[2],t[1],e),abs2(o)<this.epsilon&&(o=this.normal(e,t[4],t[8],a,t[11],t[7],i)));let h=this.normal(r,t[11],t[7],i,t[2],t[1],e);if(abs2(h)<this.epsilon&&(h=this.normal(r,t[11],t[7],i,t[4],t[8],a),abs2(h)<this.epsilon&&(h=this.normal(a,t[13],t[14],r,t[2],t[1],e))),this.color){let l=this.color[0],c=this.color[1],d=this.color[2],m=this.color[3],f=this.data.Vertex(e,n,l),u=this.data.Vertex(a,s,c),p=this.data.Vertex(r,o,d),v=this.data.Vertex(i,h,m);this.Render(t,f,u,p,v,e,a,r,i,!1,!1,!1,!1,l,c,d,m)}else{let l=this.vertex(e,n),c=this.vertex(a,s),d=this.vertex(r,o),m=this.vertex(i,h);this.Render(t,l,c,d,m,e,a,r,i,!1,!1,!1,!1)}this.data.indices.length>0&&this.append()}append(){this.transparent?transparentData.append(this.data):this.color?colorData.append(this.data):materialData.append(this.data)}notRendered(){this.transparent?transparentData.rendered=!1:this.color?colorData.rendered=!1:materialData.rendered=!1}Render(t,e,i,a,r,n,s,o,h,l,c,d,m,f,u,p,v){let g=this.Distance(t);if(g[0]<this.res2&&g[1]<this.res2)this.offscreen([n,s,o])||(0==wireframe?(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(a)):(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(i),this.data.indices.push(a))),this.offscreen([n,o,h])||(0==wireframe?(this.data.indices.push(e),this.data.indices.push(a),this.data.indices.push(r)):(this.data.indices.push(a),this.data.indices.push(r),this.data.indices.push(r),this.data.indices.push(e)));else{if(this.offscreen(t))return;let x=t[0],w=t[3],M=t[12],b=t[15];if(g[0]<this.res2){let g=new Split3(x,t[1],t[2],w),S=new Split3(t[4],t[5],t[6],t[7]),P=new Split3(t[8],t[9],t[10],t[11]),A=new Split3(M,t[13],t[14],b),y=[x,g.m0,g.m3,g.m5,t[4],S.m0,S.m3,S.m5,t[8],P.m0,P.m3,P.m5,M,A.m0,A.m3,A.m5],T=[g.m5,g.m4,g.m2,w,S.m5,S.m4,S.m2,t[7],P.m5,P.m4,P.m2,t[11],A.m5,A.m4,A.m2,b],R=this.normal(y[12],y[13],y[14],y[15],y[11],y[7],y[3]);abs2(R)<=this.epsilon&&(R=this.normal(y[12],y[13],y[14],y[15],y[2],y[1],y[0]),abs2(R)<=this.epsilon&&(R=this.normal(y[0],y[4],y[8],y[12],y[11],y[7],y[3])));let D=this.normal(T[3],T[2],T[1],T[0],T[4],T[8],T[12]);abs2(D)<=this.epsilon&&(D=this.normal(T[3],T[2],T[1],T[0],T[13],T[14],T[15]),abs2(D)<=this.epsilon&&(D=this.normal(T[15],T[11],T[7],T[3],T[4],T[8],T[12])));let I=this.Epsilon,z=[.5*(s[0]+o[0]),.5*(s[1]+o[1]),.5*(s[2]+o[2])];if(!c)if(c=Straightness(M,t[13],t[14],b)<this.res2){let t=unit(this.differential(T[12],T[8],T[4],T[0]));z=[z[0]-I*t[0],z[1]-I*t[1],z[2]-I*t[2]]}else z=y[15];let L=[.5*(h[0]+n[0]),.5*(h[1]+n[1]),.5*(h[2]+n[2])];if(!m)if(m=Straightness(x,t[1],t[2],w)<this.res2){let t=unit(this.differential(y[3],y[7],y[11],y[15]));L=[L[0]-I*t[0],L[1]-I*t[1],L[2]-I*t[2]]}else L=T[0];if(f){let t=Array(4),g=Array(4);for(let e=0;e<4;++e)t[e]=.5*(u[e]+p[e]),g[e]=.5*(v[e]+f[e]);let x=this.data.Vertex(z,R,t),w=this.data.Vertex(L,D,g);this.Render(y,e,i,x,w,n,s,z,L,l,c,!1,m,f,u,t,g),this.Render(T,w,x,a,r,L,z,o,h,!1,c,d,m,g,t,p,v)}else{let t=this.vertex(z,R),f=this.vertex(L,D);this.Render(y,e,i,t,f,n,s,z,L,l,c,!1,m),this.Render(T,f,t,a,r,L,z,o,h,!1,c,d,m)}return}if(g[1]<this.res2){let g=new Split3(x,t[4],t[8],M),S=new Split3(t[1],t[5],t[9],t[13]),P=new Split3(t[2],t[6],t[10],t[14]),A=new Split3(w,t[7],t[11],b),y=[x,t[1],t[2],w,g.m0,S.m0,P.m0,A.m0,g.m3,S.m3,P.m3,A.m3,g.m5,S.m5,P.m5,A.m5],T=[g.m5,S.m5,P.m5,A.m5,g.m4,S.m4,P.m4,A.m4,g.m2,S.m2,P.m2,A.m2,M,t[13],t[14],b],R=this.normal(y[0],y[4],y[8],y[12],y[13],y[14],y[15]);abs2(R)<=this.epsilon&&(R=this.normal(y[0],y[4],y[8],y[12],y[11],y[7],y[3]),abs2(R)<=this.epsilon&&(R=this.normal(y[3],y[2],y[1],y[0],y[13],y[14],y[15])));let D=this.normal(T[15],T[11],T[7],T[3],T[2],T[1],T[0]);abs2(D)<=this.epsilon&&(D=this.normal(T[15],T[11],T[7],T[3],T[4],T[8],T[12]),abs2(D)<=this.epsilon&&(D=this.normal(T[12],T[13],T[14],T[15],T[2],T[1],T[0])));let I=this.Epsilon,z=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])];if(!l)if(l=Straightness(x,t[4],t[8],M)<this.res2){let t=unit(this.differential(T[0],T[1],T[2],T[3]));z=[z[0]-I*t[0],z[1]-I*t[1],z[2]-I*t[2]]}else z=y[12];let L=[.5*(o[0]+h[0]),.5*(o[1]+h[1]),.5*(o[2]+h[2])];if(!d)if(d=Straightness(b,t[11],t[7],w)<this.res2){let t=unit(this.differential(y[15],y[14],y[13],y[12]));L=[L[0]-I*t[0],L[1]-I*t[1],L[2]-I*t[2]]}else L=T[3];if(f){let t=Array(4),g=Array(4);for(let e=0;e<4;++e)t[e]=.5*(f[e]+u[e]),g[e]=.5*(p[e]+v[e]);let x=this.data.Vertex(z,R,t),w=this.data.Vertex(L,D,g);this.Render(y,e,x,w,r,n,z,L,h,l,!1,d,m,f,t,g,v),this.Render(T,x,i,a,w,z,s,o,L,l,c,d,!1,t,u,p,g)}else{let t=this.vertex(z,R),f=this.vertex(L,D);this.Render(y,e,t,f,r,n,z,L,h,l,!1,d,m),this.Render(T,t,i,a,f,z,s,o,L,l,c,d,!1)}return}let S=new Split3(x,t[1],t[2],w),P=new Split3(t[4],t[5],t[6],t[7]),A=new Split3(t[8],t[9],t[10],t[11]),y=new Split3(M,t[13],t[14],b),T=new Split3(x,t[4],t[8],M),R=new Split3(S.m0,P.m0,A.m0,y.m0),D=new Split3(S.m3,P.m3,A.m3,y.m3),I=new Split3(S.m5,P.m5,A.m5,y.m5),z=new Split3(S.m4,P.m4,A.m4,y.m4),L=new Split3(S.m2,P.m2,A.m2,y.m2),N=new Split3(w,t[7],t[11],b),E=[x,S.m0,S.m3,S.m5,T.m0,R.m0,D.m0,I.m0,T.m3,R.m3,D.m3,I.m3,T.m5,R.m5,D.m5,I.m5],O=[T.m5,R.m5,D.m5,I.m5,T.m4,R.m4,D.m4,I.m4,T.m2,R.m2,D.m2,I.m2,M,y.m0,y.m3,y.m5],V=[I.m5,z.m5,L.m5,N.m5,I.m4,z.m4,L.m4,N.m4,I.m2,z.m2,L.m2,N.m2,y.m5,y.m4,y.m2,b],C=[S.m5,S.m4,S.m2,w,I.m0,z.m0,L.m0,N.m0,I.m3,z.m3,L.m3,N.m3,I.m5,z.m5,L.m5,N.m5],B=E[15],H=this.normal(E[0],E[4],E[8],E[12],E[13],E[14],E[15]);abs2(H)<this.epsilon&&(H=this.normal(E[0],E[4],E[8],E[12],E[11],E[7],E[3]),abs2(H)<this.epsilon&&(H=this.normal(E[3],E[2],E[1],E[0],E[13],E[14],E[15])));let _=this.normal(O[12],O[13],O[14],O[15],O[11],O[7],O[3]);abs2(_)<this.epsilon&&(_=this.normal(O[12],O[13],O[14],O[15],O[2],O[1],O[0]),abs2(_)<this.epsilon&&(_=this.normal(O[0],O[4],O[8],O[12],O[11],O[7],O[3])));let F=this.normal(V[15],V[11],V[7],V[3],V[2],V[1],V[0]);abs2(F)<this.epsilon&&(F=this.normal(V[15],V[11],V[7],V[3],V[4],V[8],V[12]),abs2(F)<this.epsilon&&(F=this.normal(V[12],V[13],V[14],V[15],V[2],V[1],V[0])));let G=this.normal(C[3],C[2],C[1],C[0],C[4],C[8],C[12]);abs2(G)<this.epsilon&&(G=this.normal(C[3],C[2],C[1],C[0],C[13],C[14],C[15]),abs2(G)<this.epsilon&&(G=this.normal(C[15],C[11],C[7],C[3],C[4],C[8],C[12])));let W=this.normal(V[3],V[2],V[1],B,V[4],V[8],V[12]),U=this.Epsilon,Z=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])];if(!l)if(l=Straightness(x,t[4],t[8],M)<this.res2){let t=unit(this.differential(O[0],O[1],O[2],O[3]));Z=[Z[0]-U*t[0],Z[1]-U*t[1],Z[2]-U*t[2]]}else Z=E[12];let j=[.5*(s[0]+o[0]),.5*(s[1]+o[1]),.5*(s[2]+o[2])];if(!c)if(c=Straightness(M,t[13],t[14],b)<this.res2){let t=unit(this.differential(V[12],V[8],V[4],V[0]));j=[j[0]-U*t[0],j[1]-U*t[1],j[2]-U*t[2]]}else j=O[15];let k=[.5*(o[0]+h[0]),.5*(o[1]+h[1]),.5*(o[2]+h[2])];if(!d)if(d=Straightness(b,t[11],t[7],w)<this.res2){let t=unit(this.differential(C[15],C[14],C[13],C[12]));k=[k[0]-U*t[0],k[1]-U*t[1],k[2]-U*t[2]]}else k=V[3];let Y=[.5*(h[0]+n[0]),.5*(h[1]+n[1]),.5*(h[2]+n[2])];if(!m)if(m=Straightness(x,t[1],t[2],w)<this.res2){let t=unit(this.differential(E[3],E[7],E[11],E[15]));Y=[Y[0]-U*t[0],Y[1]-U*t[1],Y[2]-U*t[2]]}else Y=C[0];if(f){let t=Array(4),g=Array(4),x=Array(4),w=Array(4),M=Array(4);for(let e=0;e<4;++e)t[e]=.5*(f[e]+u[e]),g[e]=.5*(u[e]+p[e]),x[e]=.5*(p[e]+v[e]),w[e]=.5*(v[e]+f[e]),M[e]=.5*(t[e]+x[e]);let b=this.data.Vertex(Z,H,t),S=this.data.Vertex(j,_,g),P=this.data.Vertex(k,F,x),A=this.data.Vertex(Y,G,w),y=this.data.Vertex(B,W,M);this.Render(E,e,b,y,A,n,Z,B,Y,l,!1,!1,m,f,t,M,w),this.Render(O,b,i,S,y,Z,s,j,B,l,c,!1,!1,t,u,g,M),this.Render(V,y,S,a,P,B,j,o,k,!1,c,d,!1,M,g,p,x),this.Render(C,A,y,P,r,Y,B,k,h,!1,!1,d,m,w,M,x,v)}else{let t=this.vertex(Z,H),f=this.vertex(j,_),u=this.vertex(k,F),p=this.vertex(Y,G),v=this.vertex(B,W);this.Render(E,e,t,v,p,n,Z,B,Y,l,!1,!1,m),this.Render(O,t,i,f,v,Z,s,j,B,l,c,!1,!1),this.Render(V,v,f,a,u,B,j,o,k,!1,c,d,!1),this.Render(C,p,v,u,r,Y,B,k,h,!1,!1,d,m)}}}process3(t){if(1==wireframe)return this.curve(t,0,1,3,6),this.curve(t,6,7,8,9),void this.curve(t,9,5,2,0);let e=t[0],i=t[6],a=t[9],r=this.normal(a,t[5],t[2],e,t[1],t[3],i),n=this.normal(e,t[1],t[3],i,t[7],t[8],a),s=this.normal(i,t[7],t[8],a,t[5],t[2],e);if(this.color){let o=this.color[0],h=this.color[1],l=this.color[2],c=this.data.Vertex(e,r,o),d=this.data.Vertex(i,n,h),m=this.data.Vertex(a,s,l);this.Render3(t,c,d,m,e,i,a,!1,!1,!1,o,h,l)}else{let o=this.vertex(e,r),h=this.vertex(i,n),l=this.vertex(a,s);this.Render3(t,o,h,l,e,i,a,!1,!1,!1)}this.data.indices.length>0&&this.append()}Render3(t,e,i,a,r,n,s,o,h,l,c,d,m){if(this.Distance3(t)<this.res2)this.offscreen([r,n,s])||(0==wireframe?(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(a)):(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(i),this.data.indices.push(a),this.data.indices.push(a),this.data.indices.push(e)));else{if(this.offscreen(t))return;let f=t[0],u=t[1],p=t[2],v=t[3],g=t[4],x=t[5],w=t[6],M=t[7],b=t[8],S=t[9],P=[.5*(S[0]+x[0]),.5*(S[1]+x[1]),.5*(S[2]+x[2])],A=[.5*(S[0]+b[0]),.5*(S[1]+b[1]),.5*(S[2]+b[2])],y=[.5*(x[0]+p[0]),.5*(x[1]+p[1]),.5*(x[2]+p[2])],T=[.5*(b[0]+g[0]),.5*(b[1]+g[1]),.5*(b[2]+g[2])],R=[.5*(b[0]+M[0]),.5*(b[1]+M[1]),.5*(b[2]+M[2])],D=[.5*(p[0]+g[0]),.5*(p[1]+g[1]),.5*(p[2]+g[2])],I=[.5*(p[0]+f[0]),.5*(p[1]+f[1]),.5*(p[2]+f[2])],z=[.5*(g[0]+v[0]),.5*(g[1]+v[1]),.5*(g[2]+v[2])],L=[.5*(M[0]+w[0]),.5*(M[1]+w[1]),.5*(M[2]+w[2])],N=[.5*(f[0]+u[0]),.5*(f[1]+u[1]),.5*(f[2]+u[2])],E=[.5*(u[0]+v[0]),.5*(u[1]+v[1]),.5*(u[2]+v[2])],O=[.5*(v[0]+w[0]),.5*(v[1]+w[1]),.5*(v[2]+w[2])],V=[.5*(P[0]+y[0]),.5*(P[1]+y[1]),.5*(P[2]+y[2])],C=[.5*(A[0]+R[0]),.5*(A[1]+R[1]),.5*(A[2]+R[2])],B=[.5*(y[0]+I[0]),.5*(y[1]+I[1]),.5*(y[2]+I[2])],H=[.5*T[0]+.25*(g[0]+u[0]),.5*T[1]+.25*(g[1]+u[1]),.5*T[2]+.25*(g[2]+u[2])],_=[.5*(R[0]+L[0]),.5*(R[1]+L[1]),.5*(R[2]+L[2])],F=[.5*D[0]+.25*(g[0]+M[0]),.5*D[1]+.25*(g[1]+M[1]),.5*D[2]+.25*(g[2]+M[2])],G=[.25*(x[0]+g[0])+.5*z[0],.25*(x[1]+g[1])+.5*z[1],.25*(x[2]+g[2])+.5*z[2]],W=[.5*(N[0]+E[0]),.5*(N[1]+E[1]),.5*(N[2]+E[2])],U=[.5*(E[0]+O[0]),.5*(E[1]+O[1]),.5*(E[2]+O[2])],Z=[.5*(F[0]+W[0]),.5*(F[1]+W[1]),.5*(F[2]+W[2])],j=[.5*(F[0]+U[0]),.5*(F[1]+U[1]),.5*(F[2]+U[2])],k=[.5*(W[0]+U[0]),.5*(W[1]+U[1]),.5*(W[2]+U[2])],Y=[.5*(G[0]+_[0]),.5*(G[1]+_[1]),.5*(G[2]+_[2])],X=[.5*(C[0]+G[0]),.5*(C[1]+G[1]),.5*(C[2]+G[2])],q=[.5*(C[0]+_[0]),.5*(C[1]+_[1]),.5*(C[2]+_[2])],K=[.5*(V[0]+H[0]),.5*(V[1]+H[1]),.5*(V[2]+H[2])],$=[.5*(B[0]+H[0]),.5*(B[1]+H[1]),.5*(B[2]+H[2])],Q=[.5*(V[0]+B[0]),.5*(V[1]+B[1]),.5*(V[2]+B[2])],J=[f,N,I,W,[.5*(D[0]+N[0]),.5*(D[1]+N[1]),.5*(D[2]+N[2])],B,k,Z,$,Q],tt=[k,U,j,O,[.5*(z[0]+L[0]),.5*(z[1]+L[1]),.5*(z[2]+L[2])],Y,w,L,_,q],et=[Q,K,V,X,[.5*(P[0]+T[0]),.5*(P[1]+T[1]),.5*(P[2]+T[2])],P,q,C,A,S],it=[q,X,Y,K,[.25*(y[0]+R[0]+E[0]+g[0]),.25*(y[1]+R[1]+E[1]+g[1]),.25*(y[2]+R[2]+E[2]+g[2])],j,Q,$,Z,k],at=this.normal(k,j,Y,q,X,K,Q),rt=this.normal(q,X,K,Q,$,Z,k),nt=this.normal(Q,$,Z,k,j,Y,q),st=this.Epsilon,ot=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])];if(!o)if(o=Straightness(w,M,b,S)<this.res2){let t=unit(this.sumdifferential(it[0],it[2],it[5],it[9],it[1],it[3],it[6]));ot=[ot[0]-st*t[0],ot[1]-st*t[1],ot[2]-st*t[2]]}else ot=q;let ht=[.5*(s[0]+r[0]),.5*(s[1]+r[1]),.5*(s[2]+r[2])];if(!h)if(h=Straightness(f,p,x,S)<this.res2){let t=unit(this.sumdifferential(it[6],it[3],it[1],it[0],it[7],it[8],it[9]));ht=[ht[0]-st*t[0],ht[1]-st*t[1],ht[2]-st*t[2]]}else ht=Q;let lt=[.5*(r[0]+n[0]),.5*(r[1]+n[1]),.5*(r[2]+n[2])];if(!l)if(l=Straightness(f,u,v,w)<this.res2){let t=unit(this.sumdifferential(it[9],it[8],it[7],it[6],it[5],it[2],it[0]));lt=[lt[0]-st*t[0],lt[1]-st*t[1],lt[2]-st*t[2]]}else lt=k;if(c){let t=Array(4),f=Array(4),u=Array(4);for(let e=0;e<4;++e)t[e]=.5*(d[e]+m[e]),f[e]=.5*(m[e]+c[e]),u[e]=.5*(c[e]+d[e]);let p=this.data.Vertex(ot,at,t),v=this.data.Vertex(ht,rt,f),g=this.data.Vertex(lt,nt,u);this.Render3(J,e,g,v,r,lt,ht,!1,h,l,c,u,f),this.Render3(tt,g,i,p,lt,n,ot,o,!1,l,u,d,t),this.Render3(et,v,p,a,ht,ot,s,o,h,!1,f,t,m),this.Render3(it,p,v,g,ot,ht,lt,!1,!1,!1,t,f,u)}else{let t=this.vertex(ot,at),c=this.vertex(ht,rt),d=this.vertex(lt,nt);this.Render3(J,e,d,c,r,lt,ht,!1,h,l),this.Render3(tt,d,i,t,lt,n,ot,o,!1,l),this.Render3(et,c,t,a,ht,ot,s,o,h,!1),this.Render3(it,t,c,d,ot,ht,lt,!1,!1,!1)}}}Distance(t){let e=t[0],i=t[3],a=t[12],r=t[15],n=Flatness(e,a,i,r);n=Math.max(Straightness(e,t[4],t[8],a)),n=Math.max(n,Straightness(t[1],t[5],t[9],t[13])),n=Math.max(n,Straightness(i,t[7],t[11],r)),n=Math.max(n,Straightness(t[2],t[6],t[10],t[14]));let s=Flatness(e,i,a,r);return s=Math.max(s,Straightness(e,t[1],t[2],i)),s=Math.max(s,Straightness(t[4],t[5],t[6],t[7])),s=Math.max(s,Straightness(t[8],t[9],t[10],t[11])),s=Math.max(s,Straightness(a,t[13],t[14],r)),[n,s]}Distance3(t){let e=t[0],i=t[4],a=t[6],r=t[9],n=abs2([(e[0]+a[0]+r[0])*third-i[0],(e[1]+a[1]+r[1])*third-i[1],(e[2]+a[2]+r[2])*third-i[2]]);return n=Math.max(n,Straightness(e,t[1],t[3],a)),n=Math.max(n,Straightness(e,t[2],t[5],r)),Math.max(n,Straightness(a,t[7],t[8],r))}differential(t,e,i,a){let r=[3*(e[0]-t[0]),3*(e[1]-t[1]),3*(e[2]-t[2])];return abs2(r)>this.epsilon?r:(r=bezierPP(t,e,i),abs2(r)>this.epsilon?r:bezierPPP(t,e,i,a))}sumdifferential(t,e,i,a,r,n,s){let o=this.differential(t,e,i,a),h=this.differential(t,r,n,s);return[o[0]+h[0],o[1]+h[1],o[2]+h[2]]}normal(t,e,i,a,r,n,s){let o=3*(r[0]-a[0]),h=3*(r[1]-a[1]),l=3*(r[2]-a[2]),c=3*(i[0]-a[0]),d=3*(i[1]-a[1]),m=3*(i[2]-a[2]),f=[h*m-l*d,l*c-o*m,o*d-h*c];if(abs2(f)>this.epsilon)return f;let u=[c,d,m],p=[o,h,l],v=bezierPP(a,i,e),g=bezierPP(a,r,n),x=cross(g,u),w=cross(p,v);if(f=[x[0]+w[0],x[1]+w[1],x[2]+w[2]],abs2(f)>this.epsilon)return f;let M=bezierPPP(a,i,e,t),b=bezierPPP(a,r,n,s);x=cross(p,M),w=cross(b,u);let S=cross(g,v);return f=[x[0]+w[0]+S[0],x[1]+w[1]+S[1],x[2]+w[2]+S[2]],abs2(f)>this.epsilon?f:(x=cross(b,v),w=cross(g,M),f=[x[0]+w[0],x[1]+w[1],x[2]+w[2]],abs2(f)>this.epsilon?f:cross(b,M))}}class BezierCurve extends Geometry{constructor(t,e,i,a,r){super(),this.controlpoints=t,this.Min=a,this.Max=r,this.CenterIndex=e,this.MaterialIndex=i}setMaterialIndex(){this.setMaterial(material1Data,drawMaterial1)}processLine(t){let e=t[0],i=t[1];if(!this.offscreen([e,i])){let t=[0,0,1];this.data.indices.push(this.data.vertex(e,t)),this.data.indices.push(this.data.vertex(i,t)),this.append()}}process(t){if(2==t.length)return this.processLine(t);let e=t[0],i=t[1],a=t[2],r=t[3],n=this.normal(bezierP(e,i),bezierPP(e,i,a)),s=this.normal(bezierP(a,r),bezierPP(r,a,i)),o=this.data.vertex(e,n),h=this.data.vertex(r,s);this.Render(t,o,h),this.data.indices.length>0&&this.append()}append(){material1Data.append(this.data)}notRendered(){material1Data.rendered=!1}Render(t,e,i){let a=t[0],r=t[1],n=t[2],s=t[3];if(Straightness(a,r,n,s)<this.res2)this.offscreen([a,s])||(this.data.indices.push(e),this.data.indices.push(i));else{if(this.offscreen(t))return;let o=[.5*(a[0]+r[0]),.5*(a[1]+r[1]),.5*(a[2]+r[2])],h=[.5*(r[0]+n[0]),.5*(r[1]+n[1]),.5*(r[2]+n[2])],l=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])],c=[.5*(o[0]+h[0]),.5*(o[1]+h[1]),.5*(o[2]+h[2])],d=[.5*(h[0]+l[0]),.5*(h[1]+l[1]),.5*(h[2]+l[2])],m=[.5*(c[0]+d[0]),.5*(c[1]+d[1]),.5*(c[2]+d[2])],f=[a,o,c,m],u=[m,d,l,s],p=this.normal(bezierPh(a,r,n,s),bezierPPh(a,r,n,s)),v=this.data.vertex(m,p);this.Render(f,e,v),this.Render(u,v,i)}}normal(t,e){let i=dot(t,t),a=dot(t,e);return[i*e[0]-a*t[0],i*e[1]-a*t[1],i*e[2]-a*t[2]]}}class Pixel extends Geometry{constructor(t,e,i,a,r){super(),this.controlpoint=t,this.width=e,this.CenterIndex=0,this.MaterialIndex=i,this.Min=a,this.Max=r}setMaterialIndex(){this.setMaterial(material0Data,drawMaterial0)}process(t){this.data.indices.push(this.data.vertex0(this.controlpoint,this.width)),this.append()}append(){material0Data.append(this.data)}notRendered(){material0Data.rendered=!1}}class Triangles extends Geometry{constructor(t,e,i){super(),this.CenterIndex=0,this.MaterialIndex=t,this.Min=e,this.Max=i,this.Positions=Positions,this.Normals=Normals,this.Colors=Colors,this.Indices=Indices,Positions=[],Normals=[],Colors=[],Indices=[],this.transparent=Materials[t].diffuse[3]<1}setMaterialIndex(){this.transparent?this.setMaterial(transparentData,drawTransparent):this.setMaterial(triangleData,drawTriangle)}process(t){materialIndex=this.Colors.length>0?-1-materialIndex:1+materialIndex;for(let t=0,e=this.Indices.length;t<e;++t){let e=this.Indices[t],i=e[0],a=this.Positions[i[0]],r=this.Positions[i[1]],n=this.Positions[i[2]];if(!this.offscreen([a,r,n])){let t=e.length>1?e[1]:i;if(t&&0!=t.length||(t=i),this.Colors.length>0){let s=e.length>2?e[2]:i;s&&0!=s.length||(s=i);let o=this.Colors[s[0]],h=this.Colors[s[1]],l=this.Colors[s[2]];this.transparent|=o[3]+h[3]+l[3]<765,0==wireframe?(this.data.iVertex(i[0],a,this.Normals[t[0]],o),this.data.iVertex(i[1],r,this.Normals[t[1]],h),this.data.iVertex(i[2],n,this.Normals[t[2]],l)):(this.data.iVertex(i[0],a,this.Normals[t[0]],o),this.data.iVertex(i[1],r,this.Normals[t[1]],h),this.data.iVertex(i[1],r,this.Normals[t[1]],h),this.data.iVertex(i[2],n,this.Normals[t[2]],l),this.data.iVertex(i[2],n,this.Normals[t[2]],l),this.data.iVertex(i[0],a,this.Normals[t[0]],o))}else 0==wireframe?(this.data.iVertex(i[0],a,this.Normals[t[0]]),this.data.iVertex(i[1],r,this.Normals[t[1]]),this.data.iVertex(i[2],n,this.Normals[t[2]])):(this.data.iVertex(i[0],a,this.Normals[t[0]]),this.data.iVertex(i[1],r,this.Normals[t[1]]),this.data.iVertex(i[1],r,this.Normals[t[1]]),this.data.iVertex(i[2],n,this.Normals[t[2]]),this.data.iVertex(i[2],n,this.Normals[t[2]]),this.data.iVertex(i[0],a,this.Normals[t[0]]))}}this.data.nvertices=this.Positions.length,this.data.indices.length>0&&this.append()}append(){this.transparent?transparentData.append(this.data):triangleData.append(this.data)}notRendered(){this.transparent?transparentData.rendered=!1:triangleData.rendered=!1}}function redraw(){initProjection(),setProjection(),remesh=!0,draw()}function home(){mat4.identity(rotMat),redraw()}let positionAttribute=0,normalAttribute=1,materialAttribute=2,colorAttribute=3,widthAttribute=4;function initShader(t=[]){let e=getShader(gl,vertex,gl.VERTEX_SHADER,t),i=getShader(gl,fragment,gl.FRAGMENT_SHADER,t),a=gl.createProgram();return gl.attachShader(a,e),gl.attachShader(a,i),gl.bindAttribLocation(a,positionAttribute,"position"),gl.bindAttribLocation(a,normalAttribute,"normal"),gl.bindAttribLocation(a,materialAttribute,"materialIndex"),gl.bindAttribLocation(a,colorAttribute,"color"),gl.bindAttribLocation(a,widthAttribute,"width"),gl.linkProgram(a),gl.getProgramParameter(a,gl.LINK_STATUS)||alert("Could not initialize shaders"),a}class Split3{constructor(t,e,i,a){this.m0=[.5*(t[0]+e[0]),.5*(t[1]+e[1]),.5*(t[2]+e[2])];let r=.5*(e[0]+i[0]),n=.5*(e[1]+i[1]),s=.5*(e[2]+i[2]);this.m2=[.5*(i[0]+a[0]),.5*(i[1]+a[1]),.5*(i[2]+a[2])],this.m3=[.5*(this.m0[0]+r),.5*(this.m0[1]+n),.5*(this.m0[2]+s)],this.m4=[.5*(r+this.m2[0]),.5*(n+this.m2[1]),.5*(s+this.m2[2])],this.m5=[.5*(this.m3[0]+this.m4[0]),.5*(this.m3[1]+this.m4[1]),.5*(this.m3[2]+this.m4[2])]}}function unit(t){let e=1/(Math.sqrt(t[0]*t[0]+t[1]*t[1]+t[2]*t[2])||1);return[t[0]*e,t[1]*e,t[2]*e]}function abs2(t){return t[0]*t[0]+t[1]*t[1]+t[2]*t[2]}function dot(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function cross(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function bezierP(t,e){return[e[0]-t[0],e[1]-t[1],e[2]-t[2]]}function bezierPP(t,e,i){return[3*(t[0]+i[0])-6*e[0],3*(t[1]+i[1])-6*e[1],3*(t[2]+i[2])-6*e[2]]}function bezierPPP(t,e,i,a){return[a[0]-t[0]+3*(e[0]-i[0]),a[1]-t[1]+3*(e[1]-i[1]),a[2]-t[2]+3*(e[2]-i[2])]}function bezierPh(t,e,i,a){return[i[0]+a[0]-t[0]-e[0],i[1]+a[1]-t[1]-e[1],i[2]+a[2]-t[2]-e[2]]}function bezierPPh(t,e,i,a){return[3*t[0]-5*e[0]+i[0]+a[0],3*t[1]-5*e[1]+i[1]+a[1],3*t[2]-5*e[2]+i[2]+a[2]]}function Straightness(t,e,i,a){let r=[third*(a[0]-t[0]),third*(a[1]-t[1]),third*(a[2]-t[2])];return Math.max(abs2([e[0]-r[0]-t[0],e[1]-r[1]-t[1],e[2]-r[2]-t[2]]),abs2([a[0]-r[0]-i[0],a[1]-r[1]-i[1],a[2]-r[2]-i[2]]))}function Flatness(t,e,i,a){let r=[e[0]-t[0],e[1]-t[1],e[2]-t[2]],n=[a[0]-i[0],a[1]-i[1],a[2]-i[2]];return Math.max(abs2(cross(r,unit(n))),abs2(cross(n,unit(r))))/9}function corners(t,e){return[t,[t[0],t[1],e[2]],[t[0],e[1],t[2]],[t[0],e[1],e[2]],[e[0],t[1],t[2]],[e[0],t[1],e[2]],[e[0],e[1],t[2]],e]}function minbound(t){return[Math.min(t[0][0],t[1][0],t[2][0],t[3][0],t[4][0],t[5][0],t[6][0],t[7][0]),Math.min(t[0][1],t[1][1],t[2][1],t[3][1],t[4][1],t[5][1],t[6][1],t[7][1]),Math.min(t[0][2],t[1][2],t[2][2],t[3][2],t[4][2],t[5][2],t[6][2],t[7][2])]}function maxbound(t){return[Math.max(t[0][0],t[1][0],t[2][0],t[3][0],t[4][0],t[5][0],t[6][0],t[7][0]),Math.max(t[0][1],t[1][1],t[2][1],t[3][1],t[4][1],t[5][1],t[6][1],t[7][1]),Math.max(t[0][2],t[1][2],t[2][2],t[3][2],t[4][2],t[5][2],t[6][2],t[7][2])]}function COBTarget(t,e){mat4.fromTranslation(T,[center.x,center.y,center.z]),mat4.invert(cjMatInv,T),mat4.multiply(t,e,cjMatInv),mat4.multiply(t,T,t)}function setUniforms(t,e){let i=e==pixelShader;gl.useProgram(e),gl.enableVertexAttribArray(positionAttribute),i&&gl.enableVertexAttribArray(widthAttribute);let a=!i&&Lights.length>0;if(a&&gl.enableVertexAttribArray(normalAttribute),gl.enableVertexAttribArray(materialAttribute),e.projViewMatUniform=gl.getUniformLocation(e,"projViewMat"),e.viewMatUniform=gl.getUniformLocation(e,"viewMat"),e.normMatUniform=gl.getUniformLocation(e,"normMat"),e!=colorShader&&e!=transparentShader||gl.enableVertexAttribArray(colorAttribute),a)for(let t=0;t<Lights.length;++t)Lights[t].setUniform(e,t);for(let i=0;i<t.materials.length;++i)t.materials[i].setUniform(e,i);gl.uniformMatrix4fv(e.projViewMatUniform,!1,projViewMat),gl.uniformMatrix4fv(e.viewMatUniform,!1,viewMat),gl.uniformMatrix3fv(e.normMatUniform,!1,normMat)}function handleMouseDown(t){zoomEnabled||enableZoom(),mouseDownOrTouchActive=!0,lastMouseX=t.clientX,lastMouseY=t.clientY}let pinchStart,touchStartTime,pinch=!1;function pinchDistance(t){return Math.hypot(t[0].pageX-t[1].pageX,t[0].pageY-t[1].pageY)}function handleTouchStart(t){t.preventDefault(),zoomEnabled||enableZoom();let e=t.targetTouches;swipe=rotate=pinch=!1,zooming||(1!=e.length||mouseDownOrTouchActive||(touchStartTime=(new Date).getTime(),touchId=e[0].identifier,lastMouseX=e[0].pageX,lastMouseY=e[0].pageY),2!=e.length||mouseDownOrTouchActive||(touchId=e[0].identifier,pinchStart=pinchDistance(e),pinch=!0))}function handleMouseUpOrTouchEnd(t){mouseDownOrTouchActive=!1}function rotateScene(t,e,i,a,r){if(t==i&&e==a)return;let[n,s]=arcball([t,-e],[i,-a]);mat4.fromRotation(T,2*r*ArcballFactor*n/Zoom,s),mat4.multiply(rotMat,T,rotMat)}function shiftScene(t,e,i,a){let r=1/Zoom;shift.x+=(i-t)*r*halfCanvasWidth,shift.y-=(a-e)*r*halfCanvasHeight}function panScene(t,e,i,a){orthographic?shiftScene(t,e,i,a):(center.x+=(i-t)*(viewParam.xmax-viewParam.xmin),center.y-=(a-e)*(viewParam.ymax-viewParam.ymin))}function updateViewMatrix(){COBTarget(viewMat,rotMat),mat4.translate(viewMat,viewMat,[center.x,center.y,0]),mat3.fromMat4(viewMat3,viewMat),mat3.invert(normMat,viewMat3),mat4.multiply(projViewMat,projMat,viewMat)}function capzoom(){let t=Math.sqrt(Number.MAX_VALUE),e=1/t;Zoom<=e&&(Zoom=e),Zoom>=t&&(Zoom=t),(zoomRemeshFactor*Zoom<lastZoom||Zoom>zoomRemeshFactor*lastZoom)&&(remesh=!0,lastZoom=Zoom)}function zoomImage(t){let e=zoomStep*halfCanvasHeight*t;const i=Math.log(.1*Number.MAX_VALUE)/Math.log(zoomFactor);Math.abs(e)<i&&(Zoom*=zoomFactor**e,capzoom())}function normMouse(t){let e=t[0],i=t[1],a=Math.hypot(e,i);return a>1&&(denom=1/a,e*=denom,i*=denom),[e,i,Math.sqrt(Math.max(1-i*i-e*e,0))]}function arcball(t,e){let i=normMouse(t),a=normMouse(e),r=dot(i,a);return[r>1?0:r<-1?pi:Math.acos(r),unit(cross(i,a))]}function zoomScene(t,e,i,a){zoomImage(e-a)}const DRAGMODE_ROTATE=1,DRAGMODE_SHIFT=2,DRAGMODE_ZOOM=3,DRAGMODE_PAN=4;function processDrag(t,e,i,a=1){let r;switch(i){case 1:r=rotateScene;break;case 2:r=shiftScene;break;case 3:r=zoomScene;break;case 4:r=panScene;break;default:r=(t,e,i,a)=>{}}r((lastMouseX-halfCanvasWidth)/halfCanvasWidth,(lastMouseY-halfCanvasHeight)/halfCanvasHeight,(t-halfCanvasWidth)/halfCanvasWidth,(e-halfCanvasHeight)/halfCanvasHeight,a),lastMouseX=t,lastMouseY=e,setProjection(),draw()}let zoomEnabled=0;function enableZoom(){zoomEnabled=1,canvas.addEventListener("wheel",handleMouseWheel,!1)}function disableZoom(){zoomEnabled=0,canvas.removeEventListener("wheel",handleMouseWheel,!1)}function handleKey(t){if(zoomEnabled||enableZoom(),embedded&&zoomEnabled&&27==t.keyCode)return void disableZoom();let e=[];switch(t.key){case"x":e=[1,0,0];break;case"y":e=[0,1,0];break;case"z":e=[0,0,1];break;case"h":home();break;case"m":++wireframe,3==wireframe&&(wireframe=0),2!=wireframe&&(embedded||deleteShaders(),initShaders()),remesh=!0,draw();break;case"+":case"=":case">":expand();break;case"-":case"_":case"<":shrink()}e.length>0&&(mat4.rotate(rotMat,rotMat,.1,e),updateViewMatrix(),draw())}function setZoom(){capzoom(),setProjection(),draw()}function handleMouseWheel(t){t.preventDefault(),t.deltaY<0?Zoom*=zoomFactor:Zoom/=zoomFactor,setZoom()}function handleMouseMove(t){if(!mouseDownOrTouchActive)return;let e,i=t.clientX,a=t.clientY;e=t.getModifierState("Control")?2:t.getModifierState("Shift")?3:t.getModifierState("Alt")?4:1,processDrag(i,a,e)}let zooming=!1,swipe=!1,rotate=!1;function handleTouchMove(t){if(t.preventDefault(),zooming)return;let e=t.targetTouches;if(!pinch&&1==e.length&&touchId==e[0].identifier){let t=e[0].pageX,i=e[0].pageY,a=t-lastMouseX,r=i-lastMouseY,n=a*a+r*r<=shiftHoldDistance*shiftHoldDistance;if(n&&!swipe&&!rotate&&(new Date).getTime()-touchStartTime>shiftWaitTime&&(navigator.vibrate&&window.navigator.vibrate(vibrateTime),swipe=!0),swipe)processDrag(t,i,2);else if(!n){rotate=!0,processDrag(e[0].pageX,e[0].pageY,1,.5)}}if(pinch&&!swipe&&2==e.length&&touchId==e[0].identifier){let t=pinchDistance(e),i=t-pinchStart;zooming=!0,i*=zoomPinchFactor,i>zoomPinchCap&&(i=zoomPinchCap),i<-zoomPinchCap&&(i=-zoomPinchCap),zoomImage(i/size2),pinchStart=t,swipe=rotate=zooming=!1,setProjection(),draw()}}let pixelShader,materialShader,colorShader,transparentShader,zbuffer=[];function transformVertices(t){let e=viewMat[2],i=viewMat[6],a=viewMat[10];zbuffer.length=t.length;for(let r=0;r<t.length;++r){let n=6*r;zbuffer[r]=e*t[n]+i*t[n+1]+a*t[n+2]}}function drawMaterial0(){drawBuffer(material0Data,pixelShader),material0Data.clear()}function drawMaterial1(){drawBuffer(material1Data,materialShader),material1Data.clear()}function drawMaterial(){drawBuffer(materialData,materialShader),materialData.clear()}function drawColor(){drawBuffer(colorData,colorShader),colorData.clear()}function drawTriangle(){drawBuffer(triangleData,transparentShader),triangleData.rendered=!1,triangleData.clear()}function drawTransparent(){let t=transparentData.indices;if(wireframe>0)return drawBuffer(transparentData,transparentShader,t),void transparentData.clear();if(t.length>0){transformVertices(transparentData.vertices);let e=t.length/3,i=Array(e).fill().map((t,e)=>e);i.sort((function(e,i){let a=3*e;Ia=t[a],Ib=t[a+1],Ic=t[a+2];let r=3*i;return IA=t[r],IB=t[r+1],IC=t[r+2],zbuffer[Ia]+zbuffer[Ib]+zbuffer[Ic]<zbuffer[IA]+zbuffer[IB]+zbuffer[IC]?-1:1}));let a=Array(t.length);for(let r=0;r<e;++r){let e=3*i[r];a[3*r]=t[e],a[3*r+1]=t[e+1],a[3*r+2]=t[e+2]}gl.depthMask(!1),drawBuffer(transparentData,transparentShader,a),transparentData.rendered=!1,gl.depthMask(!0)}transparentData.clear()}function drawBuffers(){drawMaterial0(),drawMaterial1(),drawMaterial(),drawColor(),drawTriangle(),drawTransparent()}function draw(){embedded&&(offscreen.width=canvasWidth,offscreen.height=canvasHeight,setViewport()),gl.clearColor(Background[0],Background[1],Background[2],Background[3]),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);for(let t=0;t<P.length;++t)P[t].render();drawBuffers(),embedded&&(context.clearRect(0,0,canvasWidth,canvasHeight),context.drawImage(offscreen,0,0)),0==wireframe&&(remesh=!1)}function setDimensions(t,e,i,a){let r=t/e,n=1/Zoom,s=(i/t+viewportshift[0])*Zoom,o=(a/e+viewportshift[1])*Zoom;if(orthographic){let t=B[0]-b[0],e=B[1]-b[1];if(t<e*r){let t=.5*e*r*n,i=2*t*s,a=e*n*o;viewParam.xmin=-t-i,viewParam.xmax=t-i,viewParam.ymin=b[1]*n-a,viewParam.ymax=B[1]*n-a}else{let e=.5*t/(r*Zoom),i=t*n*s,a=2*e*o;viewParam.xmin=b[0]*n-i,viewParam.xmax=B[0]*n-i,viewParam.ymin=-e-a,viewParam.ymax=e-a}}else{let t=H*n,e=t*r,i=2*e*s,a=2*t*o;viewParam.xmin=-e-i,viewParam.xmax=e-i,viewParam.ymin=-t-a,viewParam.ymax=t-a}}function setProjection(){setDimensions(canvasWidth,canvasHeight,shift.x,shift.y),(orthographic?mat4.ortho:mat4.frustum)(projMat,viewParam.xmin,viewParam.xmax,viewParam.ymin,viewParam.ymax,-viewParam.zmax,-viewParam.zmin),updateViewMatrix()}function initProjection(){H=-Math.tan(.5*angle)*B[2],center.x=center.y=0,center.z=.5*(b[2]+B[2]),lastZoom=Zoom=zoom0,viewParam.zmin=b[2],viewParam.zmax=B[2],shift.x=shift.y=0}function setViewport(){gl.viewportWidth=canvasWidth,gl.viewportHeight=canvasHeight,gl.viewport(.5*(canvas.width-canvasWidth),.5*(canvas.height-canvasHeight),canvasWidth,canvasHeight),gl.scissor(0,0,canvas.width,canvas.height)}function setCanvas(){embedded&&(canvas.width=offscreen.width=canvasWidth,canvas.height=offscreen.height=canvasHeight),size2=Math.hypot(canvasWidth,canvasHeight),halfCanvasWidth=.5*canvas.width,halfCanvasHeight=.5*canvas.height,ArcballFactor=1+8*Math.hypot(viewportmargin[0],viewportmargin[1])/size2}function setsize(t,e){t>maxViewportWidth&&(t=maxViewportWidth),e>maxViewportHeight&&(e=maxViewportHeight),shift.x*=t/canvasWidth,shift.y*=e/canvasHeight,canvasWidth=t,canvasHeight=e,setCanvas(),setViewport(),setProjection(),remesh=!0}function resize(){if(zoom0=Zoom0,absolute&&!embedded)canvasWidth=canvasWidth0*window.devicePixelRatio,canvasHeight=canvasHeight0*window.devicePixelRatio;else{let t=canvasWidth0/canvasHeight0;canvasWidth=Math.max(window.innerWidth-10,10),canvasHeight=Math.max(window.innerHeight-10,10),!orthographic&&canvasWidth<canvasHeight*t&&(zoom0*=canvasWidth/(canvasHeight*t))}canvas.width=canvasWidth,canvas.height=canvasHeight;window.innerWidth,window.innerHeight;viewportshift[0]/=zoom0,viewportshift[1]/=zoom0,setsize(canvasWidth,canvasHeight),redraw()}function expand(){Zoom*=zoomFactor,setZoom()}function shrink(){Zoom/=zoomFactor,setZoom()}class Align{constructor(t,e){if(this.center=t,e){let t=e[0],i=e[1];this.ct=Math.cos(t),this.st=Math.sin(t),this.cp=Math.cos(i),this.sp=Math.sin(i)}}T0(t){return[t[0]+this.center[0],t[1]+this.center[1],t[2]+this.center[2]]}T(t){let e=t[0],i=t[1],a=t[2],r=e*this.ct+a*this.st;return[r*this.cp-i*this.sp+this.center[0],r*this.sp+i*this.cp+this.center[1],-e*this.st+a*this.ct+this.center[2]]}}function Tcorners(t,e,i){let a=[t(e),t([e[0],e[1],i[2]]),t([e[0],i[1],e[2]]),t([e[0],i[1],i[2]]),t([i[0],e[1],e[2]]),t([i[0],e[1],i[2]]),t([i[0],i[1],e[2]]),t(i)];return[minbound(a),maxbound(a)]}function sphere(t,e,i,r,n){let s,o,h,l,c,d,m=.524670512339254,f=.595936986722291,u=.954967051233925,p=.0820155480083437,v=.996685028842544,g=.0549670512339254,x=.998880711874577,w=.0405017186586849,M=[[[1,0,0],[1,0,m],[f,0,u],[p,0,v],[1,a,0],[1,a,m],[f,a*f,u],[p,a*p,v],[a,1,0],[a,1,m],[a*f,f,u],[a*p,p,v],[0,1,0],[0,1,m],[0,f,u],[0,p,v]],[[p,0,v],[p,a*p,v],[g,0,x],[a*p,p,v],[w,w,1],[.05*a,0,1],[0,p,v],[0,g,x],[0,.05*a,1],[0,0,1]]],b=new Align(t,n);function S(t){let e=Array(t.length);for(let i=0;i<t.length;++i){let a=t[i];e[i]=c([s*a[0],o*a[1],h*a[2]])}return e}n?(l=1,d=0,c=b.T.bind(b)):(l=-1,d=-e,c=b.T0.bind(b));let A=Tcorners(c,[-e,-e,d],[e,e,e]),y=A[0],T=A[1];for(let t=-1;t<=1;t+=2){s=t*e;for(let t=-1;t<=1;t+=2){o=t*e;for(let t=l;t<=1;t+=2){h=t*e;for(let t=0;t<2;++t)P.push(new BezierPatch(S(M[t]),i,r,y,T))}}}}let a=4/3*(Math.sqrt(2)-1);function disk(t,e,i,r,n){let s=1-2*a/3,o=[[1,0,0],[1,-a,0],[a,-1,0],[0,-1,0],[1,a,0],[s,0,0],[0,-s,0],[-a,-1,0],[a,1,0],[0,s,0],[-s,0,0],[-1,-a,0],[0,1,0],[-a,1,0],[-1,a,0],[-1,0,0]],h=new Align(t,n);let l=Tcorners(h.T.bind(h),[-e,-e,0],[e,e,0]);P.push(new BezierPatch(function(t){let i=Array(t.length);for(let a=0;a<t.length;++a){let r=t[a];i[a]=h.T([e*r[0],e*r[1],0])}return i}(o),i,r,l[0],l[1]))}function cylinder(t,e,i,r,n,s,o){let h,l,c=[[1,0,0],[1,0,1/3],[1,0,2/3],[1,0,1],[1,a,0],[1,a,1/3],[1,a,2/3],[1,a,1],[a,1,0],[a,1,1/3],[a,1,2/3],[a,1,1],[0,1,0],[0,1,1/3],[0,1,2/3],[0,1,1]],d=new Align(t,s);function m(t){let e=Array(t.length);for(let a=0;a<t.length;++a){let r=t[a];e[a]=d.T([h*r[0],l*r[1],i*r[2]])}return e}let f=Tcorners(d.T.bind(d),[-e,-e,0],[e,e,i]),u=f[0],p=f[1];for(let t=-1;t<=1;t+=2){h=t*e;for(let t=-1;t<=1;t+=2)l=t*e,P.push(new BezierPatch(m(c),r,n,u,p))}if(o){let e=d.T([0,0,i]);P.push(new BezierCurve([t,e],r,n,t,e))}}function rmf(t,e,i,a,r){class n{constructor(t,e,i){this.p=t,this.r=e,this.t=i,this.s=cross(i,e)}}let s=Number.EPSILON*Math.max(abs2(t),abs2(e),abs2(i),abs2(a));function o(r){if(1==r){let r=[a[0]-i[0],a[1]-i[1],a[2]-i[2]];return abs2(r)>s?unit(r):(r=[2*i[0]-e[0]-a[0],2*i[1]-e[1]-a[1],2*i[2]-e[2]-a[2]],abs2(r)>s?unit(r):[a[0]-t[0]+3*(e[0]-i[0]),a[1]-t[1]+3*(e[1]-i[1]),a[2]-t[2]+3*(e[2]-i[2])])}let n=[a[0]-t[0]+3*(e[0]-i[0]),a[1]-t[1]+3*(e[1]-i[1]),a[2]-t[2]+3*(e[2]-i[2])],o=[2*(t[0]+i[0])-4*e[0],2*(t[1]+i[1])-4*e[1],2*(t[2]+i[2])-4*e[2]],h=[e[0]-t[0],e[1]-t[1],e[2]-t[2]],l=r*r,c=[n[0]*l+o[0]*r+h[0],n[1]*l+o[1]*r+h[1],n[2]*l+o[2]*r+h[2]];return abs2(c)>s?unit(c):(l=2*r,c=[n[0]*l+o[0],n[1]*l+o[1],n[2]*l+o[2]],abs2(c)>s?unit(c):unit(n))}let h=Array(r.length),l=[e[0]-t[0],e[1]-t[1],e[2]-t[2]];abs2(l)<s&&(l=[t[0]-2*e[0]+i[0],t[1]-2*e[1]+i[1],t[2]-2*e[2]+i[2]],abs2(l)<s&&(l=[a[0]-t[0]+3*(e[0]-i[0]),a[1]-t[1]+3*(e[1]-i[1]),a[2]-t[2]+3*(e[2]-i[2])])),l=unit(l);let c=function(t){let e=cross(t,[0,1,0]),i=Number.EPSILON*abs2(t);return abs2(e)>i?unit(e):(e=cross(t,[0,0,1]),abs2(e)>i?unit(e):[1,0,0])}(l);h[0]=new n(t,c,l);for(let s=1;s<r.length;++s){let l=h[s-1],c=r[s],d=1-c,m=d*d,f=m*d,u=3*c;m*=u,d*=u*c;let p=c*c*c,v=[f*t[0]+m*e[0]+d*i[0]+p*a[0],f*t[1]+m*e[1]+d*i[1]+p*a[1],f*t[2]+m*e[2]+d*i[2]+p*a[2]],g=[v[0]-l.p[0],v[1]-l.p[1],v[2]-l.p[2]];if(0!=g[0]||0!=g[1]||0!=g[2]){let t=l.r,e=unit(g),i=l.t,a=dot(e,i),r=[i[0]-2*a*e[0],i[1]-2*a*e[1],i[2]-2*a*e[2]];i=o(c);let d=2*dot(e,t),m=[t[0]-d*e[0],t[1]-d*e[1],t[2]-d*e[2]],f=unit([i[0]-r[0],i[1]-r[1],i[2]-r[2]]),u=2*dot(f,m);m=[m[0]-u*f[0],m[1]-u*f[1],m[2]-u*f[2]],h[s]=new n(v,unit(m),unit(i))}else h[s]=h[s-1]}return h}function tube(t,e,i,r,n,s,o){let h=rmf(t[0],t[1],t[2],t[3],[0,1/3,2/3,1]),l=a*e,c=[[e,0],[e,l],[l,e],[0,e]];function d(e,a,o,l){let d=Array(16);for(let i=0;i<4;++i){let r=h[i],n=r.r[0],s=r.s[0],m=n*e+s*a,f=n*o+s*l;n=r.r[1],s=r.s[1];let u=n*e+s*a,p=n*o+s*l;n=r.r[2],s=r.s[2];let v=n*e+s*a,g=n*o+s*l,x=t[i],w=x[0];w1=x[1],w2=x[2];for(let t=0;t<4;++t){let e=c[t],a=e[0],r=e[1];d[4*i+t]=[m*a+f*r+w,u*a+p*r+w1,v*a+g*r+w2]}}P.push(new BezierPatch(d,i,r,n,s))}d(1,0,0,1),d(0,-1,1,0),d(-1,0,0,-1),d(0,1,-1,0),o&&P.push(new BezierCurve(t,i,r,n,s))}function webGLStart(){canvas=document.getElementById("Asymptote"),embedded=window.top.document!=document,initGL(),gl.enable(gl.BLEND),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA),gl.enable(gl.DEPTH_TEST),gl.enable(gl.SCISSOR_TEST),canvas.onmousedown=handleMouseDown,document.onmouseup=handleMouseUpOrTouchEnd,document.onmousemove=handleMouseMove,canvas.onkeydown=handleKey,embedded||enableZoom(),canvas.addEventListener("touchstart",handleTouchStart,!1),canvas.addEventListener("touchend",handleMouseUpOrTouchEnd,!1),canvas.addEventListener("touchcancel",handleMouseUpOrTouchEnd,!1),canvas.addEventListener("touchleave",handleMouseUpOrTouchEnd,!1),canvas.addEventListener("touchmove",handleTouchMove,!1),document.addEventListener("keydown",handleKey,!1),canvasWidth0=canvasWidth,canvasHeight0=canvasHeight,mat4.identity(rotMat),0!=window.innerWidth&&0!=window.innerHeight&&resize(),window.addEventListener("resize",resize,!1)} diff --git a/Build/source/utils/asymptote/base/x11colors.asy b/Build/source/utils/asymptote/base/x11colors.asy new file mode 100644 index 00000000000..77e0401f06e --- /dev/null +++ b/Build/source/utils/asymptote/base/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); |