summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/base/geometry.asy
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2009-05-16 00:19:13 +0000
committerKarl Berry <karl@freefriends.org>2009-05-16 00:19:13 +0000
commitbab45528d65eaafe68a705dbb2a57075c7b7cbd8 (patch)
tree10b4ae2b5195c8dede153ab89359ec00f55f325f /Build/source/utils/asymptote/base/geometry.asy
parent8643d90372e9c31e0f461c15c596b60a545bd7d3 (diff)
asymptote 1.72 sources (not integrated into build yet)
git-svn-id: svn://tug.org/texlive/trunk@13110 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/utils/asymptote/base/geometry.asy')
-rw-r--r--Build/source/utils/asymptote/base/geometry.asy7150
1 files changed, 7150 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/base/geometry.asy b/Build/source/utils/asymptote/base/geometry.asy
new file mode 100644
index 00000000000..a4860557e1f
--- /dev/null
+++ b/Build/source/utils/asymptote/base/geometry.asy
@@ -0,0 +1,7150 @@
+// Copyright (c) 2007, Philippe Ivaldi.
+// Version: : geometry.asy,v 0.1 2007/09/01 Philippe Ivaldi Exp $
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or (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
+// General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+// COMMENTARY:
+// An Asymptote geometry module.
+
+// THANKS:
+// Special thanks to Olivier Guibé for his help in mathematical issues.
+
+// BUGS:
+
+// CODE:
+
+import math;
+import markers;
+// *=======================================================*
+// *........................HEADER.........................*
+/*<asyxml><variable type="real" signature="epsgeo"><code></asyxml>*/
+real epsgeo=10*sqrt(realEpsilon);/*<asyxml></code><documentation>Variable used in the approximate calculations.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="void" signature="addMargins(picture,real,real,real,real)"><code></asyxml>*/
+void addMargins(picture pic=currentpicture,
+ real lmargin=0, real bmargin=0,
+ real rmargin=lmargin, real tmargin=bmargin,
+ bool rigid=true, bool allObject=true)
+{/*<asyxml></code><documentation>Add margins to 'pic' with respect to
+ the current bounding box of 'pic'.
+ If 'rigid' is false, margins are added iff an infinite curve will
+ be prolonged on the margin.
+ If 'allObject' is false, fixed-size objects (such as labels and
+ arrowheads) will be ignored.</documentation></function></asyxml>*/
+ pair m=allObject ? truepoint(pic,SW) : point(pic,SW);
+ pair M=allObject ? truepoint(pic,NE) : point(pic,NE);
+ if(rigid) {
+ draw(m-inverse(pic.calculateTransform())*(lmargin,bmargin),invisible);
+ draw(M+inverse(pic.calculateTransform())*(rmargin,tmargin),invisible);
+ } else pic.addBox(m,M,-(lmargin,bmargin),(rmargin,tmargin));
+}
+
+real approximate(real t)
+{
+ real ot=t;
+ if(abs(t-ceil(t)) < epsgeo) ot=ceil(t);
+ else if(abs(t-floor(t)) < epsgeo) ot=floor(t);
+ return ot;
+}
+
+real[] approximate(real[] T)
+{
+ return map(approximate,T);
+}
+
+/*<asyxml><function type="real" signature="binomial(real,real)"><code></asyxml>*/
+real binomial(real n, real k)
+{/*<asyxml></code><documentation>Return n!/((n-k)!*k!)</documentation></function></asyxml>*/
+ return gamma(n+1)/(gamma(n-k+1)*gamma(k+1));
+}
+
+/*<asyxml><function type="real" signature="rf(real,real,real)"><code></asyxml>*/
+real rf(real x, real y, real z)
+{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the first kind.
+ x, y, and z must be non negative, and at most one can be zero.</documentation></function></asyxml>*/
+ real ERRTOL=0.0025,
+ TINY=1.5e-38,
+ BIG=3e37,
+ THIRD=1/3,
+ C1=1/24,
+ C2=0.1,
+ C3=3/44,
+ C4=1/14;
+ real alamb,ave,delx,dely,delz,e2,e3,sqrtx,sqrty,sqrtz,xt,yt,zt;
+ if(min(x,y,z) < 0 || min(x+y,x+z,y+z) < TINY ||
+ max(x,y,z) > BIG) abort("rf: invalid arguments.");
+ xt=x;
+ yt=y;
+ zt=z;
+ do {
+ sqrtx=sqrt(xt);
+ sqrty=sqrt(yt);
+ sqrtz=sqrt(zt);
+ alamb=sqrtx*(sqrty+sqrtz)+sqrty*sqrtz;
+ xt=0.25*(xt+alamb);
+ yt=0.25*(yt+alamb);
+ zt=0.25*(zt+alamb);
+ ave=THIRD*(xt+yt+zt);
+ delx=(ave-xt)/ave;
+ dely=(ave-yt)/ave;
+ delz=(ave-zt)/ave;
+ } while(max(fabs(delx),fabs(dely),fabs(delz)) > ERRTOL);
+ e2=delx*dely-delz*delz;
+ e3=delx*dely*delz;
+ return (1.0+(C1*e2-C2-C3*e3)*e2+C4*e3)/sqrt(ave);
+}
+
+/*<asyxml><function type="real" signature="rd(real,real,real)"><code></asyxml>*/
+real rd(real x, real y, real z)
+{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the second kind.
+ x and y must be positive, and at most one can be zero.
+ z must be non negative.</documentation></function></asyxml>*/
+ real ERRTOL=0.0015,
+ TINY=1e-25,
+ BIG=4.5*10.0^21,
+ C1=(3/14),
+ C2=(1/6),
+ C3=(9/22),
+ C4=(3/26),
+ C5=(0.25*C3),
+ C6=(1.5*C4);
+ real alamb,ave,delx,dely,delz,ea,eb,ec,ed,ee,fac,sqrtx,sqrty,
+ sqrtz,sum,xt,yt,zt;
+ if (min(x,y) < 0 || min(x+y,z) < TINY || max(x,y,z) > BIG)
+ abort("rd: invalid arguments");
+ xt=x;
+ yt=y;
+ zt=z;
+ sum=0;
+ fac=1;
+ do {
+ sqrtx=sqrt(xt);
+ sqrty=sqrt(yt);
+ sqrtz=sqrt(zt);
+ alamb=sqrtx*(sqrty+sqrtz)+sqrty*sqrtz;
+ sum += fac/(sqrtz*(zt+alamb));
+ fac=0.25*fac;
+ xt=0.25*(xt+alamb);
+ yt=0.25*(yt+alamb);
+ zt=0.25*(zt+alamb);
+ ave=0.2*(xt+yt+3.0*zt);
+ delx=(ave-xt)/ave;
+ dely=(ave-yt)/ave;
+ delz=(ave-zt)/ave;
+ } while (max(fabs(delx),fabs(dely),fabs(delz)) > ERRTOL);
+ ea=delx*dely;
+ eb=delz*delz;
+ ec=ea-eb;
+ ed=ea-6*eb;
+ ee=ed+ec+ec;
+ return 3*sum+fac*(1.0+ed*(-C1+C5*ed-C6*delz*ee)
+ +delz*(C2*ee+delz*(-C3*ec+delz*C4*ea)))/(ave*sqrt(ave));
+}
+
+/*<asyxml><function type="real" signature="elle(real,real)"><code></asyxml>*/
+real elle(real phi, real k)
+{/*<asyxml></code><documentation>Legendre elliptic integral of the 2nd kind,
+ evaluated using Carlson's functions RD and RF.
+ The argument ranges are -infinity < phi < +infinity, 0 <= k*sin(phi) <= 1.</documentation></function></asyxml>*/
+ real result;
+ if (phi >= 0 && phi <= pi/2) {
+ real cc,q,s;
+ s=sin(phi);
+ cc=cos(phi)^2;
+ q=(1-s*k)*(1+s*k);
+ result=s*(rf(cc,q,1)-(s*k)^2*rd(cc,q,1)/3);
+ } else
+ if (phi <= pi && phi >= 0) {
+ result=2*elle(pi/2,k)-elle(pi-phi,k);
+ } else
+ if (phi <= 3*pi/2 && phi >= 0) {
+ result=2*elle(pi/2,k)+elle(phi-pi,k);
+ } else
+ if (phi <= 2*pi && phi >= 0) {
+ result=4*elle(pi/2,k)-elle(2*pi-phi,k);
+ } else
+ if (phi >= 0) {
+ int nb=floor(0.5*phi/pi);
+ result=nb*elle(2*pi,k)+elle(phi%(2*pi),k);
+ } else result=-elle(-phi,k);
+ return result;
+}
+
+/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real,real,real,real,real,real)"><code></asyxml>*/
+pair[] intersectionpoints(pair A, pair B,
+ real a, real b, real c, real d, real f, real g)
+{/*<asyxml></code><documentation>Intersection points with the line (AB) and the quadric curve
+ a*x^2+b*x*y+c*y^2+d*x+f*y+g=0 given in the default coordinate system</documentation></function></asyxml>*/
+ pair[] op;
+ real ap=B.y-A.y,
+ bpp=A.x-B.x,
+ cp=A.y*B.x-A.x*B.y;
+ real sol[];
+ if (abs(ap) > epsgeo) {
+ real aa=ap*c+a*bpp^2/ap-b*bpp,
+ bb=ap*f-bpp*d+2*a*bpp*cp/ap-b*cp,
+ cc=ap*g-cp*d+a*cp^2/ap;
+ sol=quadraticroots(aa,bb,cc);
+ for (int i=0; i<sol.length; ++i) {
+ op.push((-bpp*sol[i]/ap-cp/ap,sol[i]));
+ }
+ } else {
+ real aa=a*bpp,
+ bb=d*bpp-b*cp,
+ cc=g*bpp-cp*f+c*cp^2/bpp;
+ sol=quadraticroots(aa,bb,cc);
+ for (int i=0; i<sol.length; ++i) {
+ op.push((sol[i],-cp/bpp));
+ }
+ }
+ return op;
+}
+
+/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real[])"><code></asyxml>*/
+pair[] intersectionpoints(pair A, pair B, real[] equation)
+{/*<asyxml></code><documentation>Return the intersection points of the line AB with
+ the conic whose an equation is
+ equation[0]*x^2+equation[1]*x*y+equation[2]*y^2+equation[3]*x+equation[4]*y+equation[5]=0</documentation></function></asyxml>*/
+ if(equation.length != 6) abort("intersectionpoints: bad length of array for a conic equation.");
+ return intersectionpoints(A, B, equation[0], equation[1], equation[2],
+ equation[3], equation[4], equation[5]);
+}
+// *........................HEADER.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *......................COORDINATES......................*
+// Copyright (c) 2007, Philippe Ivaldi.
+// Version: $Id: coordinates.asy,v 0.0 2007/02/03 16:06:23 Philippe Ivaldi Exp$
+// Last modified: Wed Aug 15 15:53:01 CEST 2007
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// any later version.
+
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+real EPS=sqrt(realEpsilon);
+
+/*<asyxml><typedef type="convert" return="pair" params="pair"><code></asyxml>*/
+typedef pair convert(pair);/*<asyxml></code><documentation>Function type to convert pair in an other coordinate system.</documentation></typedef></asyxml>*/
+/*<asyxml><typedef type="abs" return="real" params="pair"><code></asyxml>*/
+typedef real abs(pair);/*<asyxml></code><documentation>Function type to calculate modulus of pair.</documentation></typedef></asyxml>*/
+/*<asyxml><typedef type="dot" return="real" params="pair,pair"><code></asyxml>*/
+typedef real dot(pair,pair);/*<asyxml></code><documentation>Function type to calculate dot product.</documentation></typedef></asyxml>*/
+/*<asyxml><typedef type="polar" return="pair" params="real,real"><code></asyxml>*/
+typedef pair polar(real,real);/*<asyxml></code><documentation>Function type to calculate the coordinates from the polar coordinates.</documentation></typedef></asyxml>*/
+
+/*<asyxml><struct signature="coordsys"><code></asyxml>*/
+struct coordsys
+{/*<asyxml></code><documentation>This structure represents a coordinate system in the plane.</documentation></asyxml>*/
+ /*<asyxml><method type="pair" signature="relativetodefault(pair)"><code></asyxml>*/
+ restricted convert relativetodefault=new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to this coordinate system to
+ the pair relatively to the default coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type="pair" signature="defaulttorelativet(pair)"><code></asyxml>*/
+ restricted convert defaulttorelative=new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to the default coordinate system to
+ the pair relatively to this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type="real" signature="dot(pair,pair)"><code></asyxml>*/
+ restricted dot dot=new real(pair m, pair n){return dot(m,n);};/*<asyxml></code><documentation>Return the dot product of this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type="real" signature="abs(pair)"><code></asyxml>*/
+ restricted abs abs=new real(pair m){return abs(m);};/*<asyxml></code><documentation>Return the modulus of a pair in this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><method type="pair" signature="polar(real,real)"><code></asyxml>*/
+ restricted polar polar=new pair(real r, real a){return (r*cos(a),r*sin(a));};/*<asyxml></code><documentation>Polar coordinates routine of this coordinate system.</documentation></method></asyxml>*/
+ /*<asyxml><property type="pair" signature="O, i, j"><code></asyxml>*/
+ restricted pair O=(0,0), i=(1,0), j=(0,1);/*<asyxml></code><documentation>Origin and units vector.</documentation></property></asyxml>*/
+ /*<asyxml><method type="void" signature="init(convert,convert,polar,dot)"><code></asyxml>*/
+ void init(convert rtd, convert dtr,
+ polar polar, dot dot)
+ {/*<asyxml></code><documentation>The default constructor of the coordinate system.</documentation></method></asyxml>*/
+ this.relativetodefault=rtd;
+ this.defaulttorelative=dtr;
+ this.polar=polar;
+ this.dot=dot;
+ this.abs=new real(pair m){return sqrt(dot(m,m));};;
+ this.O=rtd((0,0));
+ this.i=rtd((1,0))-O;
+ this.j=rtd((0,1))-O;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><operator type="bool" signature="==(coordsys,coordsys)"><code></asyxml>*/
+bool operator ==(coordsys c1, coordsys c2)
+{/*<asyxml></code><documentation>Return true iff the coordinate system have the same origin and units vector.</documentation></operator></asyxml>*/
+ return c1.O == c2.O && c1.i == c2.i && c1.j == c2.j;
+}
+
+/*<asyxml><function type="coordsys" signature="cartesiansystem(pair,pair,pair)"><code></asyxml>*/
+coordsys cartesiansystem(pair O=(0,0), pair i, pair j)
+{/*<asyxml></code><documentation>Return the Cartesian coordinate system (O, i, j).</documentation></function></asyxml>*/
+ coordsys R;
+ real[][] P={{0,0},{0,0}};
+ real[][] iP;
+ P[0][0]=i.x;
+ P[0][1]=j.x;
+ P[1][0]=i.y;
+ P[1][1]=j.y;
+ iP=inverse(P);
+ real ni=abs(i);
+ real nj=abs(j);
+ real ij=angle(j)-angle(i);
+
+ pair rtd(pair m)
+ {
+ return O+(P[0][0]*m.x+P[0][1]*m.y,P[1][0]*m.x+P[1][1]*m.y);
+ }
+
+ pair dtr(pair m)
+ {
+ m-=O;
+ return (iP[0][0]*m.x+iP[0][1]*m.y,iP[1][0]*m.x+iP[1][1]*m.y);
+ }
+
+ pair polar(real r, real a)
+ {
+ real ca=sin(ij-a)/(ni*sin(ij));
+ real sa=sin(a)/(nj*sin(ij));
+ return r*(ca,sa);
+ }
+
+ real tdot(pair m, pair n)
+ {
+ return m.x*n.x*ni^2+m.y*n.y*nj^2+(m.x*n.y+n.x*m.y)*dot(i,j);
+ }
+
+ R.init(rtd,dtr,polar,tdot);
+ return R;
+}
+
+
+/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,coordsys,pen,pen,pen,pen,pen)"><code></asyxml>*/
+void show(picture pic=currentpicture, Label lo="$O$",
+ Label li="$\vec{\imath}$",
+ Label lj="$\vec{\jmath}$",
+ coordsys R,
+ pen dotpen=currentpen, pen xpen=currentpen, pen ypen=xpen,
+ pen ipen=red,
+ pen jpen=ipen,
+ arrowbar arrow=Arrow)
+{/*<asyxml></code><documentation>Draw the components (O, i, j, x-axis, y-axis) of 'R'.</documentation></function></asyxml>*/
+ unravel R;
+ dot(pic,O,dotpen);
+ drawline(pic,O,O+i,xpen);
+ drawline(pic,O,O+j,ypen);
+ draw(pic,li,O--(O+i),ipen,arrow);
+ Label lj=lj.copy();
+ lj.align(lj.align,unit(I*j));
+ draw(pic,lj,O--(O+j),jpen,arrow);
+ draw(pic,lj,O--(O+j),jpen,arrow);
+ Label lo=lo.copy();
+ lo.align(lo.align,-2*dir(O--O+i,O--O+j));
+ lo.p(dotpen);
+ label(pic,lo,O);
+}
+
+/*<asyxml><operator type="pair" signature="/(pair,coordsys)"><code></asyxml>*/
+pair operator /(pair p, coordsys R)
+{/*<asyxml></code><documentation>Return the xy-coordinates of 'p' relatively to
+ the coordinate system 'R'.
+ For example, if R=cartesiansystem((1,2),(1,0),(0,1)), (0,0)/R is (-1,-2).</documentation></operator></asyxml>*/
+ return R.defaulttorelative(p);
+}
+
+/*<asyxml><operator type="pair" signature="*(coordsys,pair)"><code></asyxml>*/
+pair operator *(coordsys R, pair p)
+{/*<asyxml></code><documentation>Return the coordinates of 'p' given in the
+ xy-coordinates 'R'.
+ For example, if R=cartesiansystem((1,2),(1,0),(0,1)), R*(0,0) is (1,2).</documentation></operator></asyxml>*/
+ return R.relativetodefault(p);
+}
+
+/*<asyxml><operator type="path" signature="*(coordsys,path)"><code></asyxml>*/
+path operator *(coordsys R, path g)
+{/*<asyxml></code><documentation>Return the reconstructed path applying R*pair to each node, pre and post control point of 'g'.</documentation></operator></asyxml>*/
+ guide og=point(g,0);
+ real l=length(g);
+ for(int i=1; i <= l; ++i)
+ {
+ pair P=R*point(g,i);
+ pair post=R*postcontrol(g,i-1);
+ pair pre=R*precontrol(g,i);
+ if(i == l && (cyclic(g)))
+ og=og..controls post and pre..cycle;
+ else
+ og=og..controls post and pre..P;
+ }
+ return og;
+}
+
+/*<asyxml><operator type="coordsys" signature="*(transform,coordsys)"><code></asyxml>*/
+coordsys operator *(transform t, coordsys R)
+{/*<asyxml></code><documentation>Provide transform*coordsys.
+ Note that shiftless(t) is applied to R.i and R.j.</documentation></operator></asyxml>*/
+ coordsys oc;
+ oc=cartesiansystem(t*R.O,shiftless(t)*R.i,shiftless(t)*R.j);
+ return oc;
+}
+
+/*<asyxml><constant type="coordsys" signature="defaultcoordsys"><code></asyxml>*/
+restricted coordsys defaultcoordsys=cartesiansystem(0,(1,0),(0,1));/*<asyxml></code><documentation>One can always refer to the default coordinate system using this constant.</documentation></constant></asyxml>*/
+/*<asyxml><variable type="coordsys" signature="currentcoordsys"><code></asyxml>*/
+coordsys currentcoordsys=defaultcoordsys;/*<asyxml></code><documentation>The coordinate system used by default.</documentation></variable></asyxml>*/
+
+/*<asyxml><struct signature="point"><code></asyxml>*/
+struct point
+{/*<asyxml></code><documentation>This structure replaces the pair to embed its coordinate system.
+ For example, if 'P=point(cartesiansystem((1,2),i,j), (0,0))',
+ P is equal to the pair (1,2).</documentation></asyxml>*/
+ /*<asyxml><property type="coordsys" signature="coordsys"><code></asyxml>*/
+ coordsys coordsys;/*<asyxml></code><documentation>The coordinate system of this point.</documentation></property><property type="pair" signature="coordinates"><code></asyxml>*/
+ restricted pair coordinates;/*<asyxml></code><documentation>The coordinates of this point relatively to the coordinate system 'coordsys'.</documentation></property><property type="real" signature="x,y"><code></asyxml>*/
+ restricted real x, y;/*<asyxml></code><documentation>The xpart and the ypart of 'coordinates'.</documentation></property></asyxml>*/
+ /*<asyxml><method type="" signature="init(coordsys,pair)"><code><property type="real" signature="m"><code></asyxml>*/
+ real m=1;/*<asyxml></code><documentation>Used to cast mass<->point.</documentation></property></asyxml>*/
+ void init(coordsys R, pair coordinates, real mass)
+ {/*<asyxml></code><documentation>The constructor.</documentation></method></asyxml>*/
+ this.coordsys=R;
+ this.coordinates=coordinates;
+ this.x=coordinates.x;
+ this.y=coordinates.y;
+ this.m=mass;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="point" signature="point(coordsys,pair,real)"><code></asyxml>*/
+point point(coordsys R, pair p, real m=1)
+{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the
+ coordinate system 'R' and the mass 'm'.</documentation></function></asyxml>*/
+ point op;
+ op.init(R, p, m);
+ return op;
+}
+
+/*<asyxml><function type="point" signature="point(explicit pair,real)"><code></asyxml>*/
+point point(explicit pair p, real m)
+{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the current
+ coordinate system and the mass 'm'.</documentation></function></asyxml>*/
+ point op;
+ op.init(currentcoordsys, p, m);
+ return op;
+}
+point point(explicit pair p, int m)
+{// Handle ambiguity
+ return point(p,(real)m);
+}
+
+/*<asyxml><function type="point" signature="point(coordsys,explicit point,real)"><code></asyxml>*/
+point point(coordsys R, explicit point M, real m=M.m)
+{/*<asyxml></code><documentation>Return the point of 'R' which has the coordinates of 'M' and the mass 'm'.
+ Do not confuse this routine with the further routine 'changecoordsys'.</documentation></function></asyxml>*/
+ point op;
+ op.init(R, M.coordinates, M.m);
+ return op;
+}
+
+/*<asyxml><function type="point" signature="changecoordsys(coordsys,point)"><code></asyxml>*/
+point changecoordsys(coordsys R, point M)
+{/*<asyxml></code><documentation>Return the point 'M' in the coordinate system 'coordsys'.
+ In other words, the returned point marks the same plot as 'M' does.</documentation></function></asyxml>*/
+ point op;
+ coordsys mco=M.coordsys;
+ op.init(R, R.defaulttorelative(mco.relativetodefault(M.coordinates)), M.m);
+ return op;
+}
+
+/*<asyxml><function type="pair" signature="pair coordinates(point)"><code></asyxml>*/
+pair coordinates(point M)
+{/*<asyxml></code><documentation>Return the coordinates of 'M' in its coordinate system.</documentation></function></asyxml>*/
+ return M.coordinates;
+}
+
+/*<asyxml><function type="bool" signature="bool samecoordsys(bool...point[])"><code></asyxml>*/
+bool samecoordsys(bool warn=true ... point[] M)
+{/*<asyxml></code><documentation>Return true iff all the points have the same coordinate system.
+ If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/
+ bool ret=true;
+ coordsys t=M[0].coordsys;
+ for (int i=1; i < M.length; ++i) {
+ ret=(t == M[i].coordsys);
+ if(!ret) break;
+ t=M[i].coordsys;
+ }
+ if(warn && !ret)
+ write("Warning, the coordinate system of two objects are not the same.
+The operation will be done relatively to the default coordinate system.");
+ return ret;
+}
+
+/*<asyxml><function type="point[]" signature="standardizecoordsys(coordsys,bool...point[])"><code></asyxml>*/
+point[] standardizecoordsys(coordsys R=currentcoordsys,
+ bool warn=true ... point[] M)
+{/*<asyxml></code><documentation>Return the points with the same coordinate system 'R'.
+ If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/
+ point[] op=new point[];
+ op=M;
+ if(!samecoordsys(warn ... M))
+ for (int i=1; i < M.length; ++i)
+ op[i]=changecoordsys(R,M[i]);
+ return op;
+}
+
+/*<asyxml><operator type="pair" signature="cast(point)"><code></asyxml>*/
+pair operator cast(point P)
+{/*<asyxml></code><documentation>Cast point to pair.</documentation></operator></asyxml>*/
+ return P.coordsys.relativetodefault(P.coordinates);
+}
+
+/*<asyxml><operator type="pair[]" signature="cast(point[])"><code></asyxml>*/
+pair[] operator cast(point[] P)
+{/*<asyxml></code><documentation>Cast point[] to pair[].</documentation></operator></asyxml>*/
+ pair[] op;
+ for (int i=0; i<P.length; ++i) {
+ op.push((pair)P[i]);
+ }
+ return op;
+}
+
+/*<asyxml><operator type="point" signature="cast(pair)"><code></asyxml>*/
+point operator cast(pair p)
+{/*<asyxml></code><documentation>Cast pair to point relatively to the current coordinate
+ system 'currentcoordsys'.</documentation></operator></asyxml>*/
+ return point(currentcoordsys,p);
+}
+
+/*<asyxml><operator type="point[]" signature="cast(pair[])"><code></asyxml>*/
+point[] operator cast(pair[] p)
+{/*<asyxml></code><documentation>Cast pair[] to point[] relatively to the current coordinate
+ system 'currentcoordsys'.</documentation></operator></asyxml>*/
+ pair[] op;
+ for (int i=0; i<p.length; ++i) {
+ op.push((point)p[i]);
+ }
+ return op;
+}
+
+/*<asyxml><function type="pair" signature="locate(point)"><code></asyxml>*/
+pair locate(point P)
+{/*<asyxml></code><documentation>Return the coordinates of 'P' in the default coordinate system.</documentation></function></asyxml>*/
+ return P.coordsys*P.coordinates;
+}
+
+/*<asyxml><function type="point" signature="locate(pair)"><code></asyxml>*/
+point locate(pair p)
+{/*<asyxml></code><documentation>Return the point in the current coordinate system 'currentcoordsys'.</documentation></function></asyxml>*/
+ return p; //automatic casting 'pair to point'.
+}
+
+/*<asyxml><operator type="point" signature="*(real, explicit point)"><code></asyxml>*/
+point operator *(real x, explicit point P)
+{/*<asyxml></code><documentation>Multiply the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/
+ return point(P.coordsys,x*P.coordinates,P.m);
+}
+
+/*<asyxml><operator type="point" signature="/(explicit point, real)"><code></asyxml>*/
+point operator /(explicit point P, real x)
+{/*<asyxml></code><documentation>Divide the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/
+ return point(P.coordsys,P.coordinates/x,P.m);
+}
+
+/*<asyxml><operator type="point" signature="/(real,explicit point)"><code></asyxml>*/
+point operator /(real x, explicit point P)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return point(P.coordsys,x/P.coordinates,P.m);
+}
+
+/*<asyxml><operator type="point" signature="-(explicit point)"><code></asyxml>*/
+point operator -(explicit point P)
+{/*<asyxml></code><documentation>-P. The mass is inchanged.</documentation></operator></asyxml>*/
+ return point(P.coordsys,-P.coordinates, P.m);
+}
+
+/*<asyxml><operator type="point" signature="+(explicit point,explicit point)"><code></asyxml>*/
+point operator +(explicit point P1, explicit point P2)
+{/*<asyxml></code><documentation>Provide 'point+point'.
+ If the two points haven't the same coordinate system, a warning is sent and the
+ returned point has the default coordinate system 'defaultcoordsys'.
+ The masses are added.</documentation></operator></asyxml>*/
+ point[] P=standardizecoordsys(P1,P2);
+ coordsys R=P[0].coordsys;
+ return point(R,P[0].coordinates+P[1].coordinates, P1.m+P2.m);
+}
+
+/*<asyxml><operator type="point" signature="+(explicit point,explicit pair)"><code></asyxml>*/
+point operator +(explicit point P1, explicit pair p2)
+{/*<asyxml></code><documentation>Provide 'point+pair'.
+ The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.
+ The mass is not changed.</documentation></operator></asyxml>*/
+ coordsys R=currentcoordsys;
+ return point(R,P1.coordinates+point(R,p2).coordinates, P1.m);
+}
+point operator +(explicit pair p1, explicit point p2)
+{
+ return p2+p1;
+}
+
+/*<asyxml><operator type="point" signature="-(explicit point,explicit point)"><code></asyxml>*/
+point operator -(explicit point P1, explicit point P2)
+{/*<asyxml></code><documentation>Provide 'point-point'.</documentation></operator></asyxml>*/
+ return P1+(-P2);
+}
+
+/*<asyxml><operator type="point" signature="-(explicit point,explicit pair)"><code></asyxml>*/
+point operator -(explicit point P1, explicit pair p2)
+{/*<asyxml></code><documentation>Provide 'point-pair'.
+ The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.</documentation></operator></asyxml>*/
+ return P1+(-p2);
+}
+point operator -(explicit pair p1, explicit point P2)
+{
+ return p1+(-P2);
+}
+
+/*<asyxml><operator type="point" signature="*(transform,explicit point)"><code></asyxml>*/
+point operator *(transform t, explicit point P)
+{/*<asyxml></code><documentation>Provide 'transform*point'.
+ Note that the transforms scale, xscale, yscale and rotate are carried out relatively
+ the default coordinate system 'defaultcoordsys' which is not desired for point
+ defined in an other coordinate system.
+ On can use scale(real,point), xscale(real,point), yscale(real,point), rotate(real,point),
+ scaleO(real), xscaleO(real), yscaleO(real) and rotateO(real) (described further)
+ to change the coordinate system of reference.</documentation></operator></asyxml>*/
+ coordsys R=P.coordsys;
+ return point(R,(t*locate(P))/R, P.m);
+}
+
+/*<asyxml><operator type="point" signature="*(explicit point,explicit point)"><code></asyxml>*/
+point operator *(explicit point P1, explicit point P2)
+{/*<asyxml></code><documentation>Provide 'point*point'.
+ The resulted mass is the mass of P2</documentation></operator></asyxml>*/
+ point[] P=standardizecoordsys(P1,P2);
+ coordsys R=P[0].coordsys;
+ return point(R,P[0].coordinates*P[1].coordinates, P2.m);
+}
+
+/*<asyxml><operator type="point" signature="*(explicit point,explicit pair)"><code></asyxml>*/
+point operator *(explicit point P1, explicit pair p2)
+{/*<asyxml></code><documentation>Provide 'point*pair'.
+ The pair 'p2' is supposed to be the coordinates of
+ the point in the coordinates system of 'P1'.
+ 'pair*point' is also defined.</documentation></operator></asyxml>*/
+ point P=point(P1.coordsys,p2, P1.m);
+ return P1*P;
+}
+point operator *(explicit pair p1, explicit point p2)
+{
+ return p2*p1;
+}
+
+/*<asyxml><operator type="bool" signature="==(explicit point, explicit point)"><code></asyxml>*/
+bool operator ==(explicit point M, explicit point N)
+{/*<asyxml></code><documentation>Provide the test 'M == N' wish returns true iff MN < EPS</documentation></operator></asyxml>*/
+ return abs(locate(M)-locate(N)) < EPS;
+}
+
+/*<asyxml><operator type="bool" signature="!=(explicit point, explicit point)"><code></asyxml>*/
+bool operator !=(explicit point M, explicit point N)
+{/*<asyxml></code><documentation>Provide the test 'M != N' wish return true iff MN >= EPS</documentation></operator></asyxml>*/
+ return !(M == N);
+}
+
+/*<asyxml><operator type="guide" signature="cast(point)"><code></asyxml>*/
+guide operator cast(point p)
+{/*<asyxml></code><documentation>Cast point to guide.</documentation></operator></asyxml>*/
+ return locate(p);
+}
+
+/*<asyxml><operator type="path" signature="cast(point)"><code></asyxml>*/
+path operator cast(point p)
+{/*<asyxml></code><documentation>Cast point to path.</documentation></operator></asyxml>*/
+ return locate(p);
+}
+
+/*<asyxml><function type="void" signature="dot(picture,Label,explicit point,align,string,pen)"><code></asyxml>*/
+void dot(picture pic=currentpicture, Label L, explicit point Z,
+ align align=NoAlign,
+ string format=defaultformat, pen p=currentpen)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ Label L=L.copy();
+ L.position(locate(Z));
+ if(L.s == "") {
+ if(format == "") format=defaultformat;
+ L.s="("+format(format,Z.x)+","+format(format,Z.y)+")";
+ }
+ L.align(align,E);
+ L.p(p);
+ dot(pic,locate(Z),p);
+ add(pic,L);
+}
+
+/*<asyxml><function type="real" signature="abs(coordsys,pair)"><code></asyxml>*/
+real abs(coordsys R, pair m)
+{/*<asyxml></code><documentation>Return the modulus |m| in the coordinate system 'R'.</documentation></function></asyxml>*/
+ return R.abs(m);
+}
+
+/*<asyxml><function type="real" signature="abs(explicit point)"><code></asyxml>*/
+real abs(explicit point M)
+{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system.</documentation></function></asyxml>*/
+ return M.coordsys.abs(M.coordinates);
+}
+
+/*<asyxml><function type="real" signature="length(explicit point)"><code></asyxml>*/
+real length(explicit point M)
+{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system (same as 'abs').</documentation></function></asyxml>*/
+ return M.coordsys.abs(M.coordinates);
+}
+
+/*<asyxml><function type="point" signature="conj(explicit point)"><code></asyxml>*/
+point conj(explicit point M)
+{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
+ return point(M.coordsys,conj(M.coordinates), M.m);
+}
+
+/*<asyxml><function type="real" signature="degrees(explicit point,coordsys,bool)"><code></asyxml>*/
+real degrees(explicit point M, coordsys R=M.coordsys, bool warn=true)
+{/*<asyxml></code><documentation>Return the angle of M (in degrees) relatively to 'R'.</documentation></function></asyxml>*/
+ return (degrees(locate(M)-R.O, warn) - degrees(R.i))%360;
+}
+
+/*<asyxml><function type="real" signature="angle(explicit point,coordsys,bool)"><code></asyxml>*/
+real angle(explicit point M, coordsys R=M.coordsys, bool warn=true)
+{/*<asyxml></code><documentation>Return the angle of M (in radians) relatively to 'R'.</documentation></function></asyxml>*/
+ return radians(degrees(M,R,warn));
+}
+
+/*<asyxml><function type="bool" signature="finite(explicit point)"><code></asyxml>*/
+bool finite(explicit point p)
+{/*<asyxml></code><documentation>Avoid to compute 'finite((pair)(infinite_point))'.</documentation></function></asyxml>*/
+ return finite(p.coordinates);
+}
+
+/*<asyxml><function type="real" signature="dot(point,point)"><code></asyxml>*/
+real dot(point A, point B)
+{/*<asyxml></code><documentation>Return the dot product in the coordinate system of 'A'.</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A.coordsys,A,B);
+ return P[0].coordsys.dot(P[0].coordinates,P[1].coordinates);
+}
+
+/*<asyxml><function type="real" signature="dot(point,explicit pair)"><code></asyxml>*/
+real dot(point A, explicit pair B)
+{/*<asyxml></code><documentation>Return the dot product in the default coordinate system.
+ dot(explicit pair,point) is also defined.</documentation></function></asyxml>*/
+ return dot(locate(A),B);
+}
+real dot(explicit pair A, point B)
+{
+ return dot(A,locate(B));
+}
+
+/*<asyxml><function type="transforms" signature="rotateO(real)"><code></asyxml>*/
+transform rotateO(real a)
+{/*<asyxml></code><documentation>Rotation around the origin of the current coordinate system.</documentation></function></asyxml>*/
+ return rotate(a,currentcoordsys.O);
+};
+
+/*<asyxml><function type="transform" signature="projection(point,point)"><code></asyxml>*/
+transform projection(point A, point B)
+{/*<asyxml></code><documentation>Return the orthogonal projection on the line (AB).</documentation></function></asyxml>*/
+ pair dir=unit(locate(A)-locate(B));
+ pair a=locate(A);
+ real cof=dir.x*a.x+dir.y*a.y;
+ real tx=a.x-dir.x*cof;
+ real txx=dir.x^2;
+ real txy=dir.x*dir.y;
+ real ty=a.y-dir.y*cof;
+ real tyx=txy;
+ real tyy=dir.y^2;
+ transform t=(tx,ty,txx,txy,tyx,tyy);
+ return t;
+}
+
+/*<asyxml><function type="transform" signature="projection(point,point,point,point,bool)"><code></asyxml>*/
+transform projection(point A, point B, point C, point D, bool safe=false)
+{/*<asyxml></code><documentation>Return the (CD) parallel projection on (AB).
+ If 'safe=true' and (AB)//(CD) return the identity.
+ If 'safe=false' and (AB)//(CD) return an infinity scaling.</documentation></function></asyxml>*/
+ pair a=locate(A);
+ pair u=unit(locate(B)-locate(A));
+ pair v=unit(locate(D)-locate(C));
+ real c=u.x*a.y-u.y*a.x;
+ real d=(conj(u)*v).y;
+ if (abs(d) < epsgeo) {
+ return safe ? identity() : scale(infinity);
+ }
+ real tx=c*v.x/d;
+ real ty=c*v.y/d;
+ real txx=u.x*v.y/d;
+ real txy=-u.x*v.x/d;
+ real tyx=u.y*v.y/d;
+ real tyy=-u.y*v.x/d;
+ transform t=(tx,ty,txx,txy,tyx,tyy);
+ return t;
+}
+
+/*<asyxml><function type="transform" signature="scale(real,point)"><code></asyxml>*/
+transform scale(real k, point M)
+{/*<asyxml></code><documentation>Homothety.</documentation></function></asyxml>*/
+ pair P=locate(M);
+ return shift(P)*scale(k)*shift(-P);
+}
+
+/*<asyxml><function type="transform" signature="xscale(real,point)"><code></asyxml>*/
+transform xscale(real k, point M)
+{/*<asyxml></code><documentation>xscale from 'M' relatively to the x-axis of the coordinate system of 'M'.</documentation></function></asyxml>*/
+ pair P=locate(M);
+ real a=degrees(M.coordsys.i);
+ return (shift(P)*rotate(a))*xscale(k)*(rotate(-a)*shift(-P));
+}
+
+/*<asyxml><function type="transform" signature="yscale(real,point)"><code></asyxml>*/
+transform yscale(real k, point M)
+{/*<asyxml></code><documentation>yscale from 'M' relatively to the y-axis of the coordinate system of 'M'.</documentation></function></asyxml>*/
+ pair P=locate(M);
+ real a=degrees(M.coordsys.j)-90;
+ return (shift(P)*rotate(a))*yscale(k)*(rotate(-a)*shift(-P));
+}
+
+/*<asyxml><function type="transform" signature="scale(real,point,point,point,point,bool)"><code></asyxml>*/
+transform scale(real k, point A, point B, point C, point D, bool safe=false)
+{/*<asyxml></code><documentation><url href="http://fr.wikipedia.org/wiki/Affinit%C3%A9_%28math%C3%A9matiques%29"/>
+ (help me for English translation...)
+ If 'safe=true' and (AB)//(CD) return the identity.
+ If 'safe=false' and (AB)//(CD) return a infinity scaling.</documentation></function></asyxml>*/
+ pair a=locate(A);
+ pair u=unit(locate(B)-locate(A));
+ pair v=unit(locate(D)-locate(C));
+ real c=u.x*a.y-u.y*a.x;
+ real d=(conj(u)*v).y;
+ real d=(conj(u)*v).y;
+ if (abs(d) < epsgeo) {
+ return safe ? identity() : scale(infinity);
+ }
+ real tx=(1-k)*c*v.x/d;
+ real ty=(1-k)*c*v.y/d;
+ real txx=(1-k)*u.x*v.y/d+k;
+ real txy=(k-1)*u.x*v.x/d;
+ real tyx=(1-k)*u.y*v.y/d;
+ real tyy=(k-1)*u.y*v.x/d+k;
+ transform t=(tx,ty,txx,txy,tyx,tyy);
+ return t;
+}
+
+/*<asyxml><function type="transform" signature="scaleO(real)"><code></asyxml>*/
+transform scaleO(real x)
+{/*<asyxml></code><documentation>Homothety from the origin of the current coordinate system.</documentation></function></asyxml>*/
+ return scale(x,(0,0));
+}
+
+/*<asyxml><function type="transform" signature="xscaleO(real)"><code></asyxml>*/
+transform xscaleO(real x)
+{/*<asyxml></code><documentation>xscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/
+ return scale(x,(0,0),(0,1),(0,0),(1,0));
+}
+
+/*<asyxml><function type="transform" signature="yscaleO(real)"><code></asyxml>*/
+transform yscaleO(real x)
+{/*<asyxml></code><documentation>yscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/
+ return scale(x,(0,0),(1,0),(0,0),(0,1));
+}
+
+/*<asyxml><struct signature="vector"><code></asyxml>*/
+struct vector
+{/*<asyxml></code><documentation>Like a point but casting to pair, adding etc does not take account
+ of the origin of the coordinate system.</documentation><property type="point" signature="v"><code></asyxml>*/
+ point v;/*<asyxml></code><documentation>Coordinates as a point (embed coordinate system and pair).</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><operator type="point" signature="cast(vector)"><code></asyxml>*/
+point operator cast(vector v)
+{/*<asyxml></code><documentation>Cast vector 'v' to point 'M' so that OM=v.</documentation></operator></asyxml>*/
+ return v.v;
+}
+
+/*<asyxml><operator type="vector" signature="cast(pair)"><code></asyxml>*/
+vector operator cast(pair v)
+{/*<asyxml></code><documentation>Cast pair to vector relatively to the current coordinate
+ system 'currentcoordsys'.</documentation></operator></asyxml>*/
+ vector ov;
+ ov.v=point(currentcoordsys,v);
+ return ov;
+}
+
+/*<asyxml><operator type="vector" signature="cast(explicit point)"><code></asyxml>*/
+vector operator cast(explicit point v)
+{/*<asyxml></code><documentation>A point can be interpreted like a vector using the code
+ '(vector)a_point'.</documentation></operator></asyxml>*/
+ vector ov;
+ ov.v=v;
+ return ov;
+}
+
+/*<asyxml><operator type="pair" signature="cast(explicit vector)"><code></asyxml>*/
+pair operator cast(explicit vector v)
+{/*<asyxml></code><documentation>Cast vector to pair (the coordinates of 'v' in the default coordinate system).</documentation></operator></asyxml>*/
+ return locate(v.v)-v.v.coordsys.O;
+}
+
+/*<asyxml><operator type="align" signature="cast(vector)"><code></asyxml>*/
+align operator cast(vector v)
+{/*<asyxml></code><documentation>Cast vector to align.</documentation></operator></asyxml>*/
+ return (pair)v;
+}
+
+/*<asyxml><function type="vector" signature="vector(coordsys,pair)"><code></asyxml>*/
+vector vector(coordsys R=currentcoordsys, pair v)
+{/*<asyxml></code><documentation>Return the vector of 'R' which has the coordinates 'v'.</documentation></function></asyxml>*/
+ vector ov;
+ ov.v=point(R,v);
+ return ov;
+}
+
+/*<asyxml><function type="vector" signature="vector(point)"><code></asyxml>*/
+vector vector(point M)
+{/*<asyxml></code><documentation>Return the vector OM, where O is the origin of the coordinate system of 'M'.
+ Useful to write 'vector(P-M);' instead of '(vector)(P-M)'.</documentation></function></asyxml>*/
+ return M;
+}
+
+/*<asyxml><function type="point" signature="point(explicit vector)"><code></asyxml>*/
+point point(explicit vector u)
+{/*<asyxml></code><documentation>Return the point M so that OM=u, where O is the origin of the coordinate system of 'u'.</documentation></function></asyxml>*/
+ return u.v;
+}
+
+/*<asyxml><function type="pair" signature="locate(explicit vector)"><code></asyxml>*/
+pair locate(explicit vector v)
+{/*<asyxml></code><documentation>Return the coordinates of 'v' in the default coordinate system (like casting vector to pair).</documentation></function></asyxml>*/
+ return (pair)v;
+}
+
+/*<asyxml><function type="void" signature="show(Label,pen,arrowbar)"><code></asyxml>*/
+void show(Label L, vector v, pen p=currentpen, arrowbar arrow=Arrow)
+{/*<asyxml></code><documentation>Draw the vector v (from the origin of its coordinate system).</documentation></function></asyxml>*/
+ coordsys R=v.v.coordsys;
+ draw(L, R.O--v.v, p, arrow);
+}
+
+/*<asyxml><function type="vector" signature="changecoordsys(coordsys,vector)"><code></asyxml>*/
+vector changecoordsys(coordsys R, vector v)
+{/*<asyxml></code><documentation>Return the vector 'v' relatively to coordinate system 'R'.</documentation></function></asyxml>*/
+ vector ov;
+ ov.v=point(R,(locate(v)+R.O)/R);
+ return ov;
+}
+
+/*<asyxml><operator type="vector" signature="*(real,explicit vector)"><code></asyxml>*/
+vector operator *(real x, explicit vector v)
+{/*<asyxml></code><documentation>Provide real*vector.</documentation></operator></asyxml>*/
+ return x*v.v;
+}
+
+/*<asyxml><operator type="vector" signature="/(explicit vector,real)"><code></asyxml>*/
+vector operator /(explicit vector v, real x)
+{/*<asyxml></code><documentation>Provide vector/real</documentation></operator></asyxml>*/
+ return v.v/x;
+}
+
+/*<asyxml><operator type="vector" signature="*(transform t,explicit vector)"><code></asyxml>*/
+vector operator *(transform t, explicit vector v)
+{/*<asyxml></code><documentation>Provide transform*vector.</documentation></operator></asyxml>*/
+ return t*v.v;
+}
+
+/*<asyxml><operator type="vector" signature="*(explicit point,explicit vector)"><code></asyxml>*/
+vector operator *(explicit point M, explicit vector v)
+{/*<asyxml></code><documentation>Provide point*vector</documentation></operator></asyxml>*/
+ return M*v.v;
+}
+
+/*<asyxml><operator type="point" signature="+(explicit point,explicit vector)"><code></asyxml>*/
+point operator +(point M, explicit vector v)
+{/*<asyxml></code><documentation>Return 'M' shifted by 'v'.</documentation></operator></asyxml>*/
+ return shift(locate(v))*M;
+}
+
+/*<asyxml><operator type="point" signature="-(explicit point, explicit vector)"><code></asyxml>*/
+point operator -(point M, explicit vector v)
+{/*<asyxml></code><documentation>Return 'M' shifted by '-v'.</documentation></operator></asyxml>*/
+ return shift(-locate(v))*M;
+}
+
+/*<asyxml><operator type="vector" signature="-(explicit vector)"><code></asyxml>*/
+vector operator -(explicit vector v)
+{/*<asyxml></code><documentation>Provide -v.</documentation></operator></asyxml>*/
+ return -v.v;
+}
+
+/*<asyxml><operator type="point" signature="+(explicit pair,explicit vector)"><code></asyxml>*/
+point operator +(explicit pair m, explicit vector v)
+{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of
+ a point in the current coordinates system 'currentcoordsys'.
+ Return this point shifted by the vector 'v'.</documentation></operator></asyxml>*/
+ return locate(m)+v;
+}
+
+/*<asyxml><operator type="point" signature="-(explicit pair,explicit vector)"><code></asyxml>*/
+point operator -(explicit pair m, explicit vector v)
+{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of
+ a point in the current coordinates system 'currentcoordsys'.
+ Return this point shifted by the vector '-v'.</documentation></operator></asyxml>*/
+ return m+(-v);
+}
+
+/*<asyxml><operator type="vector" signature="+(explicit vector, explicit vector)"><code></asyxml>*/
+vector operator +(explicit vector v1, explicit vector v2)
+{/*<asyxml></code><documentation>Provide vector+vector.
+ If the two vector haven't the same coordinate system, the returned
+ vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/
+ coordsys R=v1.v.coordsys;
+ if(samecoordsys(false,v1,v2)){R=defaultcoordsys;}
+ return vector(R,(locate(v1)+locate(v2))/R);
+}
+
+/*<asyxml><operator type="vector" signature="-(explicit vector, explicit vector)"><code></asyxml>*/
+vector operator -(explicit vector v1, explicit vector v2)
+{/*<asyxml></code><documentation>Provide vector-vector.
+ If the two vector haven't the same coordinate system, the returned
+ vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/
+ return v1+(-v2);
+}
+
+/*<asyxml><operator type="bool" signature="==(explicit vector, explicit vector)"><code></asyxml>*/
+bool operator ==(explicit vector u, explicit vector v)
+{/*<asyxml></code><documentation>Return true iff |u-v|<EPS.</documentation></operator></asyxml>*/
+ return abs(u-v) < EPS;
+}
+
+/*<asyxml><function type="bool" signature="collinear(vector,vector)"><code></asyxml>*/
+bool collinear(vector u, vector v)
+{/*<asyxml></code><documentation>Return 'true' iff the vectors 'u' and 'v' are collinear.</documentation></function></asyxml>*/
+ return abs(ypart((conj((pair)u)*(pair)v))) < EPS;
+}
+
+/*<asyxml><function type="vector" signature="unit(point)"><code></asyxml>*/
+vector unit(point M)
+{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/
+ return M/abs(M);
+}
+
+/*<asyxml><function type="vector" signature="unit(vector)"><code></asyxml>*/
+vector unit(vector u)
+{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/
+ return u.v/abs(u.v);
+}
+
+/*<asyxml><function type="real" signature="degrees(vector,coordsys,bool)"><code></asyxml>*/
+real degrees(vector v,
+ coordsys R=v.v.coordsys,
+ bool warn=true)
+{/*<asyxml></code><documentation>Return the angle of 'v' (in degrees) relatively to 'R'.</documentation></function></asyxml>*/
+ return (degrees(locate(v),warn)-degrees(R.i))%360;
+}
+
+/*<asyxml><function type="real" signature="angle(vector,coordsys,bool)"><code></asyxml>*/
+real angle(explicit vector v,
+ coordsys R=v.v.coordsys,
+ bool warn=true)
+{/*<asyxml></code><documentation>Return the angle of 'v' (in radians) relatively to 'R'.</documentation></function></asyxml>*/
+ return radians(degrees(v,R,warn));
+}
+
+/*<asyxml><function type="vector" signature="conj(explicit vector)"><code></asyxml>*/
+vector conj(explicit vector u)
+{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
+ return conj(u.v);
+}
+
+/*<asyxml><function type="transform" signature="rotate(explicit vector)"><code></asyxml>*/
+transform rotate(explicit vector dir)
+{/*<asyxml></code><documentation>A rotation in the direction 'dir' limited to [-90,90]
+This is useful for rotating text along a line in the direction dir.
+rotate(explicit point dir) is also defined.
+</documentation></function></asyxml>*/
+ return rotate(locate(dir));
+}
+transform rotate(explicit point dir){return rotate(locate(vector(dir)));}
+// *......................COORDINATES......................*
+// *=======================================================*
+
+// *=======================================================*
+// *.........................BASES.........................*
+/*<asyxml><variable type="point" signature="origin"><code></asyxml>*/
+point origin=point(defaultcoordsys,(0,0));/*<asyxml></code><documentation>The origin of the current coordinate system.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="point" signature="origin(coordsys)"><code></asyxml>*/
+point origin(coordsys R=currentcoordsys)
+{/*<asyxml></code><documentation>Return the origin of the coordinate system 'R'.</documentation></function></asyxml>*/
+ return point(R,(0,0)); //use automatic casting;
+}
+
+/*<asyxml><variable type="real" signature="linemargin"><code></asyxml>*/
+real linemargin=0;/*<asyxml></code><documentation>Margin used to draw lines.</documentation></variable></asyxml>*/
+/*<asyxml><function type="real" signature="linemargin()"><code></asyxml>*/
+real linemargin()
+{/*<asyxml></code><documentation>Return the margin used to draw lines.</documentation></function></asyxml>*/
+ return linemargin;
+}
+
+/*<asyxml><variable type="pen" signature="addpenline"><code></asyxml>*/
+pen addpenline=squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of "finish" lines.</documentation></variable></asyxml>*/
+pen addpenline(pen p) {
+ return addpenline+p;
+}
+
+/*<asyxml><variable type="pen" signature="addpenarc"><code></asyxml>*/
+pen addpenarc=squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of arcs.</documentation></variable></asyxml>*/
+pen addpenarc(pen p) {return addpenarc+p;}
+
+/*<asyxml><variable type="string" signature="defaultmassformat"><code></asyxml>*/
+string defaultmassformat="$\left(%L;%.4g\right)$";/*<asyxml></code><documentation>Format used to construct the default label of masses.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="int" signature="sgnd(real)"><code></asyxml>*/
+int sgnd(real x)
+{/*<asyxml></code><documentation>Return the -1 if x < 0, 1 if x >= 0.</documentation></function></asyxml>*/
+ return (x == 0) ? 1 : sgn(x);
+}
+int sgnd(int x)
+{
+ return (x == 0) ? 1 : sgn(x);
+}
+
+/*<asyxml><function type="bool" signature="defined(pair)"><code></asyxml>*/
+bool defined(point P)
+{/*<asyxml></code><documentation>Return true iff the coordinates of 'P' are finite.</documentation></function></asyxml>*/
+ return finite(P.coordinates);
+}
+
+/*<asyxml><function type="bool" signature="onpath(picture,path,point,pen)"><code></asyxml>*/
+bool onpath(picture pic=currentpicture, path g, point M, pen p=currentpen)
+{/*<asyxml></code><documentation>Return true iff 'M' is on the path drawn with the pen 'p' in 'pic'.</documentation></function></asyxml>*/
+ transform t=inverse(pic.calculateTransform());
+ return intersect(g, shift(locate(M))*scale(linewidth(p)/2)*t*unitcircle).length > 0;
+}
+
+/*<asyxml><function type="bool" signature="sameside(point,point,point)"><code></asyxml>*/
+bool sameside(point M, point N, point O)
+{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the point 'O'.</documentation></function></asyxml>*/
+ pair m=M, n=N, o=O;
+ return dot(m-o,n-o) >= -epsgeo;
+}
+
+/*<asyxml><function type="bool" signature="between(point,point,point)"><code></asyxml>*/
+bool between(point M, point O, point N)
+{/*<asyxml></code><documentation>Return 'true' iff 'O' is between 'M' and 'N'.</documentation></function></asyxml>*/
+ return (!sameside(N,M,O) || M == O || N == O);
+}
+
+
+typedef path pathModifier(path);
+pathModifier NoModifier=new path(path g){return g;};
+
+private void Drawline(picture pic=currentpicture, Label L="",pair P, bool dirP=true, pair Q, bool dirQ=true,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None,
+ Label legend="", marker marker=nomarker,
+ pathModifier pathModifier=NoModifier)
+{/* Add the two parameters 'dirP' and 'dirQ' to the native routine
+ 'drawline' of the module 'maths'.
+ Segment [PQ] will be prolonged in direction of P if 'dirP=true', in
+ direction of Q if 'dirQ=true'.
+ If 'dirP=dirQ=true', the behavior is that of the native 'drawline'.
+ Add all the other parameters of 'Draw'.*/
+ pic.add(new void (frame f, transform t, transform, pair m, pair M) {
+ picture opic;
+ // Reduce the bounds by the size of the pen.
+ m -= min(p)-(linemargin(),linemargin()); M -= max(p)+(linemargin(),linemargin());
+
+ // Calculate the points and direction vector in the transformed space.
+ pair z=t*P;
+ pair q=t*Q;
+ pair v=t*Q-z;
+ // path g;
+ pair ptp,ptq;
+ real cp = dirP ? 1:0;
+ real cq = dirQ ? 1:0;
+ // Handle horizontal and vertical lines.
+ if(v.x == 0) {
+ if(m.x <= z.x && z.x <= M.x)
+ if (dot(v,(z.x,m.y)) < 0) {
+ ptp=(z.x,z.y+cp*(m.y-z.y));
+ ptq=(z.x,q.y+cq*(M.y-q.y));
+ } else {
+ ptp=(z.x,q.y+cq*(m.y-q.y));
+ ptq=(z.x,z.y+cp*(M.y-z.y));
+ }
+ } else if(v.y == 0) {
+ if (dot(v,(m.x,z.y)) < 0) {
+ ptp=(z.x+cp*(m.x-z.x),z.y);
+ ptq=(q.x+cq*(M.x-q.x),z.y);
+ } else {
+ ptp=(q.x+cq*(m.x-q.x),z.y);
+ ptq=(z.x+cp*(M.x-z.x),z.y);
+ }
+ } else {
+ // Calculate the maximum and minimum t values allowed for the
+ // parametric equation z + t*v
+ real mx=(m.x-z.x)/v.x, Mx=(M.x-z.x)/v.x;
+ real my=(m.y-z.y)/v.y, My=(M.y-z.y)/v.y;
+ real tmin=max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My);
+ real tmax=min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my);
+ pair pmin=z+tmin*v;
+ pair pmax=z+tmax*v;
+ if(tmin <= tmax) {
+ ptp=z+cp*tmin*v;
+ ptq=z+(cq == 0 ? v:tmax*v);
+ }
+ }
+ path g=ptp--ptq;
+ if (length(g)>0)
+ {
+ if(L.s != "") {
+ Label lL=L.copy();
+ if(L.defaultposition) lL.position(Relative(.9));
+ lL.p(p);
+ lL.out(opic,g);
+ }
+ g=pathModifier(g);
+ if(linetype(p) == ""){
+ pair m=midpoint(g);
+ pen tp;
+ tp=dirP ? p : addpenline(p);
+ draw(opic,pathModifier(m--ptp),tp);
+ tp=dirQ ? p : addpenline(p);
+ draw(opic,pathModifier(m--ptq),tp);
+ } else {
+ draw(opic,g,p);
+ }
+ marker.markroutine(opic,marker.f,g);
+ arrow(opic,g,p,NoMargin);
+ add(f,opic.fit());
+ }
+ });
+}
+
+/*<asyxml><function type="void" signature="clipdraw(picture,Label,path,align,pen,arrowbar,arrowbar,real,real,Label,marker)"><code></asyxml>*/
+void clipdraw(picture pic=currentpicture, Label L="", path g,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None,
+ real xmargin=0, real ymargin=xmargin,
+ Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation>Draw the path 'g' on 'pic' clipped to the bounding box of 'pic'.</documentation></function></asyxml>*/
+ if(L.s != "") {
+ picture tmp;
+ label(tmp,L,g,p);
+ add(pic,tmp);
+ }
+ pic.add(new void (frame f, transform t, transform, pair m, pair M) {
+ // Reduce the bounds by the size of the pen and the margins.
+ m += min(p)+(xmargin,ymargin); M -= max(p)+(xmargin,ymargin);
+ path bound=box(m,M);
+ picture tmp;
+ draw(tmp,"",t*g,align,p,arrow,bar,NoMargin,legend,marker);
+ clip(tmp,bound);
+ add(f,tmp.fit());
+ });
+}
+
+/*<asyxml><function type="void" signature="distance(picture pic,Label,point,point,bool,real,pen,pen,arrow)"><code></asyxml>*/
+void distance(picture pic=currentpicture, Label L="", point A, point B,
+ bool rotated=true, real offset=3mm,
+ pen p=currentpen, pen joinpen=invisible,
+ arrowbar arrow=Arrows(NoFill))
+{/*<asyxml></code><documentation>Draw arrow between A and B (from FAQ).</documentation></function></asyxml>*/
+ pair A=A, B=B;
+ path g=A--B;
+ transform Tp=shift(-offset*unit(B-A)*I);
+ pic.add(new void(frame f, transform t) {
+ picture opic;
+ path G=Tp*t*g;
+ transform id=identity();
+ transform T=rotated ? rotate(B-A) : id;
+ Label L=L.copy();
+ L.align(L.align,Center);
+ if(abs(ypart((conj(A-B)*L.align.dir))) < epsgeo && L.filltype == NoFill)
+ L.filltype=UnFill(1);
+ draw(opic,T*L,G,p,arrow,Bars,PenMargins);
+ pair Ap=t*A, Bp=t*B;
+ draw(opic,(Ap--Tp*Ap)^^(Bp--Tp*Bp), joinpen);
+ add(f,opic.fit());
+ }, true);
+ pic.addBox(min(g),max(g),Tp*min(p),Tp*max(p));
+}
+
+/*<asyxml><variable type="real" signature="perpfactor"><code></asyxml>*/
+real perpfactor=1;/*<asyxml></code><documentation>Factor for drawing perpendicular symbol.</documentation></variable></asyxml>*/
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,explicit pair,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic=currentpicture, point z,
+ explicit pair align,
+ explicit pair dir=E, real size=0,
+ pen p=currentpen,
+ margin margin=NoMargin,
+ filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z+dir.
+ dir(45+n*90), where n in N*, are common values for 'align'.</documentation></function></asyxml>*/
+ p=squarecap+p;
+ if(size == 0) size=perpfactor*3mm+sqrt(1+linewidth(p))-1;
+ frame apic;
+ pair d1=size*align*unit(dir)*dir(-45);
+ pair d2=I*d1;
+ path g=d1--d1+d2--d2;
+ g=margin(g,p).g;
+ draw(apic,g,p);
+ if(filltype != NoFill) filltype.fill(apic,(relpoint(g,0)-relpoint(g,0.5)+
+ relpoint(g,1))--g--cycle,p+solid);
+ add(pic,apic,locate(z));
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,vector,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic=currentpicture, point z,
+ vector align,
+ vector dir=E, real size=0,
+ pen p=currentpen,
+ margin margin=NoMargin,
+ filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z+dir.
+ dir(45+n*90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
+ perpendicularmark(pic, z, (pair)align, (pair)dir, size,
+ p, margin, filltype);
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,path,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic=currentpicture, point z, explicit pair align, path g,
+ real size=0, pen p=currentpen,
+ margin margin=NoMargin,
+ filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z+dir(g,0).
+ dir(45+n*90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
+ perpendicularmark(pic,z,align,dir(g,0),size,p,margin,filltype);
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,path,real,pen,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic=currentpicture, point z, vector align, path g,
+ real size=0, pen p=currentpen,
+ margin margin=NoMargin,
+ filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
+ relative to the path z--z+dir(g,0).
+ dir(45+n*90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
+ perpendicularmark(pic,z,(pair)align,dir(g,0),size,p,margin,filltype);
+}
+
+/*<asyxml><function type="void" signature="markrightangle(picture,point,point,point,real,pen,margin,filltype)"><code></asyxml>*/
+void markrightangle(picture pic=currentpicture, point A, point O,
+ point B, real size=0, pen p=currentpen,
+ margin margin=NoMargin,
+ filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Mark the angle AOB with a perpendicular symbol.</documentation></function></asyxml>*/
+ pair Ap=A, Bp=B, Op=O;
+ pair dir=Ap-Op;
+ real a1=degrees(dir);
+ pair align=rotate(-a1)*unit(dir(Op--Ap,Op--Bp));
+ if (margin == NoMargin)
+ margin=TrueMargin(linewidth(currentpen)/2,linewidth(currentpen)/2);
+ perpendicularmark(pic=pic, z=O, align=align,
+ dir=dir, size=size, p=p,
+ margin=margin, filltype=filltype);
+}
+
+/*<asyxml><function type="bool" signature="simeq(point,point,real)"><code></asyxml>*/
+bool simeq(point A, point B, real fuzz=epsgeo)
+{/*<asyxml></code><documentation>Return true iff abs(A-B) < fuzz.
+ This routine is used internally to know if two points are equal, in particular by the operator == in 'point == point'.</documentation></function></asyxml>*/
+ return (abs(A-B) < fuzz);
+}
+bool simeq(point a, real b, real fuzz=epsgeo)
+{
+ coordsys R=a.coordsys;
+ return (abs(a-point(R,((pair)b)/R)) < fuzz);
+}
+
+/*<asyxml><function type="pair" signature="attract(pair,path,real)"><code></asyxml>*/
+pair attract(pair m, path g, real fuzz=0)
+{/*<asyxml></code><documentation>Return the nearest point (A PAIR) of 'm' which is on the path g.
+ 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
+ if(intersect(m,g,fuzz).length > 0) return m;
+ pair p;
+ real step=1, r=0;
+ real[] t;
+ static real eps=sqrt(realEpsilon);
+ do {// Find a radius for intersection
+ r+=step;
+ t=intersect(shift(m)*scale(r)*unitcircle,g);
+ } while(t.length <= 0);
+ p=point(g,t[1]);
+ real rm=0, rM=r;
+ while(rM-rm > eps) {
+ r=(rm+rM)/2;
+ t=intersect(shift(m)*scale(r)*unitcircle,g,fuzz);
+ if(t.length <= 0) {
+ rm=r;
+ } else {
+ rM=r;
+ p=point(g,t[1]);
+ }
+ }
+ return p;
+}
+
+/*<asyxml><function type="point" signature="attract(point,path,real)"><code></asyxml>*/
+point attract(point M, path g, real fuzz=0)
+{/*<asyxml></code><documentation>Return the nearest point (A POINT) of 'M' which is on the path g.
+ 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
+ return point(M.coordsys, attract(locate(M),g)/M.coordsys);
+}
+
+/*<asyxml><function type="real[]" signature="intersect(path,explicit pair)"><code></asyxml>*/
+real[] intersect(path g, explicit pair p, real fuzz=0)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ fuzz=fuzz <= 0 ? sqrt(realEpsilon) : fuzz;
+ real[] or;
+ real r=realEpsilon;
+ do{
+ or=intersect(g,shift(p)*scale(r)*unitcircle,fuzz);
+ r *= 2;
+ } while(or.length == 0);
+ return or;
+}
+
+/*<asyxml><function type="real[]" signature="intersect(path,explicit point)"><code></asyxml>*/
+real[] intersect(path g, explicit point P, real fuzz=epsgeo)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersect(g,locate(P),fuzz);
+}
+// *.........................BASES.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.........................LINES.........................*
+/*<asyxml><struct signature="line"><code></asyxml>*/
+struct line
+{/*<asyxml></code><documentation>This structure provides the objects line, semi-line and segment oriented from A to B.
+ All the calculus with this structure will be as exact as Asymptote can do.
+ For a full precision, you must not cast 'line' to 'path' excepted for drawing routines.</documentation></asyxml>*/
+ /*<asyxml><property type="point" signature="A,B"><code></asyxml>*/
+ restricted point A,B;/*<asyxml></code><documentation>Two line's points with same coordinate system.</documentation></property><property type="bool" signature="extendA,extendB"><code></asyxml>*/
+ bool extendA,extendB;/*<asyxml></code><documentation>If true, extend 'l' in direction of A (resp. B).</documentation></property><property type="vector" signature="u,v"><code></asyxml>*/
+ restricted vector u,v;/*<asyxml></code><documentation>u=unit(AB)=direction vector, v=normal vector.</documentation></property><property type="real" signature="a,b,c"><code></asyxml>*/
+ restricted real a,b,c;/*<asyxml></code><documentation>Coefficients of the equation ax+by+c=0 in the coordinate system of 'A'.</documentation></property><property type="real" signature="slope,origin"><code></asyxml>*/
+ restricted real slope,origin;/*<asyxml></code><documentation>Slope and ordinate at the origin.</documentation></property></asyxml>*/
+ /*<asyxml><method type="line" signature="copy()"><code></asyxml>*/
+ line copy()
+ {/*<asyxml></code><documentation>Copy a line in a new instance.</documentation></method></asyxml>*/
+ line l=new line;
+ l.A=A;
+ l.B=B;
+ l.a=a;
+ l.b=b;
+ l.c=c;
+ l.slope=slope;
+ l.origin=origin;
+ l.u=u;
+ l.v=v;
+ l.extendA=extendA;
+ l.extendB=extendB;
+ return l;
+ }
+
+ /*<asyxml><method type="void" signature="init(point,bool,point,bool)"><code></asyxml>*/
+ void init(point A, bool extendA=true, point B, bool extendB=true)
+ {/*<asyxml></code><documentation>Initialize line.
+ If 'extendA' is true, the "line" is infinite in the direction of A.</documentation></method></asyxml>*/
+ point[] P=standardizecoordsys(A,B);
+ this.A=P[0];
+ this.B=P[1];
+ this.a=B.y-A.y;
+ this.b=A.x-B.x;
+ this.c=A.y*B.x-A.x*B.y;
+ this.slope= (this.b == 0) ? infinity : -this.a/this.b;
+ this.origin=(this.b == 0) ? (this.c==0) ? 0:infinity : -this.c/this.b;
+ this.u=unit(P[1]-P[0]);
+ // int tmp=sgnd(this.slope);
+ // this.u=(dot((pair)this.u,N) >= 0) ? tmp*this.u : -tmp*this.u;
+ this.v=rotate(90,point(P[0].coordsys,(0,0)))*this.u;
+ this.extendA=extendA;
+ this.extendB=extendB;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="line" signature="line(point,bool,point,bool)"><code></asyxml>*/
+line line(point A, bool extendA=true, point B, bool extendB=true)
+{/*<asyxml></code><documentation>Return the line passing through 'A' and 'B'.
+ If 'extendA' is true, the "line" is infinite in the direction of A.
+ A "line" can be half-line or segment.</documentation></function></asyxml>*/
+ if (A == B) abort("line: the points must be distinct.");
+ line l;
+ l.init(A,extendA,B,extendB);
+ return l;
+}
+
+/*<asyxml><struct signature="segment"><code></asyxml>*/
+struct segment
+{/*<asyxml></code><documentation><look href="struct line"/>.</documentation></asyxml>*/
+ restricted point A,B;// Extremity.
+ restricted vector u,v;// u=direction vector, v=normal vector.
+ restricted real a,b,c;// Coefficients of the équation ax+by+c=0
+ restricted real slope,origin;
+ segment copy()
+ {
+ segment s=new segment;
+ s.A=A;
+ s.B=B;
+ s.a=a;
+ s.b=b;
+ s.c=c;
+ s.slope=slope;
+ s.origin=origin;
+ s.u=u;
+ s.v=v;
+ return s;
+ }
+
+ void init(point A, point B)
+ {
+ line l;
+ l.init(A,B);
+ this.A=l.A; this.B=l.B;
+ this.a=l.a; this.b=l.b; this.c=l.c;
+ this.slope=l.slope; this.origin=l.origin;
+ this.u=l.u; this.v=l.v;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="segment" signature="segment(point,point)"><code></asyxml>*/
+segment segment(point A, point B)
+{/*<asyxml></code><documentation>Return the segment whose the extremities are A and B.</documentation></function></asyxml>*/
+ segment s;
+ s.init(A,B);
+ return s;
+}
+
+/*<asyxml><function type="real" signature="length(segment)"><code></asyxml>*/
+real length(segment s)
+{/*<asyxml></code><documentation>Return the length of 's'.</documentation></function></asyxml>*/
+ return abs(s.A-s.B);
+}
+
+/*<asyxml><operator type="line" signature="cast(segment)"><code></asyxml>*/
+line operator cast(segment s)
+{/*<asyxml></code><documentation>A segment is casted to a "finite line".</documentation></operator></asyxml>*/
+ return line(s.A,false,s.B,false);
+}
+
+/*<asyxml><operator type="segment" signature="cast(line)"><code></asyxml>*/
+segment operator cast(line l)
+{/*<asyxml></code><documentation>Cast line 'l' to segment [l.A l.B].</documentation></operator></asyxml>*/
+ return segment(l.A,l.B);
+}
+
+/*<asyxml><operator type="line" signature="*(transform,line)"><code></asyxml>*/
+line operator *(transform t, line l)
+{/*<asyxml></code><documentation>Provide transform*line</documentation></operator></asyxml>*/
+ return line(t*l.A,l.extendA,t*l.B,l.extendB);
+}
+/*<asyxml><operator type="line" signature="/(line,real)"><code></asyxml>*/
+line operator /(line l, real x)
+{/*<asyxml></code><documentation>Provide l/x.
+ Return the line passing through l.A/x and l.B/x.</documentation></operator></asyxml>*/
+ return line(l.A/x,l.extendA,l.B/x,l.extendB);
+}
+line operator /(line l, int x){return line(l.A/x,l.B/x);}
+/*<asyxml><operator type="line" signature="*(real,line)"><code></asyxml>*/
+line operator *(real x, line l)
+{/*<asyxml></code><documentation>Provide x*l.
+ Return the line passing through x*l.A and x*l.B.</documentation></operator></asyxml>*/
+ return line(x*l.A,l.extendA,x*l.B,l.extendB);
+}
+line operator *(int x, line l){return line(x*l.A,l.extendA,x*l.B,l.extendB);}
+
+/*<asyxml><operator type="line" signature="*(point,line)"><code></asyxml>*/
+line operator *(point M, line l)
+{/*<asyxml></code><documentation>Provide point*line.
+ Return the line passing through unit(M)*l.A and unit(M)*l.B.</documentation></operator></asyxml>*/
+ return line(unit(M)*l.A,l.extendA,unit(M)*l.B,l.extendB);
+}
+/*<asyxml><operator type="line" signature="+(line,point)"><code></asyxml>*/
+line operator +(line l, vector u)
+{/*<asyxml></code><documentation>Provide line+vector (and so line+point).
+ Return the line 'l' shifted by 'u'.</documentation></operator></asyxml>*/
+ return line(l.A+u,l.extendA,l.B+u,l.extendB);
+}
+/*<asyxml><operator type="line" signature="-(line,vector)"><code></asyxml>*/
+line operator -(line l, vector u)
+{/*<asyxml></code><documentation>Provide line - vector (and so line - point).
+ Return the line 'l' shifted by '-u'.</documentation></operator></asyxml>*/
+ return line(l.A-u,l.extendA,l.B-u,l.extendB);
+}
+
+/*<asyxml><operator type="line[]" signature="^^(line,line)"><code></asyxml>*/
+line[] operator ^^(line l1, line l2)
+{/*<asyxml></code><documentation>Provide line^^line.
+ Return the line array {l1,l2}.</documentation></operator></asyxml>*/
+ line[] ol;
+ ol.push(l1); ol.push(l2);
+ return ol;
+}
+
+/*<asyxml><operator type="line[]" signature="^^(line,line[])"><code></asyxml>*/
+line[] operator ^^(line l1, line[] l2)
+{/*<asyxml></code><documentation>Provide line^^line[].
+ Return the line array {l1, l2[0], l2[1]...}.
+ line[]^^line is also defined.</documentation></operator></asyxml>*/
+ line[] ol;
+ ol.push(l1);
+ for (int i=0; i<l2.length; ++i) {
+ ol.push(l2[i]);
+ }
+ return ol;
+}
+line[] operator ^^(line[] l2, line l1)
+{
+ line[] ol=l2;
+ ol.push(l1);
+ return ol;
+}
+
+/*<asyxml><operator type="line[]" signature="^^(line,line[])"><code></asyxml>*/
+line[] operator ^^(line l1[], line[] l2)
+{/*<asyxml></code><documentation>Provide line[]^^line[].
+ Return the line array {l1[0], l1[1],..., l2[0], l2[1],...}.</documentation></operator></asyxml>*/
+ line[] ol=l1;
+ for (int i=0; i<l2.length; ++i) {
+ ol.push(l2[i]);
+ }
+ return ol;
+}
+
+/*<asyxml><function type="bool" signature="sameside(point,point,line)"><code></asyxml>*/
+bool sameside(point M, point P, line l)
+{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the line (or on the line) 'l'.</documentation></function></asyxml>*/
+ pair A=l.A, B=l.B, m=M, p=P;
+ pair mil=(A+B)/2;
+ pair mA=rotate(90,mil)*A;
+ pair mB=rotate(-90,mil)*A;
+ return (abs(m-mA) <= abs(m-mB)) == (abs(p-mA) <= abs(p-mB));
+ // transform proj=projection(l.A,l.B);
+ // point Mp=proj*M;
+ // point Pp=proj*P;
+ // dot(Mp);dot(Pp);
+ // return dot(locate(Mp-M),locate(Pp-P)) >= 0;
+}
+
+/*<asyxml><function type="line" signature="line(segment)"><code></asyxml>*/
+line line(segment s)
+{/*<asyxml></code><documentation>Return the line passing through 's.A'
+ and 's.B'.</documentation></function></asyxml>*/
+ return line(s.A,s.B);
+}
+/*<asyxml><function type="segment" signature="segment(line)"><code></asyxml>*/
+segment segment(line l)
+{/*<asyxml></code><documentation>Return the segment whose extremities
+ are 'l.A' and 'l.B'.</documentation></function></asyxml>*/
+ return segment(l.A,l.B);
+}
+
+/*<asyxml><function type="point" signature="midpoint(segment)"><code></asyxml>*/
+point midpoint(segment s)
+{/*<asyxml></code><documentation>Return the midpoint of 's'.</documentation></function></asyxml>*/
+ return 0.5*(s.A+s.B);
+}
+
+/*<asyxml><function type="void" signature="write(line)"><code></asyxml>*/
+void write(explicit line l)
+{/*<asyxml></code><documentation>Write some informations about 'l'.</documentation></function></asyxml>*/
+ write("A="+(string)((pair)l.A));
+ write("Extend A="+(l.extendA ? "true" : "false"));
+ write("B="+(string)((pair)l.B));
+ write("Extend B="+(l.extendB ? "true" : "false"));
+ write("u="+(string)((pair)l.u));
+ write("v="+(string)((pair)l.v));
+ write("a="+(string) l.a);
+ write("b="+(string) l.b);
+ write("c="+(string) l.c);
+ write("slope="+(string) l.slope);
+ write("origin="+(string) l.origin);
+}
+
+/*<asyxml><function type="void" signature="write(explicit segment)"><code></asyxml>*/
+void write(explicit segment s)
+{/*<asyxml></code><documentation>Write some informations about 's'.</documentation></function></asyxml>*/
+ write("A="+(string)((pair)s.A));
+ write("B="+(string)((pair)s.B));
+ write("u="+(string)((pair)s.u));
+ write("v="+(string)((pair)s.v));
+ write("a="+(string) s.a);
+ write("b="+(string) s.b);
+ write("c="+(string) s.c);
+ write("slope="+(string) s.slope);
+ write("origin="+(string) s.origin);
+}
+
+/*<asyxml><operator type="bool" signature="==(line,line)"><code></asyxml>*/
+bool operator ==(line l1, line l2)
+{/*<asyxml></code><documentation>Provide the test 'line == line'.</documentation></operator></asyxml>*/
+ return (collinear(l1.u,l2.u) &&
+ abs(ypart((locate(l1.A)-locate(l1.B))/(locate(l1.A)-locate(l2.B)))) < epsgeo &&
+ l1.extendA == l2.extendA && l1.extendB == l2.extendB);
+}
+
+/*<asyxml><operator type="bool" signature="!=(line,line)"><code></asyxml>*/
+bool operator !=(line l1, line l2)
+{/*<asyxml></code><documentation>Provide the test 'line != line'.</documentation></operator></asyxml>*/
+ return !(l1 == l2);
+}
+
+/*<asyxml><operator type="bool" signature="@(point,line)"><code></asyxml>*/
+bool operator @(point m, line l)
+{/*<asyxml></code><documentation>Provide the test 'point @ line'.
+ Return true iff 'm' is on the 'l'.</documentation></operator></asyxml>*/
+ point M=changecoordsys(l.A.coordsys,m);
+ if (abs(l.a*M.x+l.b*M.y+l.c) >= epsgeo) return false;
+ if (l.extendA && l.extendB) return true;
+ if (!l.extendA && !l.extendB) return between(l.A,M,l.B);
+ if (l.extendA) return sameside(M,l.A,l.B);
+ return sameside(M,l.B,l.A);
+}
+
+/*<asyxml><function type="coordsys" signature="coordsys(line)"><code></asyxml>*/
+coordsys coordsys(line l)
+{/*<asyxml></code><documentation>Return the coordinate system in which 'l' is defined.</documentation></function></asyxml>*/
+ return l.A.coordsys;
+}
+
+/*<asyxml><function type="line" signature="reverse(line)"><code></asyxml>*/
+line reverse(line l)
+{/*<asyxml></code><documentation>Permute the points 'A' and 'B' of 'l' and so its orientation.</documentation></function></asyxml>*/
+ return line(l.B,l.extendB,l.A,l.extendA);
+}
+
+/*<asyxml><function type="line" signature="extend(line)"><code></asyxml>*/
+line extend(line l)
+{/*<asyxml></code><documentation>Return the infinite line passing through 'l.A' and 'l.B'.</documentation></function></asyxml>*/
+ line ol=l.copy();
+ ol.extendA=true;
+ ol.extendB=true;
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="complementary(explicit line)"><code></asyxml>*/
+line complementary(explicit line l)
+{/*<asyxml></code><documentation>Return the complementary of a half-line with respect of
+ the full line 'l'.</documentation></function></asyxml>*/
+ if (l.extendA && l.extendB)
+ abort("complementary: the parameter is not a half-line.");
+ point origin=l.extendA ? l.B : l.A;
+ point ptdir=l.extendA ?
+ rotate(180,l.B)*l.A : rotate(180,l.A)*l.B;
+ return line(origin,false,ptdir);
+}
+
+/*<asyxml><function type="line[]" signature="complementary(explicit segment)"><code></asyxml>*/
+line[] complementary(explicit segment s)
+{/*<asyxml></code><documentation>Return the two half-lines of origin 's.A' and 's.B' respectively.</documentation></function></asyxml>*/
+ line[] ol=new line[2];
+ ol[0]=complementary(line(s.A,false,s.B));
+ ol[1]=complementary(line(s.A,s.B,false));
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="Ox(coordsys)"><code></asyxml>*/
+line Ox(coordsys R=currentcoordsys)
+{/*<asyxml></code><documentation>Return the x-axis of 'R'.</documentation></function></asyxml>*/
+ return line(point(R,(0,0)), point(R,E));
+}
+/*<asyxml><constant type="line" signature="Ox"><code></asyxml>*/
+restricted line Ox=Ox();/*<asyxml></code><documentation>the x-axis of
+ the default coordinate system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="Oy(coordsys)"><code></asyxml>*/
+line Oy(coordsys R=currentcoordsys)
+{/*<asyxml></code><documentation>Return the y-axis of 'R'.</documentation></function></asyxml>*/
+ return line(point(R,(0,0)), point(R,N));
+}
+/*<asyxml><constant type="line" signature="Oy"><code></asyxml>*/
+restricted line Oy=Oy();/*<asyxml></code><documentation>the y-axis of
+ the default coordinate system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="line(real,point)"><code></asyxml>*/
+line line(real a, point A=point(currentcoordsys,(0,0)))
+{/*<asyxml></code><documentation>Return the line passing through 'A' with an
+ angle (in the coordinate system of A) 'a' in degrees.
+ line(point,real) is also defined.</documentation></function></asyxml>*/
+ return line(A, A+point(A.coordsys,A.coordsys.polar(1,radians(a))));
+}
+line line(point A=point(currentcoordsys,(0,0)),real a)
+{
+ return line(a,A);
+}
+line line(int a, point A=point(currentcoordsys,(0,0)))
+{
+ return line((real)a, A);
+}
+
+/*<asyxml><function type="line" signature="line(coordsys,real,real)"><code></asyxml>*/
+line line(coordsys R=currentcoordsys, real slope, real origin)
+{/*<asyxml></code><documentation>Return the line defined by slope and y-intercept relative to 'R'.</documentation></function></asyxml>*/
+ if (slope == infinity || slope == -infinity)
+ abort("The slope is infinite. Please, use the routine 'vline'.");
+ return line(point(R,(0,origin)), point(R,(1,origin+slope)));
+}
+
+/*<asyxml><function type="line" signature="line(coordsys,real,real,real)"><code></asyxml>*/
+line line(coordsys R=currentcoordsys, real a, real b, real c)
+{/*<asyxml></code><documentation>Retrun the line defined by equation relative to 'R'.</documentation></function></asyxml>*/
+ if (a == 0 && b == 0) abort("line: inconsistent equation...");
+ pair M;
+ M=(a == 0) ? (0,-c/b) : (-c/a,0);
+ return line(point(R,M), point(R,M+(-b,a)));
+}
+
+/*<asyxml><function type="line" signature="vline(coordsys)"><code></asyxml>*/
+line vline(coordsys R=currentcoordsys)
+{/*<asyxml></code><documentation>Return a vertical line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/
+ point P=point(R,(0,0));
+ point PP=point(R,(R.O+N)/R);
+ return line(P,PP);
+}
+/*<asyxml><constant type="line" signature="vline"><code></asyxml>*/
+restricted line vline=vline();/*<asyxml></code><documentation>The vertical line in the current coordinate system passing
+ through the origin of this system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="hline(coordsys)"><code></asyxml>*/
+line hline(coordsys R=currentcoordsys)
+{/*<asyxml></code><documentation>Return a horizontal line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/
+ point P=point(R,(0,0));
+ point PP=point(R,(R.O+E)/R);
+ return line(P,PP);
+}
+/*<asyxml><constant type="line" signature="hline"><code></asyxml>*/
+line hline=hline();/*<asyxml></code><documentation>The horizontal line in the current coordinate system passing
+ through the origin of this system.</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="line" signature="changecoordsys(coordsys,line)"><code></asyxml>*/
+line changecoordsys(coordsys R, line l)
+{/*<asyxml></code><documentation>Return the line 'l' in the coordinate system 'R'.</documentation></function></asyxml>*/
+ point A=changecoordsys(R,l.A);
+ point B=changecoordsys(R,l.B);
+ return line(A,B);
+}
+
+/*<asyxml><function type="transform" signature="scale(real,line,line,bool)"><code></asyxml>*/
+transform scale(real k, line l1, line l2, bool safe=false)
+{/*<asyxml></code><documentation>Return the dilatation with respect to
+ 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/
+ return scale(k,l1.A,l1.B,l2.A,l2.B,safe);
+}
+
+/*<asyxml><function type="transform" signature="reflect(line)"><code></asyxml>*/
+transform reflect(line l)
+{/*<asyxml></code><documentation>Return the reflect about the line 'l'.</documentation></function></asyxml>*/
+ return reflect((pair)l.A,(pair)l.B);
+}
+
+/*<asyxml><function type="transform" signature="reflect(line,line)"><code></asyxml>*/
+transform reflect(line l1, line l2, bool safe=false)
+{/*<asyxml></code><documentation>Return the reflect about the line
+ 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/
+ return scale(-1.0,l1,l2,safe);
+}
+
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,path)"><code></asyxml>*/
+point[] intersectionpoints(line l, path g)
+{/*<asyxml></code><documentation>Return all points of intersection of the line 'l' with the path 'g'.</documentation></function></asyxml>*/
+ // TODO utiliser la version 1.44 de intersections(path g, pair p, pair q)
+ // real [] t=intersections(g,l.A,l.B);
+ // coordsys R=coordsys(l);
+ // return sequence(new point(int n){return point(R,point(g,t[n])/R);}, t.length);
+ real [] t;
+ pair[] op;
+ pair A=l.A;
+ pair B=l.B;
+ real dy=B.y-A.y,
+ dx=A.x-B.x,
+ lg=length(g);
+ for (int i=0; i<lg; ++i)
+ {
+ pair z0=point(g,i),
+ z1=point(g,i+1),
+ c0=postcontrol(g,i),
+ c1=precontrol(g,i+1),
+ t3=z1-z0-3*c1+3*c0,
+ t2=3*z0+3*c1-6*c0,
+ t1=3*c0-3z0;
+ real a=dy*t3.x+dx*t3.y,
+ b=dy*t2.x+dx*t2.y,
+ c=dy*t1.x+dx*t1.y,
+ d=dy*z0.x+dx*z0.y+A.y*B.x-A.x*B.y;
+ t=cubicroots(a,b,c,d);
+ for (int j=0; j<t.length; ++j)
+ if (t[j]>=0 && (t[j]<1 || (t[j]==1 && i==lg-1 && !cyclic(g)))) op.push(point(g,i+t[j]));
+ }
+ point[] opp;
+ for (int i=0; i<op.length; ++i)
+ opp.push(point(coordsys(l),op[i]/coordsys(l)));
+ return opp;
+}
+
+/*<asyxml><function type="point" signature="intersectionpoint(line,line)"><code></asyxml>*/
+point intersectionpoint(line l1, line l2)
+{/*<asyxml></code><documentation>Return the point of intersection of line 'l1' with 'l2'.
+ If 'l1' and 'l2' have an infinity or none point of intersection,
+ this routine return (infinity,infinity).</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(l1.A,l1.B,l2.A,l2.B);
+ coordsys R=P[0].coordsys;
+ pair p=extension(P[0],P[1],P[2],P[3]);
+ if(finite(p)){
+ point p=point(R,p/R);
+ if (p @ l1 && p @ l2) return p;
+ }
+ return point(R,(infinity,infinity));
+}
+
+/*<asyxml><function type="line" signature="parallel(point,line)"><code></asyxml>*/
+line parallel(point M, line l)
+{/*<asyxml></code><documentation>Return the line parallel to 'l' passing through 'M'.</documentation></function></asyxml>*/
+ point A,B;
+ if (M.coordsys != coordsys(l))
+ {
+ A=changecoordsys(M.coordsys,l.A);
+ B=changecoordsys(M.coordsys,l.B);
+ } else {A=l.A;B=l.B;}
+ return line(M,M-A+B);
+}
+
+/*<asyxml><function type="line" signature="parallel(point,explicit vector)"><code></asyxml>*/
+line parallel(point M, explicit vector dir)
+{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/
+ return line(M,M+locate(dir));
+}
+
+/*<asyxml><function type="line" signature="parallel(point,explicit pair)"><code></asyxml>*/
+line parallel(point M, explicit pair dir)
+{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/
+ return line(M,M+vector(currentcoordsys,dir));
+}
+
+/*<asyxml><function type="bool" signature="parallel(line,line)"><code></asyxml>*/
+bool parallel(line l1, line l2, bool strictly=false)
+{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are (strictly ?) parallel.</documentation></function></asyxml>*/
+ bool coll=collinear(l1.u,l2.u);
+ return strictly ? coll && (l1 != l2) : coll;
+}
+
+/*<asyxml><function type="bool" signature="concurrent(...line[])"><code></asyxml>*/
+bool concurrent(... line[] l)
+{/*<asyxml></code><documentation>Returns true if all the lines 'l' are concurrent.</documentation></function></asyxml>*/
+ if (l.length < 3) abort("'concurrent' needs at least for three lines ...");
+ pair point=intersectionpoint(l[0],l[1]);
+ bool conc;
+ for (int i=2; i < l.length; ++i) {
+ pair pt=intersectionpoint(l[i-1],l[i]);
+ conc=simeq(pt, point);
+ if (!conc) break;
+ }
+ return conc;
+}
+
+/*<asyxml><function type="transform" signature="projection(line)"><code></asyxml>*/
+transform projection(line l)
+{/*<asyxml></code><documentation>Return the orthogonal projection on 'l'.</documentation></function></asyxml>*/
+ return projection(l.A,l.B);
+}
+
+/*<asyxml><function type="transform" signature="projection(line,line,bool)"><code></asyxml>*/
+transform projection(line l1, line l2, bool safe=false)
+{/*<asyxml></code><documentation>Return the projection on (AB) in parallel of (CD).
+ If 'safe=true' and (l1)//(l2) return the identity.
+ If 'safe=false' and (l1)//(l2) return a infinity scaling.</documentation></function></asyxml>*/
+ return projection(l1.A,l1.B,l2.A,l2.B,safe);
+}
+
+/*<asyxml><function type="transform" signature="vprojection(line,bool)"><code></asyxml>*/
+transform vprojection(line l, bool safe=false)
+{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of N--S.
+ If 'safe' is 'true' the projected point keeps the same place if 'l'
+ is vertical.</documentation></function></asyxml>*/
+ coordsys R=defaultcoordsys;
+ return projection(l, line(point(R,N),point(R,S)), safe);
+}
+
+/*<asyxml><function type="transform" signature="hprojection(line,bool)"><code></asyxml>*/
+transform hprojection(line l, bool safe=false)
+{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of E--W.
+ If 'safe' is 'true' the projected point keeps the same place if 'l'
+ is horizontal.</documentation></function></asyxml>*/
+ coordsys R=defaultcoordsys;
+ return projection(l, line(point(R,E),point(R,W)), safe);
+}
+
+/*<asyxml><function type="line" signature="perpendicular(point,line)"><code></asyxml>*/
+line perpendicular(point M, line l)
+{/*<asyxml></code><documentation>Return the perpendicular line of 'l' passing through 'M'.</documentation></function></asyxml>*/
+ point Mp=projection(l)*M;
+ point A=Mp == l.A ? l.B : l.A;
+ return line(Mp, rotate(90,Mp)*A);
+}
+
+/*<asyxml><function type="line" signature="perpendicular(point,explicit vector)"><code></asyxml>*/
+line perpendicular(point M, explicit vector normal)
+{/*<asyxml></code><documentation>Return the line passing through 'M'
+ whose normal is \param{normal}.</documentation></function></asyxml>*/
+ return perpendicular(M,line(M,M+locate(normal)));
+}
+
+/*<asyxml><function type="line" signature="perpendicular(point,explicit pair)"><code></asyxml>*/
+line perpendicular(point M, explicit pair normal)
+{/*<asyxml></code><documentation>Return the line passing through 'M'
+ whose normal is \param{normal} (given in the currentcoordsys).</documentation></function></asyxml>*/
+ return perpendicular(M,line(M,M+vector(currentcoordsys,normal)));
+}
+
+/*<asyxml><function type="bool" signature="perpendicular(line,line)"><code></asyxml>*/
+bool perpendicular(line l1, line l2)
+{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are perpendicular.</documentation></function></asyxml>*/
+ return abs(dot(locate(l1.u),locate(l2.u))) < epsgeo ;
+}
+
+/*<asyxml><function type="real" signature="angle(line,coordsys)"><code></asyxml>*/
+real angle(line l, coordsys R=coordsys(l))
+{/*<asyxml></code><documentation>Return the angle of the oriented line 'l',
+ in radian, in the interval ]-pi,pi] and relatively to 'R'.</documentation></function></asyxml>*/
+ return angle(l.u, R, false);
+}
+
+/*<asyxml><function type="real" signature="degrees(line,coordsys,bool)"><code></asyxml>*/
+real degrees(line l, coordsys R=coordsys(l))
+{/*<asyxml></code><documentation>Returns the angle of the oriented line 'l' in degrees,
+ in the interval [0,360[ and relatively to 'R'.</documentation></function></asyxml>*/
+ return degrees(angle(l, R));
+}
+
+/*<asyxml><function type="real" signature="sharpangle(line,line)"><code></asyxml>*/
+real sharpangle(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in radians of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/
+ vector u1=l1.u;
+ vector u2=(dot(l1.u,l2.u) < 0) ? -l2.u : l2.u;
+ real a12=angle(locate(u2))-angle(locate(u1));
+ a12=a12%(sgnd(a12)*pi);
+ if (a12 <= -pi/2) a12 += pi; else if (a12 > pi/2) a12 -= pi;
+ return a12;
+}
+
+/*<asyxml><function type="real" signature="angle(line,line)"><code></asyxml>*/
+real angle(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in radians of oriented angle (l1.u,l2.u).</documentation></function></asyxml>*/
+ return angle(locate(l2.u))-angle(locate(l1.u));
+}
+
+/*<asyxml><function type="real" signature="degrees(line,line)"><code></asyxml>*/
+real degrees(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in degrees of the
+ angle formed by the oriented lines 'l1' and 'l2'.</documentation></function></asyxml>*/
+ return degrees(angle(l1,l2));
+}
+
+/*<asyxml><function type="real" signature="sharpdegrees(line,line)"><code></asyxml>*/
+real sharpdegrees(line l1, line l2)
+{/*<asyxml></code><documentation>Return the measure in degrees of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/
+ return degrees(sharpangle(l1,l2));
+}
+
+/*<asyxml><function type="line" signature="bisector(line,line,real,bool)"><code></asyxml>*/
+line bisector(line l1, line l2, real angle=0, bool sharp=true)
+{/*<asyxml></code><documentation>Return the bisector of the angle formed by 'l1' and 'l2'
+ rotated by the angle 'angle' (in degrees) around intersection point of 'l1' with 'l2'.
+ If 'sharp' is true (the default), this routine returns the bisector of the sharp angle.
+ Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/
+ line ol;
+ if (l1 == l2) return l1;
+ point A=intersectionpoint(l1,l2);
+ if (finite(A)) {
+ if(sharp) ol=rotate(sharpdegrees(l1,l2)/2+angle,A)*l1;
+ else {
+ coordsys R=coordsys(l1);
+ pair a=A, b=A+l1.u, c=A+l2.u;
+ pair pp=extension(a, a+dir(a--b,a--c), b, b+dir(b--a,b--c));
+ return rotate(angle,A)*line(A,point(R,pp/R));
+ }
+ } else {
+ ol=l1;
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="sector(int,int,line,line,real,bool)"><code></asyxml>*/
+line sector(int n=2, int p=1, line l1, line l2, real angle=0, bool sharp=true)
+{/*<asyxml></code><documentation>Return the p-th nth-sector of the angle
+ formed by the oriented line 'l1' and 'l2'
+ rotated by the angle 'angle' (in degrees) around the intersection point of 'l1' with 'l2'.
+ If 'sharp' is true (the default), this routine returns the bisector of the sharp angle.
+ Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/
+ line ol;
+ if (l1 == l2) return l1;
+ point A=intersectionpoint(l1,l2);
+ if (finite(A)) {
+ if(sharp) ol=rotate(p*sharpdegrees(l1,l2)/n+angle,A)*l1;
+ else {
+ ol=rotate(p*degrees(l1,l2)/n+angle,A)*l1;
+ }
+ } else {
+ ol=l1;
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="bisector(point,point,point,point,real)"><code></asyxml>*/
+line bisector(point A, point B, point C, point D, real angle=0, bool sharp=true)
+{/*<asyxml></code><documentation>Return the bisector of the angle formed by the lines (AB) and (CD).
+ <look href="#bisector(line,line,real,bool)"/>.</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B,C,D);
+ return bisector(line(P[0],P[1]),line(P[2],P[3]),angle,sharp);
+}
+
+/*<asyxml><function type="line" signature="bisector(segment,real)"><code></asyxml>*/
+line bisector(segment s, real angle=0)
+{/*<asyxml></code><documentation>Return the bisector of the segment line 's' rotated by 'angle' (in degrees) around the
+ midpoint of 's'.</documentation></function></asyxml>*/
+ coordsys R=coordsys(s);
+ point m=midpoint(s);
+ vector dir=rotateO(90)*unit(s.A-m);
+ return rotate(angle,m)*line(m+dir,m-dir);
+}
+
+/*<asyxml><function type="line" signature="bisector(point,point,real)"><code></asyxml>*/
+line bisector(point A, point B, real angle=0)
+{/*<asyxml></code><documentation>Return the bisector of the segment line [AB] rotated by 'angle' (in degrees) around the
+ midpoint of [AB].</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B);
+ return bisector(segment(P[0],P[1]),angle);
+}
+
+/*<asyxml><function type="real" signature="distance(point,line)"><code></asyxml>*/
+real distance(point M, line l)
+{/*<asyxml></code><documentation>Return the distance from 'M' to 'l'.
+ distance(line,point) is also defined.</documentation></function></asyxml>*/
+ point A=changecoordsys(defaultcoordsys,l.A);
+ point B=changecoordsys(defaultcoordsys,l.B);
+ line ll=line(A,B);
+ pair m=locate(M);
+ return abs(ll.a*m.x+ll.b*m.y+ll.c)/sqrt(ll.a^2+ll.b^2);
+}
+
+real distance(line l, point M)
+{
+ return distance(M,l);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,line,bool,bool,align,pen,arrowbar,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="",
+ line l, bool dirA=l.extendA, bool dirB=l.extendB,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None,
+ Label legend="", marker marker=nomarker,
+ pathModifier pathModifier=NoModifier)
+{/*<asyxml></code><documentation>Draw the line 'l' without altering the size of picture pic.
+ The boolean parameters control the infinite section.
+ The global variable 'linemargin' (default value is 0) allows to modify
+ the bounding box in which the line must be drawn.</documentation></function></asyxml>*/
+ if(!(dirA || dirB)) draw(l.A--l.B, invisible);// l is a segment.
+ Drawline(pic, L, l.A, dirP=dirA, l.B, dirQ=dirB,
+ align, p, arrow,
+ legend, marker, pathModifier);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label[],line[],align,pen[],arrowbar,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture,Label[] L=new Label[], line[] l,
+ align align=NoAlign, pen[] p=new pen[],
+ arrowbar arrow=None,
+ Label[] legend=new Label[], marker marker=nomarker,
+ pathModifier pathModifier=NoModifier)
+{/*<asyxml></code><documentation>Draw each lines with the corresponding pen.</documentation></function></asyxml>*/
+ for (int i=0; i < l.length; ++i) {
+ draw(pic, L.length>0 ? L[i] : "", l[i],
+ align, p=p.length>0 ? p[i] : currentpen,
+ arrow, legend.length>0 ? legend[i] : "", marker,
+ pathModifier);
+ }
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label[],line[],align,pen,arrowbar,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture,Label[] L=new Label[], line[] l,
+ align align=NoAlign, pen p,
+ arrowbar arrow=None,
+ Label[] legend=new Label[], marker marker=nomarker,
+ pathModifier pathModifier=NoModifier)
+{/*<asyxml></code><documentation>Draw each lines with the same pen 'p'.</documentation></function></asyxml>*/
+ pen[] tp=sequence(new pen(int i){return p;},l.length);
+ draw(pic, L, l, align, tp, arrow, legend, marker, pathModifier);
+}
+
+/*<asyxml><function type="void" signature="show(picture,line,pen)"><code></asyxml>*/
+void show(picture pic=currentpicture, line l, pen p=red)
+{/*<asyxml></code><documentation>Draw some informations of 'l'.</documentation></function></asyxml>*/
+ dot("$A$",(pair)l.A,align=-locate(l.v),p);
+ dot("$B$",(pair)l.B,align=-locate(l.v),p);
+ draw(l,dotted);
+ draw("$\vec{u}$",locate(l.A)--locate(l.A+l.u),p,Arrow);
+ draw("$\vec{v}$",locate(l.A)--locate(l.A+l.v),p,Arrow);
+}
+
+/*<asyxml><function type="point[]" signature="sameside(point,line,line)"><code></asyxml>*/
+point[] sameside(point M, line l1, line l2)
+{/*<asyxml></code><documentation>Return two points on 'l1' and 'l2' respectively.
+ The first point is from the same side of M relatively to 'l2',
+ the second point is from the same side of M relatively to 'l1'.</documentation></function></asyxml>*/
+ point[] op;
+ coordsys R1=coordsys(l1);
+ coordsys R2=coordsys(l2);
+ if (parallel(l1,l2)) {
+ op.push(projection(l1)*M);
+ op.push(projection(l2)*M);
+ } else {
+ point O=intersectionpoint(l1,l2);
+ if (M @ l2) op.push((sameside(M,O+l1.u,l2)) ? O+l1.u : rotate(180,O)*(O+l1.u));
+ else op.push(projection(l1,l2)*M);
+ if (M @ l1) op.push((sameside(M,O+l2.u,l1)) ? O+l2.u : rotate(180,O)*(O+l2.u));
+ else {op.push(projection(l2,l1)*M);}
+ }
+ return op;
+}
+
+// /*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,explicit pair,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
+// void markangle(picture pic=currentpicture,
+// Label L="", int n=1, real radius=0, real space=0,
+// line l1, line l2, explicit pair align=dir(1),
+// arrowbar arrow=None, pen p=currentpen,
+// filltype filltype=NoFill,
+// margin margin=NoMargin, marker marker=nomarker)
+// {/*<asyxml></code><documentation>Mark the angle (l1,l2) aligned in the direction 'align' relative to 'l1'.
+// Commune values for 'align' are dir(real).</documentation></function></asyxml>*/
+// if (parallel(l1,l2,true)) return;
+// real al=degrees(l1,defaultcoordsys);
+// pair O,A,B;
+// if (radius == 0) radius=markangleradius(p);
+// real d=degrees(locate(l1.u));
+// align=rotate(d)*align;
+// if (l1 == l2) {
+// O=midpoint(segment(l1.A,l1.B));
+// A=l1.A;B=l1.B;
+// if (sameside(rotate(sgn(angle(B-A))*45,O)*A,O+align,l1)) {radius=-radius;}
+// } else {
+// O=intersectionpoint(extend(l1),extend(l2));
+// pair R=O+align;
+// point [] ss=sameside(point(coordsys(l1),R/coordsys(l1)),l1,l2);
+// A=ss[0];
+// B=ss[1];
+// }
+// markangle(pic=pic,L=L,n=n,radius=radius,space=space,
+// O=O,A=A,B=B,
+// arrow=arrow,p=p,filltype=filltype,
+// margin=margin,marker=marker);
+// }
+
+// /*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,explicit vector,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
+// void markangle(picture pic=currentpicture,
+// Label L="", int n=1, real radius=0, real space=0,
+// line l1, line l2,explicit vector align,
+// arrowbar arrow=None, pen p=currentpen,
+// filltype filltype=NoFill,
+// margin margin=NoMargin, marker marker=nomarker)
+// {/*<asyxml></code><documentation>Mark the angle (l1,l2) in the direction 'dir' given relatively to 'l1'.</documentation></function></asyxml>*/
+// markangle(pic, L, n, radius, space, l1, l2, (pair)align, arrow,
+// p, filltype, margin, marker);
+// }
+
+/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
+void markangle(picture pic=currentpicture,
+ Label L="", int n=1, real radius=0, real space=0,
+ line l1, line l2,
+ arrowbar arrow=None, pen p=currentpen,
+ filltype filltype=NoFill,
+ margin margin=NoMargin, marker marker=nomarker)
+{/*<asyxml></code><documentation>Mark the oriented angle (l1,l2).</documentation></function></asyxml>*/
+ if (parallel(l1,l2,true)) return;
+ real al=degrees(l1,defaultcoordsys);
+ pair O,A,B;
+ if (radius == 0) radius=markangleradius(p);
+ real d=degrees(locate(l1.u));
+ if (l1 == l2) {
+ O=midpoint(segment(l1.A,l1.B));
+ } else {
+ O=intersectionpoint(extend(l1),extend(l2));
+ }
+ A=O+locate(l1.u);
+ B=O+locate(l2.u);
+ markangle(pic=pic,L=L,n=n,radius=radius,space=space,
+ O=O,A=A,B=B,
+ arrow=arrow,p=p,filltype=filltype,
+ margin=margin,marker=marker);
+}
+
+/*<asyxml><function type="void" signature="perpendicularmark(picture,line,line,real,pen,int,margin,filltype)"><code></asyxml>*/
+void perpendicularmark(picture pic=currentpicture, line l1, line l2,
+ real size=0, pen p=currentpen, int quarter=1,
+ margin margin=NoMargin, filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw a right angle at the intersection point of lines and
+ aligned in the 'quarter' nth quarter of circle formed by 'l1.u' and
+ 'l2.u'.</documentation></function></asyxml>*/
+ point P=intersectionpoint(l1,l2);
+ pair align=rotate(90*(quarter-1))*dir(45);
+ perpendicularmark(P,align,locate(l1.u),size,p,margin,filltype);
+}
+// *.........................LINES.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *........................CONICS.........................*
+/*<asyxml><struct signature="bqe"><code></asyxml>*/
+struct bqe
+{/*<asyxml></code><documentation>Bivariate Quadratic Equation.</documentation></asyxml>*/
+ /*<asyxml><property type="real[]" signature="a"><code></asyxml>*/
+ real[] a;/*<asyxml></code><documentation>a[0]*x^2 + a[1]*x*y + a[2]*y^2 + a[3]*x + a[4]*y + a[5]=0</documentation></property><property type="coordsys" signature="coordsys"><code></asyxml>*/
+ coordsys coordsys;/*<asyxml></code></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="bqe" signature="bqe(coordsys,real,real,real,real,real,real)"><code></asyxml>*/
+bqe bqe(coordsys R=currentcoordsys,
+ real a, real b, real c, real d, real e, real f)
+{/*<asyxml></code><documentation>Return the bivariate quadratic equation
+ a[0]*x^2 + a[1]*x*y + a[2]*y^2 + a[3]*x + a[4]*y + a[5]=0
+ relatively to the coordinate system R.</documentation></function></asyxml>*/
+ bqe obqe;
+ obqe.coordsys=R;
+ obqe.a=new real[] {a,b,c,d,e,f};
+ return obqe;
+}
+
+/*<asyxml><function type="bqe" signature="changecoordsys(coordsys,bqe)"><code></asyxml>*/
+bqe changecoordsys(coordsys R, bqe bqe)
+{/*<asyxml></code><documentation>Returns the bivariate quadratic equation relatively to 'R'.</documentation></function></asyxml>*/
+ pair i=coordinates(changecoordsys(R,vector(defaultcoordsys,
+ bqe.coordsys.i)));
+ pair j=coordinates(changecoordsys(R,vector(defaultcoordsys,
+ bqe.coordsys.j)));
+ pair O=coordinates(changecoordsys(R,point(defaultcoordsys,
+ bqe.coordsys.O)));
+ real a=bqe.a[0], b=bqe.a[1], c=bqe.a[2], d=bqe.a[3], f=bqe.a[4], g=bqe.a[5];
+ real ux=i.x, uy=i.y;
+ real vx=j.x, vy=j.y;
+ real ox=O.x, oy=O.y;
+ real D=ux*vy-uy*vx;
+ real ap=(a*vy^2-b*uy*vy+c*uy^2)/D^2;
+ real bpp=(-2*a*vx*vy+b*ux*vy+b*uy*vx-2*c*ux*uy)/D^2;
+ real cp=(a*vx^2-b*ux*vx+c*ux^2)/D^2;
+ real dp=(-2a*ox*vy^2+2a*oy*vx*vy+2b*ox*uy*vy-
+ b*oy*ux*vy-b*oy*uy*vx-2c*ox*uy^2+2c*oy*uy*ux)/D^2+
+ (d*vy-f*uy)/D;
+ real fp=(2a*ox*vx*vy-b*ox*ux*vy-2a*oy*vx^2-
+ b*ox*uy*vx+2*b*oy*ux*vx+2c*ox*ux*uy-2c*oy*ux^2)/D^2+
+ (f*ux-d*vx)/D;
+ g=(a*ox^2*vy^2-2a*ox*oy*vx*vy-b*ox^2*uy*vy+b*ox*oy*ux*vy+
+ a*oy^2*vx^2+b*ox*oy*uy*vx-b*oy^2*ux*vx+c*ox^2*uy^2-
+ 2*c*ox*oy*ux*uy+c*oy^2*ux^2)/D^2+
+ (d*oy*vx+f*ox*uy-d*ox*vy-f*oy*ux)/D+g;
+ bqe obqe;
+ obqe.a=approximate(new real[] {ap,bpp,cp,dp,fp,g});
+ obqe.coordsys=R;
+ return obqe;
+}
+
+/*<asyxml><function type="bqe" signature="bqe(point,point,point,point,point)"><code></asyxml>*/
+bqe bqe(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the bqe of conic passing through the five points (if possible).</documentation></function></asyxml>*/
+ coordsys R;
+ pair[] pts;
+ if (samecoordsys(M1,M2,M3,M4,M5)) {
+ R=M1.coordsys;
+ pts= new pair[] {M1.coordinates,M2.coordinates,M3.coordinates,M4.coordinates,M5.coordinates};
+ } else {
+ R=defaultcoordsys;
+ pts= new pair[] {M1,M2,M3,M4,M5};
+ }
+ real[][] M;
+ real[] x;
+ bqe bqe;
+ bqe.coordsys=R;
+ for (int i=0; i < 5; ++i) {// Try a=-1
+ M[i]=new real[] {pts[i].x*pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1};
+ x[i]=pts[i].x^2;
+ }
+ if(abs(determinant(M)) < 1e-5) {// Try c=-1
+ for (int i=0; i < 5; ++i) {
+ M[i]=new real[] {pts[i].x^2, pts[i].x*pts[i].y, pts[i].x, pts[i].y, 1};
+ x[i]=pts[i].y^2;
+ }
+ real[] coef=solve(M,x);
+ bqe.a=new real[] {coef[0],coef[1],-1,coef[2],coef[3],coef[4]};
+ } else {
+ real[] coef=solve(M,x);
+ bqe.a=new real[] {-1,coef[0],coef[1],coef[2],coef[3],coef[4]};
+ }
+ bqe.a=approximate(bqe.a);
+ return bqe;
+}
+
+/*<asyxml><function type="bool" signature="samecoordsys(bool...bqe[])"><code></asyxml>*/
+bool samecoordsys(bool warn=true ... bqe[] bqes)
+{/*<asyxml></code><documentation>Return true if all the bivariate quadratic equations have the same coordinate system.</documentation></function></asyxml>*/
+ bool ret=true;
+ coordsys t=bqes[0].coordsys;
+ for (int i=1; i < bqes.length; ++i) {
+ ret=(t == bqes[i].coordsys);
+ if(!ret) break;
+ t=bqes[i].coordsys;
+ }
+ if(warn && !ret)
+ write("Warning, the coordinate system of two bivariate quadratic equations are not the same.
+The operation will be done relatively to the default coordinate system.");
+ return ret;
+}
+
+/*<asyxml><function type="real[]" signature="realquarticroots(real,real,real,real,real)"><code></asyxml>*/
+real[] realquarticroots(real a, real b, real c, real d, real e)
+{/*<asyxml></code><documentation>Return the real roots of the quartic equation ax^4 + b^x3 + cx^2 + dx = 0.</documentation></function></asyxml>*/
+ static real Fuzz=sqrt(realEpsilon);
+ pair[] zroots=quarticroots(a, b, c, d, e);
+ real[] roots;
+ real p(real x){return a*x^4+b*x^3+c*x^2+d*x+e;}
+ real prime(real x){return 4*a*x^3+3*b*x^2+2*c*x+d;}
+ real x;
+ bool search=true;
+ int n;
+ void addroot(real x)
+ {
+ bool exist=false;
+ for (int i=0; i < roots.length; ++i) {
+ if(abs(roots[i]-x) < 1e-5) {exist=true; break;}
+ }
+ if(!exist) roots.push(x);
+ }
+ for(int i=0; i < zroots.length; ++i) {
+ if(zroots[i].y == 0 || abs(p(zroots[i].x)) < Fuzz) addroot(zroots[i].x);
+ else {
+ if(abs(zroots[i].y) < 1e-3) {
+ x=zroots[i].x;
+ search=true;
+ n=200;
+ while(search) {
+ real tx=abs(p(x)) < Fuzz ? x : newton(iterations=n, p, prime, x);
+ if(tx < realMax) {
+ if(abs(p(tx)) < Fuzz) {
+ addroot(tx);
+ search=false;
+ } else if(n < 200) n *=2;
+ else {
+ search=false;
+ }
+ } else search=false; //It's not a real root.
+ }
+ }
+ }
+ }
+ return roots;
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(bqe,bqe)"><code></asyxml>*/
+point[] intersectionpoints(bqe bqe1, bqe bqe2)
+{/*<asyxml></code><documentation>Return the interscetion of the two conic sections whose equations are 'bqe1' and 'bqe2'.</documentation></function></asyxml>*/
+ coordsys R=bqe1.coordsys;
+ bqe lbqe1,lbqe2;
+ real[] a, b;
+ if(R != bqe2.coordsys) {
+ R=currentcoordsys;
+ a=changecoordsys(R, bqe1).a;
+ b=changecoordsys(R, bqe2).a;
+ } else {
+ a=bqe1.a;
+ b=bqe2.a;
+ }
+ static real e=100*sqrt(realEpsilon);
+ real[] x,y,c;
+ point[] P;
+ if(abs(a[0]-b[0]) > e || abs(a[1]-b[1]) > e || abs(a[2]-b[2]) > e) {
+ c=new real[] {-2*a[0]*a[2]*b[0]*b[2]+a[0]*a[2]*b[1]^2-a[0]*a[1]*b[2]*b[1]+a[1]^2*b[0]*b[2]-
+ a[2]*a[1]*b[0]*b[1]+a[0]^2*b[2]^2+a[2]^2*b[0]^2,
+ -a[2]*a[1]*b[0]*b[4]-a[2]*a[4]*b[0]*b[1]-a[1]*a[3]*b[2]*b[1]+2*a[0]*a[2]*b[1]*b[4]-
+ a[0]*a[1]*b[2]*b[4]+a[1]^2*b[2]*b[3]-2*a[2]*a[3]*b[0]*b[2]-2*a[0]*a[2]*b[2]*b[3]+
+ a[2]*a[3]*b[1]^2-a[2]*a[1]*b[1]*b[3]+2*a[1]*a[4]*b[0]*b[2]+2*a[2]^2*b[0]*b[3]-
+ a[0]*a[4]*b[2]*b[1]+2*a[0]*a[3]*b[2]^2,
+ -a[3]*a[4]*b[2]*b[1]+a[2]*a[5]*b[1]^2-a[1]*a[5]*b[2]*b[1]-a[1]*a[3]*b[2]*b[4]+
+ a[1]^2*b[2]*b[5]-2*a[2]*a[3]*b[2]*b[3]+2*a[2]^2*b[0]*b[5]+2*a[0]*a[5]*b[2]^2+a[3]^2*b[2]^2-
+ 2*a[2]*a[5]*b[0]*b[2]+2*a[1]*a[4]*b[2]*b[3]-a[2]*a[4]*b[1]*b[3]-2*a[0]*a[2]*b[2]*b[5]+
+ a[2]^2*b[3]^2+2*a[2]*a[3]*b[1]*b[4]-a[2]*a[4]*b[0]*b[4]+a[4]^2*b[0]*b[2]-a[2]*a[1]*b[3]*b[4]-
+ a[2]*a[1]*b[1]*b[5]-a[0]*a[4]*b[2]*b[4]+a[0]*a[2]*b[4]^2,
+ -a[4]*a[5]*b[2]*b[1]+a[2]*a[3]*b[4]^2+2*a[3]*a[5]*b[2]^2-a[2]*a[1]*b[4]*b[5]-
+ a[2]*a[4]*b[3]*b[4]+2*a[2]^2*b[3]*b[5]-2*a[2]*a[3]*b[2]*b[5]-a[3]*a[4]*b[2]*b[4]-
+ 2*a[2]*a[5]*b[2]*b[3]-a[2]*a[4]*b[1]*b[5]+2*a[1]*a[4]*b[2]*b[5]-a[1]*a[5]*b[2]*b[4]+
+ a[4]^2*b[2]*b[3]+2*a[2]*a[5]*b[1]*b[4],
+ -2*a[2]*a[5]*b[2]*b[5]+a[4]^2*b[2]*b[5]+a[5]^2*b[2]^2-a[4]*a[5]*b[2]*b[4]+a[2]*a[5]*b[4]^2+
+ a[2]^2*b[5]^2-a[2]*a[4]*b[4]*b[5]};
+ x=realquarticroots(c[0],c[1],c[2],c[3],c[4]);
+ } else {
+ if(abs(b[4]-a[4]) > e){
+ real D=(b[4]-a[4])^2;
+ c=new real[] {(a[0]*b[4]^2+(-a[1]*b[3]-2*a[0]*a[4]+a[1]*a[3])*b[4]+a[2]*b[3]^2+
+ (a[1]*a[4]-2*a[2]*a[3])*b[3]+a[0]*a[4]^2-a[1]*a[3]*a[4]+a[2]*a[3]^2)/D,
+ -((a[1]*b[4]-2*a[2]*b[3]-a[1]*a[4]+2*a[2]*a[3])*b[5]-a[3]*b[4]^2+(a[4]*b[3]-a[1]*a[5]+a[3]*a[4])*b[4]+(2*a[2]*a[5]-a[4]^2)*b[3]+(a[1]*a[4]-2*a[2]*a[3])*a[5])/D,
+ a[2]*(a[5]-b[5])^2/D+a[4]*(a[5]-b[5])/(b[4]-a[4])+a[5]};
+ x=quadraticroots(c[0],c[1],c[2]);
+ } else {
+ if(abs(a[3]-b[3]) > e) {
+ real D=b[3]-a[3];
+ c=new real[] {a[2], (-a[1]*b[5] + a[4]*b[3] + a[1]*a[5] - a[3]*a[4])/D,
+ a[0]*(a[5]-b[5])^2/D^2+a[3]*(a[5]-b[5])/D+a[5]};
+ y=quadraticroots(c[0],c[1],c[2]);
+ for (int i=0; i < y.length; ++i) {
+ c=new real[] {a[0], a[1]*y[i]+a[3], a[2]*y[i]^2+a[4]*y[i]+a[5]};
+ x=quadraticroots(c[0],c[1],c[2]);
+ for (int j=0; j < x.length; ++j) {
+ if(abs(b[0]*x[j]^2+b[1]*x[j]*y[i]+b[2]*y[i]^2+b[3]*x[j]+b[4]*y[i]+b[5]) < 1e-5)
+ P.push(point(R, (x[j],y[i])));
+ }
+ }
+ return P;
+ } else {
+ if(abs(a[5]-b[5]) < e) abort("intersectionpoints: intersection of identical conics.");
+ }
+ }
+ }
+ for (int i=0; i < x.length; ++i) {
+ c=new real[] {a[2], a[1]*x[i]+a[4], a[0]*x[i]^2+a[3]*x[i]+a[5]};
+ y=quadraticroots(c[0],c[1],c[2]);
+ for (int j=0; j < y.length; ++j) {
+ if(abs(b[0]*x[i]^2+b[1]*x[i]*y[j]+b[2]*y[j]^2+b[3]*x[i]+b[4]*y[j]+b[5]) < 1e-5)
+ P.push(point(R, (x[i],y[j])));
+ }
+ }
+ return P;
+}
+
+/*<asyxml><struct signature="conic"><code></asyxml>*/
+struct conic
+{/*<asyxml></code><documentation></documentation><property type="real" signature="e, p, h"><code></asyxml>*/
+ real e, p, h;/*<asyxml></code><documentation>BE CAREFUL: h=distance(F,D) and p=h*e (http://en.wikipedia.org/wiki/Ellipse)
+ While http://mathworld.wolfram.com/ takes p=distance(F,D).</documentation></property><property type="point" signature="F"><code></asyxml>*/
+ point F;/*<asyxml></code><documentation>Focus.</documentation></property><property type="line" signature="D"><code></asyxml>*/
+ line D;/*<asyxml></code><documentation>Directrix.</documentation></property><property type="line" signature="l"><code></asyxml>*/
+ line[] l;/*<asyxml></code><documentation>Case of degenerated conic (not yet implemented !).</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+bool degenerate(conic c)
+{
+ return !finite(c.p) || !finite(c.h);
+}
+
+/*ANCconic conic(point,line,real)ANC*/
+conic conic(point F, line l, real e)
+{/*DOC
+ The conic section define by the eccentricity 'e', the focus 'F'
+ and the directrix 'l'.
+ Note that an eccentricity equal to 0 defines a circle centered at F,
+ with a radius equal at the distance from 'F' to 'l'.
+ If the coordinate system of 'F' and 'l' are not identical, the conic is
+ attached to 'defaultcoordsys'.
+ DOC*/
+ if(e < 0) abort("conic: 'e' can't be negative.");
+ conic oc;
+ point[] P=standardizecoordsys(F,l.A,l.B);
+ line ll;
+ ll=line(P[1],P[2]);
+ oc.e=e < epsgeo ? 0 : e; // Handle case of circle.
+ oc.F=P[0];
+ oc.D=ll;
+ oc.h=distance(P[0],ll);
+ oc.p=abs(e) < epsgeo ? oc.h : e*oc.h;
+ return oc;
+}
+
+/*<asyxml><struct signature="circle"><code></asyxml>*/
+struct circle
+{/*<asyxml></code><documentation>All the calculus with this structure will be as exact as Asymptote can do.
+ For a full precision, you must not cast 'circle' to 'path' excepted for drawing routines.</documentation></asyxml>*/
+ /*<asyxml><property type="point" signature="C"><code></asyxml>*/
+ point C;/*<asyxml></code><documentation>Center</documentation></property><property><code></asyxml>*/
+ real r;/*<asyxml></code><documentation>Radius</documentation></property><property><code></asyxml>*/
+ line l;/*<asyxml></code><documentation>If the radius is infinite, this line is used instead of circle.</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+bool degenerate(circle c)
+{
+ return !finite(c.r);
+}
+
+line line(circle c){
+ if(finite(c.r)) abort("Circle can not be casted to line here.");
+ return c.l;
+}
+
+/*<asyxml><struct signature="ellipse"><code></asyxml>*/
+struct ellipse
+{/*<asyxml></code><documentation>Look at <html><a href="http://mathworld.wolfram.com/Ellipse.html">http://mathworld.wolfram.com/Ellipse.html</a></html></documentation></asyxml>*/
+ /*<asyxml><property type="point" signature="F1,F2,C"><code></asyxml>*/
+ restricted point F1, F2, C;/*<asyxml></code><documentation>Foci and center.</documentation></property><property type="real" signature="a, b, c, e, p"><code></asyxml>*/
+ restricted real a, b, c, e, p;/*<asyxml></code></property><property type="real" signature="angle"><code></asyxml>*/
+ restricted real angle;/*<asyxml></code><documentation>Value is degrees(F1-F2).</documentation></property><property type="line" signature="D1, D2"><code></asyxml>*/
+ restricted line D1, D2;/*<asyxml></code><documentation>Directrices.</documentation></property><property type="line" signature="l"><code></asyxml>*/
+ line l;/*<asyxml></code><documentation>If one axis is infinite, this line is used instead of ellipse.</documentation></property></asyxml>*/
+ /*<asyxml><method type="void" signature="init(point,point,real)"><code></asyxml>*/
+ void init(point f1, point f2, real a)
+ {/*<asyxml></code><documentation>Ellipse given by foci and semimajor axis</documentation></method></asyxml>*/
+ point[] P=standardizecoordsys(f1,f2);
+ this.F1=P[0];
+ this.F2=P[1];
+ this.angle=abs(P[1]-P[0]) < 10*epsgeo ? 0 : degrees(P[1]-P[0]);
+ this.C=(P[0]+P[1])/2;
+ this.a=a;
+ if(!finite(a)) {
+ this.l=line(P[0],P[1]);
+ this.b=infinity;
+ this.e=0;
+ this.c=0;
+ } else {
+ this.c=abs(C-P[0]);
+ this.b=this.c < epsgeo ? a : sqrt(a^2-c^2); // Handle case of circle.
+ this.e=this.c < epsgeo ? 0 : this.c/a; // Handle case of circle.
+ if(this.e >= 1) abort("ellipse.init: wrong parameter: e >= 1.");
+ this.p=a*(1-this.e^2);
+ if (this.c != 0) {// directrix is not set for a circle.
+ point A=this.C+(a^2/this.c)*unit(P[0]-this.C);
+ this.D1=line(A,A+rotateO(90)*unit(A-this.C));
+ this.D2=reverse(rotate(180,C)*D1);
+ }
+ }
+ }
+}/*<asyxml></struct></asyxml>*/
+
+bool degenerate(ellipse el)
+{
+ return (!finite(el.a) || !finite(el.b));
+}
+
+/*<asyxml><struct signature="parabola"><code></asyxml>*/
+struct parabola
+{/*<asyxml></code><documentation>Look at <html><a href="http://mathworld.wolfram.com/Parabola.html">http://mathworld.wolfram.com/Parabola.html</a></html></documentation><property type="point" signature="F, V"><code></asyxml>*/
+ restricted point F, V;/*<asyxml></code><documentation>Focus and vertex</documentation></property><property type="real" signature="a, p, e=1"><code></asyxml>*/
+ restricted real a, p, e=1;/*<asyxml></code></property><property type="real" signature="angle"><code></asyxml>*/
+ restricted real angle;/*<asyxml></code><documentation>Angle, in degrees, of the line (FV).</documentation></property><property type="line" signature="D"><code></asyxml>*/
+ restricted line D;/*<asyxml></code><documentation>Directrix</documentation></property><property type="pair" signature="bmin, bmax"><code></asyxml>*/
+ pair bmin, bmax;/*<asyxml></code><documentation>The (left,bottom) and (right,top) coordinates of region bounding box for drawing the parabola.
+ If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/
+
+ /*<asyxml><method type="void" signature="init(point,line)"><code></asyxml>*/
+ void init(point F, line directrix)
+ {/*<asyxml></code><documentation>Parabola given by focus and directrix.</documentation></method></asyxml>*/
+ point[] P=standardizecoordsys(F,directrix.A,directrix.B);
+ line l=line(P[1],P[2]);
+ this.F=P[0];
+ this.D=l;
+ this.a=distance(P[0],l)/2;
+ this.p=2*a;
+ this.V=0.5*(F+projection(D)*P[0]);
+ this.angle=degrees(F-V);
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><struct signature="hyperbola"><code></asyxml>*/
+struct hyperbola
+{/*<asyxml></code><documentation><html>Look at <a href="http://mathworld.wolfram.com/Hyperbola.html">http://mathworld.wolfram.com/Hyperbola.html</a></html></documentation><property type="point" signature="F1, F2"><code></asyxml>*/
+ restricted point F1, F2;/*<asyxml></code><documentation>Foci.</documentation></property><property type="point" signature="C, V1, V2"><code></asyxml>*/
+ restricted point C, V1, V2;/*<asyxml></code><documentation>Center and vertices.</documentation></property><property type="real" signature="a, b, c, e, p"><code></asyxml>*/
+ restricted real a, b, c, e, p;/*<asyxml></code><documentation></documentation></property><property type="real" signature="angle"><code></asyxml>*/
+ restricted real angle;/*<asyxml></code><documentation>Angle, in degrees, of the line (F1F2).</documentation></property><property type="line" signature="D1, D2, A1, A2"><code></asyxml>*/
+ restricted line D1, D2, A1, A2;/*<asyxml></code><documentation>Directrices and asymptotes.</documentation></property><property type="pair" signature="bmin, bmax"><code></asyxml>*/
+ pair bmin, bmax; /*<asyxml></code><documentation>The (left,bottom) and (right,top) coordinates of region bounding box for drawing the hyperbola.
+ If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/
+
+ /*<asyxml><method type="void" signature="init(point,point,real)"><code></asyxml>*/
+ void init(point f1, point f2, real a)
+ {/*<asyxml></code><documentation>Hyperbola given by foci and semimajor axis.</documentation></method></asyxml>*/
+ point[] P=standardizecoordsys(f1,f2);
+ this.F1=P[0];
+ this.F2=P[1];
+ this.angle=degrees(F2-F1);
+ this.a=a;
+ this.C=(P[0]+P[1])/2;
+ this.c=abs(C-P[0]);
+ this.e=this.c/a;
+ if(this.e <= 1) abort("hyperbola.init: wrong parameter: e <= 1.");
+ this.b=a*sqrt(this.e^2-1);
+ this.p=a*(this.e^2-1);
+ point A=this.C+(a^2/this.c)*unit(P[0]-this.C);
+ this.D1=line(A,A+rotateO(90)*unit(A-this.C));
+ this.D2=reverse(rotate(180,C)*D1);
+ this.V1=C+a*unit(F1-C);
+ this.V2=C+a*unit(F2-C);
+ this.A1=line(C,V1+b*unit(rotateO(-90)*(C-V1)));
+ this.A2=line(C,V1+b*unit(rotateO(90)*(C-V1)));
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><variable type="int" signature="conicnodesfactor"><code></asyxml>*/
+int conicnodesfactor=1;/*<asyxml></code><documentation>Factor for the node number of all conics.</documentation></variable></asyxml>*/
+
+/*<asyxml><variable type="int" signature="circlenodesnumberfactor"><code></asyxml>*/
+int circlenodesnumberfactor=100;/*<asyxml></code><documentation>Factor for the node number of circles.</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="circlenodesnumber(real)"><code></asyxml>*/
+int circlenodesnumber(real r)
+{/*<asyxml></code><documentation>Return the number of nodes for drawing a circle of radius 'r'.</documentation></function></asyxml>*/
+ if (circlenodesnumberfactor < 100) write("Warning: variable 'circlenodesnumberfactor' maybe too small.");
+ int oi=ceil(circlenodesnumberfactor*abs(r)^0.1);
+ oi=45*floor(oi/45);
+ return oi == 0 ? 4 : conicnodesfactor*oi;
+}
+
+/*<asyxml><function type="int" signature="circlenodesnumber(real,real,real)"><code></asyxml>*/
+int circlenodesnumber(real r, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the number of nodes to draw a circle arc.</documentation></function></asyxml>*/
+ return (r > 0) ?
+ ceil(circlenodesnumber(r)*abs(angle1-angle2)/360) :
+ ceil(circlenodesnumber(r)*abs((1-abs(angle1-angle2)/360)));
+}
+
+/*<asyxml><variable type="int" signature="ellispenodesnumberfactor"><code></asyxml>*/
+int ellipsenodesnumberfactor=250;/*<asyxml></code><documentation>Factor for the node number of ellispe (non-circle).</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real)"><code></asyxml>*/
+int ellipsenodesnumber(real a, real b)
+{/*<asyxml></code><documentation>Return the number of nodes to draw a ellipse of axis 'a' and 'b'.</documentation></function></asyxml>*/
+ if (ellipsenodesnumberfactor < 250) write("Warning: variable 'ellipsenodesnumberfactor' maybe too small.");
+ int tmp=circlenodesnumberfactor;
+ circlenodesnumberfactor=ellipsenodesnumberfactor;
+ int oi=circlenodesnumber(max(abs(a),abs(b))/min(abs(a),abs(b)));
+ circlenodesnumberfactor=tmp;
+ return conicnodesfactor*oi;
+}
+
+/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real,real)"><code></asyxml>*/
+int ellipsenodesnumber(real a, real b, real angle1, real angle2, bool dir)
+{/*<asyxml></code><documentation>Return the number of nodes to draw an ellipse arc.</documentation></function></asyxml>*/
+ real d;
+ real da=angle2-angle1;
+ if(dir) {
+ d=angle1 < angle2 ? da : 360+da;
+ } else {
+ d=angle1 < angle2 ? -360+da : da;
+ }
+ int n=floor(ellipsenodesnumber(a,b)*abs(d)/360);
+ return n < 5 ? 5 : n;
+}
+
+/*<asyxml><variable type="int" signature="parabolanodesnumberfactor"><code></asyxml>*/
+int parabolanodesnumberfactor=100;/*<asyxml></code><documentation>Factor for the number of nodes of parabolas.</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="parabolanodesnumber(parabola,real,real)"><code></asyxml>*/
+int parabolanodesnumber(parabola p, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the number of nodes for drawing a parabola.</documentation></function></asyxml>*/
+ return conicnodesfactor*floor(0.01*parabolanodesnumberfactor*abs(angle1-angle2));
+}
+
+/*<asyxml><variable type="int" signature="hyperbolanodesnumberfactor"><code></asyxml>*/
+int hyperbolanodesnumberfactor=100;/*<asyxml></code><documentation>Factor for the number of nodes of hyperbolas.</documentation></variable></asyxml>*/
+/*<asyxml><function type="int" signature="hyperbolanodesnumber(hyperbola,real,real)"><code></asyxml>*/
+int hyperbolanodesnumber(hyperbola h, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the number of nodes for drawing an hyperbola.</documentation></function></asyxml>*/
+ return conicnodesfactor*floor(0.01*hyperbolanodesnumberfactor*abs(angle1-angle2)/h.e);
+}
+
+/*<asyxml><operator type="conic" signature="+(conic,explicit point)"><code></asyxml>*/
+conic operator +(conic c, explicit point M)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F+M,c.D+M,c.e);
+}
+/*<asyxml><operator type="conic" signature="-(conic,explicit point)"><code></asyxml>*/
+conic operator -(conic c, explicit point M)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F-M,c.D-M,c.e);
+}
+/*<asyxml><operator type="conic" signature="+(conic,explicit pair)"><code></asyxml>*/
+conic operator +(conic c, explicit pair m)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ point M=point(c.F.coordsys,m);
+ return conic(c.F+M,c.D+M,c.e);
+}
+/*<asyxml><operator type="conic" signature="-(conic,explicit pair)"><code></asyxml>*/
+conic operator -(conic c, explicit pair m)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ point M=point(c.F.coordsys,m);
+ return conic(c.F-M,c.D-M,c.e);
+}
+/*<asyxml><operator type="conic" signature="+(conic,vector)"><code></asyxml>*/
+conic operator +(conic c, vector v)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F+v,c.D+v,c.e);
+}
+/*<asyxml><operator type="conic" signature="-(conic,vector)"><code></asyxml>*/
+conic operator -(conic c, vector v)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return conic(c.F-v,c.D-v,c.e);
+}
+
+/*<asyxml><function type="coordsys" signature="coordsys(conic)"><code></asyxml>*/
+coordsys coordsys(conic co)
+{/*<asyxml></code><documentation>Return the coordinate system of 'co'.</documentation></function></asyxml>*/
+ return co.F.coordsys;
+}
+
+/*<asyxml><function type="conic" signature="changecoordsys(coordsys,conic)"><code></asyxml>*/
+conic changecoordsys(coordsys R, conic co)
+{/*<asyxml></code><documentation>Change the coordinate system of 'co' to 'R'</documentation></function></asyxml>*/
+ line l=changecoordsys(R,co.D);
+ point F=changecoordsys(R,co.F);
+ return conic(F,l,co.e);
+}
+
+/*<asyxml><typedef type="polarconicroutine" return="path" params="conic,real,real,int,bool"><code></asyxml>*/
+typedef path polarconicroutine(conic co, real angle1, real angle2, int n, bool direction);/*<asyxml></code><documentation>Routine type used to draw conics from 'angle1' to 'angle2'</documentation></typedef></asyxml>*/
+
+/*<asyxml><function type="path" signature="arcfromfocus(conic,real,real,int,bool)"><code></asyxml>*/
+path arcfromfocus(conic co, real angle1, real angle2, int n=400, bool direction=CCW)
+{/*<asyxml></code><documentation>Return the path of the conic section 'co' from angle1 to angle2 in degrees,
+ drawing in the given direction, with n nodes.</documentation></function></asyxml>*/
+ guide op;
+ if (n < 1) return op;
+ if (angle1 > angle2) {
+ path g=arcfromfocus(co,angle2,angle1,n,!direction);
+ return g == nullpath ? g : reverse(g);
+ }
+ point O=projection(co.D)*co.F;
+ pair i=unit(locate(co.F)-locate(O));
+ pair j=rotate(90)*i;
+ coordsys Rp=cartesiansystem(co.F,i,j);
+ real a1=direction ? radians(angle1) : radians(angle2);
+ real a2=direction ? radians(angle2) : radians(angle1)+2*pi;
+ real step=n == 1 ? 0 : (a2-a1)/(n-1);
+ real a,r;
+ for (int i=0; i < n; ++i) {
+ a=a1+i*step;
+ if(co.e >= 1) {
+ r=1-co.e*cos(a);
+ if(r > epsgeo) {
+ r=co.p/r;
+ op=op--Rp*Rp.polar(r,a);
+ }
+ } else {
+ r=co.p/(1-co.e*cos(a));
+ op=op..Rp*Rp.polar(r,a);
+ }
+ }
+ if(co.e < 1 && abs(abs(a2-a1)-2*pi) < epsgeo) op=(path)op..cycle;
+
+ return (direction ? op : op == nullpath ? op :reverse(op));
+}
+
+/*<asyxml><variable type="polarconicroutine" signature="currentpolarconicroutine"><code></asyxml>*/
+polarconicroutine currentpolarconicroutine=arcfromfocus;/*<asyxml></code><documentation>Default routine used to cast conic section to path.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="point" signature="angpoint(conic,real)"><code></asyxml>*/
+point angpoint(conic co, real angle)
+{/*<asyxml></code><documentation>Return the point of 'co' whose the angular (in degrees)
+ coordinate is 'angle' (mesured from the focus of 'co', relatively
+ to its 'natural coordinate system').</documentation></function></asyxml>*/
+ coordsys R=coordsys(co);
+ return point(R,point(arcfromfocus(co,angle,angle,1,CCW),0)/R);
+}
+
+/*<asyxml><operator type="bool" signature="@(point,conic)"><code></asyxml>*/
+bool operator @(point M, conic co)
+{/*<asyxml></code><documentation>Return true iff 'M' on 'co'.</documentation></operator></asyxml>*/
+ if(co.e == 0) return abs(abs(co.F-M)-co.p) < 10*epsgeo;
+ return abs(co.e*distance(M,co.D)-abs(co.F-M)) < 10*epsgeo;
+}
+
+/*<asyxml><function type="coordsys" signature="coordsys(ellipse)"><code></asyxml>*/
+coordsys coordsys(ellipse el)
+{/*<asyxml></code><documentation>Return the coordinate system of 'el'.</documentation></function></asyxml>*/
+ return el.F1.coordsys;
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(ellipse)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(ellipse el)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of the ellipse 'el'.</documentation></function></asyxml>*/
+ if(degenerate(el)) return cartesiansystem(el.l.A,el.l.u,el.l.v);
+ pair O=locate(el.C);
+ pair i=el.e == 0 ? el.C.coordsys.i : unit(locate(el.F1)-O);
+ pair j=rotate(90)*i;
+ return cartesiansystem(O,i,j);
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(parabola)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(parabola p)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of a parabola,
+ so that Origin = vertex of 'p' and directrix: x=-a.</documentation></function></asyxml>*/
+ point A=projection(p.D)*p.F;
+ pair O=locate((A+p.F)/2);
+ pair i=unit(locate(p.F)-O);
+ pair j=rotate(90)*i;
+ return cartesiansystem(O,i,j);
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(hyperbola h)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of an hyperbola.</documentation></function></asyxml>*/
+ pair O=locate(h.C);
+ pair i=unit(locate(h.F2)-O);
+ pair j=rotate(90)*i;
+ return cartesiansystem(O,i,j);
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,point,real)"><code></asyxml>*/
+ellipse ellipse(point F1, point F2, real a)
+{/*<asyxml></code><documentation>Return the ellipse whose the foci are 'F1' and 'F2'
+ and the semimajor axis is 'a'.</documentation></function></asyxml>*/
+ ellipse oe;
+ oe.init(F1,F2,a);
+ return oe;
+}
+
+/*<asyxml><constant type="bool" signature="byfoci, byvertices"><code></asyxml>*/
+restricted bool byfoci=true, byvertices=false;/*<asyxml></code><documentation>Constants useful for the routine 'hyperbola(point P1, point P2, real ae, bool byfoci=byfoci)'</documentation></constant></asyxml>*/
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,real,bool)"><code></asyxml>*/
+hyperbola hyperbola(point P1, point P2, real ae, bool byfoci=byfoci)
+{/*<asyxml></code><documentation>if 'byfoci=true':
+ return the hyperbola whose the foci are 'P1' and 'P2'
+ and the semimajor axis is 'ae'.
+ else return the hyperbola whose vertexes are 'P1' and 'P2' with eccentricity 'ae'.</documentation></function></asyxml>*/
+ hyperbola oh;
+ point[] P=standardizecoordsys(P1,P2);
+ if(byfoci) {
+ oh.init(P[0],P[1],ae);
+ } else {
+ real a=abs(P[0]-P[1])/2;
+ vector V=unit(P[0]-P[1]);
+ point F1=P[0]+a*(ae-1)*V;
+ point F2=P[1]-a*(ae-1)*V;
+ oh.init(F1,F2,a);
+ }
+ return oh;
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,point,point)"><code></asyxml>*/
+ellipse ellipse(point F1, point F2, point M)
+{/*<asyxml></code><documentation>Return the ellipse passing through 'M' whose the foci are 'F1' and 'F2'.</documentation></function></asyxml>*/
+ point P[]=standardizecoordsys(false,F1,F2,M);
+ real a=abs(F1-M)+abs(F2-M);
+ return ellipse(F1,F2,finite(a) ? a/2 : a);
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,real,real,real)"><code></asyxml>*/
+ellipse ellipse(point C, real a, real b, real angle=0)
+{/*<asyxml></code><documentation>Return the ellipse centered at 'C' with semimajor axis 'a' along C--C+dir(angle),
+ semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/
+ ellipse oe;
+ coordsys R=C.coordsys;
+ angle+=degrees(R.i);
+ if(a < b) {angle += 90; real tmp=a; a=b; b=tmp;}
+ if(finite(a) && finite(b)) {
+ real c=sqrt(abs(a^2-b^2));
+ point f1, f2;
+ if(abs(a-b) < epsgeo) {
+ f1=C; f2=C;
+ } else {
+ f1=point(R,(locate(C)+rotate(angle)*(-c,0))/R);
+ f2=point(R,(locate(C)+rotate(angle)*(c,0))/R);
+ }
+ oe.init(f1,f2,a);
+ } else {
+ if(finite(b) || !finite(a)) oe.init(C,C+R.polar(1,angle),infinity);
+ else oe.init(C,C+R.polar(1,90+angle),infinity);
+ }
+ return oe;
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(explicit pair,real,real)"><code></asyxml>*/
+ellipse ellipse(explicit pair C, real a, real b)=
+ new ellipse(explicit pair C, real a, real b)
+{/*<asyxml></code><documentation>Overwrite the default routine.</documentation></function></asyxml>*/
+ return ellipse((point)C,a,b,0);
+};
+
+/*<asyxml><function type="ellipse" signature="ellipse(bqe)"><code></asyxml>*/
+ellipse ellipse(bqe bqe)
+{/*<asyxml></code><documentation>Return the ellipse a[0]*x^2 + a[1]*xy + a[2]*y^2 + a[3]*x + a[4]*y + a[5]=0
+ given in the coordinate system of 'bqe' with a[i]=bque.a[i].
+ <url href="http://mathworld.wolfram.com/QuadraticCurve.html"/>
+ <url href="http://mathworld.wolfram.com/Ellipse.html"/>.</documentation></function></asyxml>*/
+ bqe lbqe=changecoordsys(defaultcoordsys,bqe);
+ real a=lbqe.a[0], b=lbqe.a[1]/2, c=lbqe.a[2], d=lbqe.a[3]/2, f=lbqe.a[4]/2, g=lbqe.a[5];
+ coordsys R=bqe.coordsys;
+ string message="ellipse: the given equation is not an equation of an ellipse.";
+ real u=b^2*g + d^2*c + f^2*a;
+ real delta=a*c*g + b*f*d + d*b*f - u;
+ if(abs(delta) < epsgeo) abort(message);
+ real j=b^2-a*c;
+ real i=a+c;
+ real dd=j*(sgnd(c-a)*sqrt((a-c)^2+4*(b^2))-c-a);
+ real ddd=j*(-sgnd(c-a)*sqrt((a-c)^2+4*(b^2))-c-a);
+
+ if(abs(ddd) < epsgeo || abs(dd) < epsgeo ||
+ j >= -epsgeo || delta/sgnd(i) > 0) abort(message);
+
+ real x=(c*d-b*f)/j, y=(a*f-b*d)/j;
+ // real dir=abs(b) < epsgeo ? 0 : pi/2-0.5*acot(0.5*(c-a)/b);
+ real dir=abs(b) < epsgeo ? 0 : 0.5*acot(0.5*(c-a)/b);
+ if(dir*(c-a)*b < 0) dir=dir < 0 ? dir+pi/2 : dir-pi/2;
+ real cd=cos(dir), sd=sin(dir);
+ real t=a*cd^2-2*b*cd*sd+c*sd^2;
+ real tt=a*sd^2+2*b*cd*sd+c*cd^2;
+ real gg=-g+((d*cd-f*sd)^2)/t+((d*sd+f*cd)^2)/tt;
+ t=t/gg; tt=tt/gg;
+ // The equation of the ellipse is t*(x-center.x)^2+tt*(y-center.y)^2=1;
+ real aa, bb;
+ aa=sqrt(2*(u-2*b*d*f-a*c*g)/dd);
+ bb=sqrt(2*(u-2*b*d*f-a*c*g)/ddd);
+ a=t > tt ? max(aa,bb) : min(aa,bb);
+ b=t > tt ? min(aa,bb) : max(aa,bb);
+ return ellipse(point(R,(x,y)/R),
+ a,b,degrees(pi/2-dir-angle(R.i)));
+}
+
+/*<asyxml><function type="ellipse" signature="ellipse(point,point,point,point,point)"><code></asyxml>*/
+ellipse ellipse(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the ellipse passing through the five points (if possible)</documentation></function></asyxml>*/
+ return ellipse(bqe(M1,M2,M3,M4,M5));
+}
+
+/*<asyxml><function type="bool" signature="inside(ellipse,point)"><code></asyxml>*/
+bool inside(ellipse el, point M)
+{/*<asyxml></code><documentation>Return 'true' iff 'M' is inside 'el'.</documentation></function></asyxml>*/
+ return abs(el.F1-M)+abs(el.F2-M)-2*el.a < -epsgeo;
+}
+
+/*<asyxml><function type="bool" signature="inside(parabola,point)"><code></asyxml>*/
+bool inside(parabola p, point M)
+{/*<asyxml></code><documentation>Return 'true' if 'M' is inside 'p'.</documentation></function></asyxml>*/
+ return distance(p.D,M) - abs(p.F-M) > epsgeo;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,line)"><code></asyxml>*/
+parabola parabola(point F, line l)
+{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and directrix is 'l'.</documentation></function></asyxml>*/
+ parabola op;
+ op.init(F,l);
+ return op;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,point)"><code></asyxml>*/
+parabola parabola(point F, point vertex)
+{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and vertex is 'vertex'.</documentation></function></asyxml>*/
+ parabola op;
+ point[] P=standardizecoordsys(F,vertex);
+ point A=rotate(180,P[1])*P[0];
+ point B=A+rotateO(90)*unit(P[1]-A);
+ op.init(P[0],line(A,B));
+ return op;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,real,real)"><code></asyxml>*/
+parabola parabola(point F, real a, real angle)
+{/*<asyxml></code><documentation>Return the parabola whose focus is F, latus rectum is 4a and
+ the angle of the axis of symmetry (in the coordinate system of F) is 'angle'.</documentation></function></asyxml>*/
+ parabola op;
+ coordsys R=F.coordsys;
+ point A=F-point(R,R.polar(2a,radians(angle)));
+ point B=A+point(R,R.polar(1,radians(90+angle)));
+ op.init(F,line(A,B));
+ return op;
+}
+
+/*<asyxml><function type="bool" signature="isparabola(bqe)"><code></asyxml>*/
+bool isparabola(bqe bqe)
+{/*<asyxml></code><documentation>Return true iff 'bqe' is the equation of a parabola.</documentation></function></asyxml>*/
+ bqe lbqe=changecoordsys(defaultcoordsys,bqe);
+ real a=lbqe.a[0], b=lbqe.a[1]/2, c=lbqe.a[2], d=lbqe.a[3]/2, f=lbqe.a[4]/2, g=lbqe.a[5];
+ real delta=a*c*g + b*f*d + d*b*f - (b^2*g + d^2*c + f^2*a);
+ return (abs(delta) > epsgeo && abs(b^2-a*c) < epsgeo);
+}
+
+/*<asyxml><function type="parabola" signature="parabola(bqe)"><code></asyxml>*/
+parabola parabola(bqe bqe)
+{/*<asyxml></code><documentation>Return the parabola a[0]x^2+a[1]xy+a[2]y^2+a[3]x+a[4]y+a[5]]=0 (a[n] means bqe.a[n]).
+ <url href="http://mathworld.wolfram.com/QuadraticCurve.html"/>
+ <url href="http://mathworld.wolfram.com/Parabola.html"/></documentation></function></asyxml>*/
+ bqe lbqe=changecoordsys(defaultcoordsys,bqe);
+ real a=lbqe.a[0], b=lbqe.a[1]/2, c=lbqe.a[2], d=lbqe.a[3]/2, f=lbqe.a[4]/2, g=lbqe.a[5];
+ string message="parabola: the given equation is not an equation of a parabola.";
+ real delta=a*c*g + b*f*d + d*b*f - (b^2*g + d^2*c + f^2*a);
+ if(abs(delta) < 10*epsgeo || abs(b^2-a*c) > 10*epsgeo) abort(message);
+ real dir=abs(b) < epsgeo ? 0 : 0.5*acot(0.5*(c-a)/b);
+ if(dir*(c-a)*b < 0) dir=dir < 0 ? dir+pi/2 : dir-pi/2;
+ real cd=cos(dir), sd=sin(dir);
+ real ap=a*cd^2-2*b*cd*sd+c*sd^2;
+ real cp=a*sd^2+2*b*cd*sd+c*cd^2;
+ real dp=d*cd-f*sd;
+ real fp=d*sd+f*cd;
+ real gp=g;
+ parabola op;
+ coordsys R=bqe.coordsys;
+ // The equation of the parabola is ap*x'^2+cp*y'^2+2dp*x'+2fp*y'+gp=0
+ if (abs(ap) < epsgeo) {/* directrix parallel to the rotated(dir) y-axis
+ equation: (y-vertex.y)^2=4*a*(x-vertex)
+ */
+ pair pvertex=rotate(degrees(-dir))*(0.5(-gp+fp^2/cp)/dp,-fp/cp);
+ real a=-0.5*dp/cp;
+ point vertex=point(R,pvertex/R);
+ point focus=point(R,(pvertex+a*expi(-dir))/R);
+ op=parabola(focus,vertex);
+
+ } else {/* directrix parallel to the rotated(dir) x-axis
+ equation: (x-vertex)^2=4*a*(y-vertex.y)
+ */
+ pair pvertex=rotate(degrees(-dir))*(-dp/ap,0.5*(-gp+dp^2/ap)/fp);
+ real a=-0.5*fp/ap;
+ point vertex=point(R,pvertex/R);
+ point focus=point(R,(pvertex+a*expi(pi/2-dir))/R);
+ op=parabola(focus,vertex);
+ }
+ return op;
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,point,point,line)"><code></asyxml>*/
+parabola parabola(point M1, point M2, point M3, line l)
+{/*<asyxml></code><documentation>Return the parabola passing through the three points with its directix
+ parallel to the line 'l'.</documentation></function></asyxml>*/
+ coordsys R;
+ pair[] pts;
+ if (samecoordsys(M1,M2,M3)) {
+ R=M1.coordsys;
+ } else {
+ R=defaultcoordsys;
+ }
+ real gle=degrees(l);
+ coordsys Rp=cartesiansystem(R.O,rotate(gle)*R.i,rotate(gle)*R.j);
+ pts=new pair[] {coordinates(changecoordsys(Rp,M1)),
+ coordinates(changecoordsys(Rp,M2)),
+ coordinates(changecoordsys(Rp,M3))};
+ real[][] M;
+ real[] x;
+ for (int i=0; i < 3; ++i) {
+ M[i]=new real[] {pts[i].x,pts[i].y,1};
+ x[i]=-pts[i].x^2;
+ }
+ real[] coef=solve(M,x);
+ return parabola(changecoordsys(R,bqe(Rp,1,0,0,coef[0],coef[1],coef[2])));
+}
+
+/*<asyxml><function type="parabola" signature="parabola(point,point,point,point,point)"><code></asyxml>*/
+parabola parabola(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the parabola passing through the five points.</documentation></function></asyxml>*/
+ return parabola(bqe(M1,M2,M3,M4,M5));
+}
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(point,real,real,real)"><code></asyxml>*/
+hyperbola hyperbola(point C, real a, real b, real angle=0)
+{/*<asyxml></code><documentation>Return the hyperbola centered at 'C' with semimajor axis 'a' along C--C+dir(angle),
+ semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/
+ hyperbola oh;
+ coordsys R=C.coordsys;
+ angle+=degrees(R.i);
+ real c=sqrt(a^2+b^2);
+ point f1=point(R,(locate(C)+rotate(angle)*(-c,0))/R);
+ point f2=point(R,(locate(C)+rotate(angle)*(c,0))/R);
+ oh.init(f1,f2,a);
+ return oh;
+}
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(bqe)"><code></asyxml>*/
+hyperbola hyperbola(bqe bqe)
+{/*<asyxml></code><documentation>Return the hyperbola a[0]x^2+a[1]xy+a[2]y^2+a[3]x+a[4]y+a[5]]=0 (a[n] means bqe.a[n]).
+ <url href="http://mathworld.wolfram.com/QuadraticCurve.html"/>
+ <url href="http://mathworld.wolfram.com/Hyperbola.html"/></documentation></function></asyxml>*/
+ bqe lbqe=changecoordsys(defaultcoordsys,bqe);
+ real a=lbqe.a[0], b=lbqe.a[1]/2, c=lbqe.a[2], d=lbqe.a[3]/2, f=lbqe.a[4]/2, g=lbqe.a[5];
+ string message="hyperbola: the given equation is not an equation of a hyperbola.";
+ real delta=a*c*g + b*f*d + d*b*f - (b^2*g + d^2*c + f^2*a);
+ if(abs(delta) < 10*epsgeo || abs(b^2-a*c) < 0) abort(message);
+ real dir=abs(b) < epsgeo ? 0 : 0.5*acot(0.5*(c-a)/b);
+ real cd=cos(dir), sd=sin(dir);
+ real ap=a*cd^2-2*b*cd*sd+c*sd^2;
+ real cp=a*sd^2+2*b*cd*sd+c*cd^2;
+ real dp=d*cd-f*sd;
+ real fp=d*sd+f*cd;
+ real gp=-g+dp^2/ap+fp^2/cp;
+ hyperbola op;
+ coordsys R=bqe.coordsys;
+ real j=b^2-a*c;
+ point C=point(R,((c*d-b*f)/j,(a*f-b*d)/j)/R);
+ real aa=gp/ap, bb=gp/cp;
+ real a=sqrt(abs(aa)), b=sqrt(abs(bb));
+ if(aa < 0) {dir -= pi/2; aa=a; a=b; b=aa;}
+ return hyperbola(C,a,b,degrees(-dir-angle(R.i)));
+}
+
+/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,point,point,point)"><code></asyxml>*/
+hyperbola hyperbola(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the hyperbola passing through the five points (if possible).</documentation></function></asyxml>*/
+ return hyperbola(bqe(M1,M2,M3,M4,M5));
+}
+
+/*<asyxml><function type="hyperbola" signature="conj(hyperbola)"><code></asyxml>*/
+hyperbola conj(hyperbola h)
+{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
+ return hyperbola(h.C, h.b, h.a, 90+h.angle);
+}
+
+/*<asyxml><function type="circle" signature="circle(explicit point,real)"><code></asyxml>*/
+circle circle(explicit point C, real r)
+{/*<asyxml></code><documentation>Circle given by center and radius.</documentation></function></asyxml>*/
+ circle oc=new circle;
+ oc.C=C;
+ oc.r=r;
+ if(!finite(r)) oc.l=line(C,C+vector(C.coordsys,(1,0)));
+ return oc;
+}
+circle circle(explicit point C, int r)
+{
+ return circle(C,(real) r);
+}
+
+/*<asyxml><function type="circle" signature="circle(pair,real)"><code></asyxml>*/
+circle circle(pair c, real r)=new circle(pair c, real r)
+{/*<asyxml></code><documentation>Overwrite 'circle(pair,real)'</documentation></function></asyxml>*/
+ return circle(locate(c),r);
+};
+
+/*<asyxml><function type="circle" signature="circle(point,point)"><code></asyxml>*/
+circle circle(point A, point B)
+{/*<asyxml></code><documentation>Return the circle of diameter AB.</documentation></function></asyxml>*/
+ real r;
+ circle oc;
+ real a=abs(A), b=abs(B);
+ if(finite(a) && finite(b)) {
+ oc=circle((A+B)/2,abs(A-B)/2);
+ } else {
+ oc.r=infinity;
+ if(finite(abs(A))) oc.l=line(A,A+unit(B));
+ else {
+ if(finite(abs(B))) oc.l=line(B,B+unit(A));
+ else if(finite(abs(A-B)/2)) oc=circle((A+B)/2,abs(A-B)/2); else
+ oc.l=line(A,B);
+ }
+ }
+ return oc;
+}
+
+/*<asyxml><function type="circle" signature="circle(segment)"><code></asyxml>*/
+circle circle(segment s)
+{/*<asyxml></code><documentation>Return the circle of diameter 's'.</documentation></function></asyxml>*/
+ return circle(s.A,s.B);
+}
+
+/*<asyxml><function type="point" signature="circumcenter(point,point,point)"><code></asyxml>*/
+point circumcenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the circumcenter of triangle ABC.</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B,C);
+ coordsys R=P[0].coordsys;
+ pair a=A, b=B, c=C;
+ pair mAB=(a+b)/2;
+ pair mAC=(a+c)/2;
+ pair pp=extension(mAB, rotate(90,mAB)*a, mAC, rotate(90,mAC)*c);
+ return point(R,pp/R);
+}
+
+/*<asyxml><function type="circle" signature="circle(point,point,point)"><code></asyxml>*/
+circle circle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/
+ if(collinear(A-B,A-C)) {
+ circle oc;
+ oc.r=infinity;
+ oc.C=(A+B+C)/3;
+ oc.l=line(oc.C, oc.C == A ? B : A);
+ return oc;
+ }
+ point c=circumcenter(A, B, C);
+ return circle(c,abs(c-A));
+}
+
+/*<asyxml><function type="circle" signature="circumcircle(point,point,point)"><code></asyxml>*/
+circle circumcircle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/
+ return circle(A,B,C);
+}
+
+/*<asyxml><operator type="circle" signature="*(real,explicit circle)"><code></asyxml>*/
+circle operator *(real x, explicit circle c)
+{/*<asyxml></code><documentation>Multiply the radius of 'c'.</documentation></operator></asyxml>*/
+ return finite(c.r) ? circle(c.C,x*c.r) : c;
+}
+circle operator *(int x, explicit circle c)
+{
+ return finite(c.r) ? circle(c.C,x*c.r) : c;
+}
+/*<asyxml><operator type="circle" signature="/(explicit circle,real)"><code></asyxml>*/
+circle operator /(explicit circle c, real x)
+{/*<asyxml></code><documentation>Divide the radius of 'c'</documentation></operator></asyxml>*/
+ return finite(c.r) ? circle(c.C,c.r/x) : c;
+}
+circle operator /(explicit circle c,int x)
+{
+ return finite(c.r) ? circle(c.C,c.r/x) : c;
+}
+/*<asyxml><operator type="circle" signature="+(explicit circle,explicit point)"><code></asyxml>*/
+circle operator +(explicit circle c, explicit point M)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C+M,c.r);
+}
+/*<asyxml><operator type="circle" signature="-(explicit circle,explicit point)"><code></asyxml>*/
+circle operator -(explicit circle c, explicit point M)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C-M,c.r);
+}
+/*<asyxml><operator type="circle" signature="+(explicit circle,pair)"><code></asyxml>*/
+circle operator +(explicit circle c, pair m)
+{/*<asyxml></code><documentation>Translation of 'c'.
+ 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/
+ return circle(c.C+m,c.r);
+}
+/*<asyxml><operator type="circle" signature="-(explicit circle,pair)"><code></asyxml>*/
+circle operator -(explicit circle c, pair m)
+{/*<asyxml></code><documentation>Translation of 'c'.
+ 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/
+ return circle(c.C-m,c.r);
+}
+/*<asyxml><operator type="circle" signature="+(explicit circle,vector)"><code></asyxml>*/
+circle operator +(explicit circle c, vector m)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C+m,c.r);
+}
+/*<asyxml><operator type="circle" signature="-(explicit circle,vector)"><code></asyxml>*/
+circle operator -(explicit circle c, vector m)
+{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
+ return circle(c.C-m,c.r);
+}
+/*<asyxml><operator type="real" signature="^(point,explicit circle)"><code></asyxml>*/
+real operator ^(point M, explicit circle c)
+{/*<asyxml></code><documentation>The power of 'M' with respect to the circle 'c'</documentation></operator></asyxml>*/
+ return xpart((abs(locate(M)-locate(c.C)),c.r)^2);
+}
+/*<asyxml><operator type="bool" signature="@(point,explicit circle)"><code></asyxml>*/
+bool operator @(point M, explicit circle c)
+{/*<asyxml></code><documentation>Return true iff 'M' is on the circle 'c'.</documentation></operator></asyxml>*/
+ return finite(c.r) ?
+ abs(abs(locate(M)-locate(c.C))-abs(c.r)) <= 10*epsgeo :
+ M @ c.l;
+}
+
+/*<asyxml><operator type="ellipse" signature="cast(circle)"><code></asyxml>*/
+ellipse operator cast(circle c)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ return finite(c.r) ? ellipse(c.C,c.r,c.r,0) : ellipse(c.l.A,c.l.B,infinity);
+}
+
+/*<asyxml><operator type="circle" signature="cast(ellipse)"><code></asyxml>*/
+circle operator cast(ellipse el)
+{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
+ circle oc;
+ bool infb=(!finite(el.a) || !finite(el.b));
+ if(!infb && abs(el.a-el.b) > epsgeo)
+ abort("Can not cast ellipse with different axis values to circle");
+ oc=circle(el.C,infb ? infinity : el.a);
+ oc.l=el.l.copy();
+ return oc;
+}
+
+/*<asyxml><operator type="ellipse" signature="cast(conic)"><code></asyxml>*/
+ellipse operator cast(conic co)
+{/*<asyxml></code><documentation>Cast a conic to an ellipse (can be a circle).</documentation></operator></asyxml>*/
+ if(degenerate(co) && co.e < 1) return ellipse(co.l[0].A, co.l[0].B,infinity);
+ ellipse oe;
+ if(co.e < 1) {
+ real a=co.p/(1-co.e^2);
+ real c=co.e*a;
+ vector v=co.D.v;
+ if(!sameside(co.D.A+v,co.F,co.D)) v=-v;
+ point f2=co.F+2*c*v;
+ f2=changecoordsys(co.F.coordsys,f2);
+ oe=a == 0 ? ellipse(co.F,co.p,co.p,0) : ellipse(co.F,f2,a);
+ } else
+ abort("casting: The conic section is not an ellipse.");
+ return oe;
+}
+
+/*<asyxml><operator type="parabola" signature="cast(conic)"><code></asyxml>*/
+parabola operator cast(conic co)
+{/*<asyxml></code><documentation>Cast a conic to a parabola.</documentation></operator></asyxml>*/
+ parabola op;
+ if(abs(co.e-1) > epsgeo) abort("casting: The conic section is not a parabola.");
+ op.init(co.F,co.D);
+ return op;
+}
+
+/*<asyxml><operator type="conic" signature="cast(parabola)"><code></asyxml>*/
+conic operator cast(parabola p)
+{/*<asyxml></code><documentation>Cast a parabola to a conic section.</documentation></operator></asyxml>*/
+ return conic(p.F,p.D,1);
+}
+
+/*<asyxml><operator type="hyperbola" signature="cast(conic)"><code></asyxml>*/
+hyperbola operator cast(conic co)
+{/*<asyxml></code><documentation>Cast a conic section to an hyperbola.</documentation></operator></asyxml>*/
+ hyperbola oh;
+ if(co.e > 1) {
+ real a=co.p/(co.e^2-1);
+ real c=co.e*a;
+ vector v=co.D.v;
+ if(sameside(co.D.A+v,co.F,co.D)) v=-v;
+ point f2=co.F+2*c*v;
+ f2=changecoordsys(co.F.coordsys,f2);
+ oh=hyperbola(co.F,f2,a);
+ } else
+ abort("casting: The conic section is not an hyperbola.");
+ return oh;
+}
+
+/*<asyxml><operator type="conic" signature="cast(hyperbola)"><code></asyxml>*/
+conic operator cast(hyperbola h)
+{/*<asyxml></code><documentation>Hyperbola to conic section.</documentation></operator></asyxml>*/
+ return conic(h.F1,h.D1,h.e);
+}
+
+/*<asyxml><operator type="conic" signature="cast(ellipse)"><code></asyxml>*/
+conic operator cast(ellipse el)
+{/*<asyxml></code><documentation>Ellipse to conic section.</documentation></operator></asyxml>*/
+ conic oc;
+ if(abs(el.c) > epsgeo) {
+ real x=el.a^2/el.c;
+ point O=(el.F1+el.F2)/2;
+ point A=O+x*unit(el.F1-el.F2);
+ oc=conic(el.F1,perpendicular(A,line(el.F1,el.F2)),el.e);
+ } else {//The ellipse is a circle
+ coordsys R=coordsys(el);
+ point M=el.F1+point(R,R.polar(el.a,0));
+ line l=line(rotate(90,M)*el.F1,M);
+ oc=conic(el.F1,l,0);
+ }
+ if(degenerate(el)) {
+ oc.p=infinity;
+ oc.h=infinity;
+ oc.l=new line[]{el.l};
+ }
+ return oc;
+}
+
+/*<asyxml><operator type="conic" signature="cast(circle)"><code></asyxml>*/
+conic operator cast(circle c)
+{/*<asyxml></code><documentation>Circle to conic section.</documentation></operator></asyxml>*/
+ return (conic)((ellipse)c);
+}
+
+/*<asyxml><operator type="circle" signature="cast(conic)"><code></asyxml>*/
+circle operator cast(conic c)
+{/*<asyxml></code><documentation>Conic section to circle.</documentation></operator></asyxml>*/
+ ellipse el=(ellipse)c;
+ circle oc;
+ if(abs(el.a-el.b) < epsgeo) {
+ oc=circle(el.C,el.a);
+ if(degenerate(c)) oc.l=c.l[0];
+ }
+ else abort("Can not cast this conic to a circle");
+ return oc;
+}
+
+/*<asyxml><operator type="ellipse" signature="*(transform,ellipse)"><code></asyxml>*/
+ellipse operator *(transform t, ellipse el)
+{/*<asyxml></code><documentation>Provide transform*ellipse.</documentation></operator></asyxml>*/
+ if(!degenerate(el)) {
+ point[] ep;
+ for (int i=0; i<360; i+=72) {
+ ep.push(t*angpoint(el,i));
+ }
+ ellipse oe=ellipse(ep[0],ep[1],ep[2],ep[3],ep[4]);
+ if(angpoint(oe,0) != ep[0]) return ellipse(oe.F2,oe.F1,oe.a);
+ return oe;
+ }
+ return ellipse(t*el.l.A,t*el.l.B,infinity);
+}
+
+/*<asyxml><operator type="parabola" signature="*(transform,parabola)"><code></asyxml>*/
+parabola operator *(transform t, parabola p)
+{/*<asyxml></code><documentation>Provide transform*parabola.</documentation></operator></asyxml>*/
+ point[] P;
+ P.push(t*angpoint(p,45));
+ P.push(t*angpoint(p,-45));
+ P.push(t*angpoint(p,180));
+ return parabola(P[0],P[1],P[2],t*p.D);
+}
+
+/*<asyxml><operator type="ellipse" signature="*(transform,circle)"><code></asyxml>*/
+ellipse operator *(transform t, circle c)
+{/*<asyxml></code><documentation>Provide transform*circle.
+ For example, 'circle C=scale(2)*circle' and 'ellipse E=xscale(2)*circle' are valid
+ but 'circle C=xscale(2)*circle' is invalid.</documentation></operator></asyxml>*/
+ return t*((ellipse)c);
+}
+
+/*<asyxml><operator type="hyperbola" signature="*(transform,hyperbola)"><code></asyxml>*/
+hyperbola operator *(transform t, hyperbola h)
+{/*<asyxml></code><documentation>Provide transform*hyperbola.</documentation></operator></asyxml>*/
+ point[] ep;
+ for (int i=90; i<=270; i+=45) {
+ ep.push(t*angpoint(h,i));
+ }
+ hyperbola oe=hyperbola(ep[0],ep[1],ep[2],ep[3],ep[4]);
+ if(angpoint(oe,90) != ep[0]) return hyperbola(oe.F2,oe.F1,oe.a);
+ return oe;
+}
+
+/*<asyxml><operator type="conic" signature="*(transform,conic)"><code></asyxml>*/
+conic operator *(transform t, conic co)
+{/*<asyxml></code><documentation>Provide transform*conic.</documentation></operator></asyxml>*/
+ if(co.e < 1) return (t*((ellipse)co));
+ if(co.e == 1) return (t*((parabola)co));
+ return (t*((hyperbola)co));
+}
+
+/*<asyxml><operator type="ellipse" signature="*(real,ellipse)"><code></asyxml>*/
+ellipse operator *(real x, ellipse el)
+{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(x,el.C)*el'.</documentation></operator></asyxml>*/
+ return degenerate(el) ? el : ellipse(el.C,x*el.a,x*el.b,el.angle);
+}
+
+/*<asyxml><operator type="ellipse" signature="/(ellipse,real)"><code></asyxml>*/
+ellipse operator /(ellipse el, real x)
+{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(1/x,el.C)*el'.</documentation></operator></asyxml>*/
+ return degenerate(el) ? el : ellipse(el.C,el.a/x,el.b/x,el.angle);
+}
+
+/*<asyxml><function type="path" signature="arcfromcenter(ellipse,real,real,int,bool)"><code></asyxml>*/
+path arcfromcenter(ellipse el, real angle1, real angle2,
+ bool direction=CCW,
+ int n=ellipsenodesnumber(el.a,el.b,angle1,angle2,direction))
+{/*<asyxml></code><documentation>Return the path of the ellipse 'el' from angle1 to angle2 in degrees,
+ drawing in the given direction, with n nodes.
+ The angles are mesured relatively to the axis (C,x-axis) where C is
+ the center of the ellipse.</documentation></function></asyxml>*/
+ if(degenerate(el)) abort("arcfromcenter: can not convert degenerated ellipse to path.");
+ guide op;
+ coordsys Rp=coordsys(el);
+ if (n < 1) return op;
+ if (angle1 > angle2)
+ return reverse(arcfromcenter(el,angle2,angle1,!direction,n));
+ real a1=direction ? radians(angle1) : radians(angle2);
+ real a2=direction ? radians(angle2) : radians(angle1)+2*pi;
+ real step=(a2-a1)/(n != 1 ? n-1 : 1);
+ real a,r;
+ real da=radians(el.angle);
+ for (int i=0; i < n; ++i) {
+ a=a1+i*step;
+ r=el.b/sqrt(1-(el.e*cos(a))^2);
+ op=op..Rp*Rp.polar(r,a+da);
+ }
+ return shift(el.C.x*Rp.i+el.C.y*Rp.j)*(direction ? op : reverse(op));
+}
+
+/*<asyxml><function type="path" signature="arcfromcenter(hyperbola,real,real,int,bool)"><code></asyxml>*/
+path arcfromcenter(hyperbola h, real angle1, real angle2,
+ int n=hyperbolanodesnumber(h,angle1,angle2),
+ bool direction=CCW)
+{/*<asyxml></code><documentation>Return the path of the hyperbola 'h' from angle1 to angle2 in degrees,
+ drawing in the given direction, with n nodes.
+ The angles are mesured relatively to the axis (C,x-axis) where C is
+ the center of the hyperbola.</documentation></function></asyxml>*/
+ guide op;
+ coordsys Rp=coordsys(h);
+ if (n < 1) return op;
+ if (angle1 > angle2) {
+ path g=reverse(arcfromcenter(h,angle2,angle1,n,!direction));
+ return g == nullpath ? g : reverse(g);
+ }
+ real a1=direction ? radians(angle1) : radians(angle2);
+ real a2=direction ? radians(angle2) : radians(angle1)+2*pi;
+ real step=(a2-a1)/(n != 1 ? n-1 : 1);
+ real a,r;
+ typedef guide interpolate(... guide[]);
+ interpolate join=operator ..;
+ real da=radians(h.angle);
+ for (int i=0; i < n; ++i) {
+ a=a1+i*step;
+ r=(h.b*cos(a))^2-(h.a*sin(a))^2;
+ if(r > epsgeo) {
+ r=sqrt(h.a^2*h.b^2/r);
+ op=join(op,Rp*Rp.polar(r,a+da));
+ join=operator ..;
+ } else join=operator --;
+ }
+ return shift(h.C.x*Rp.i+h.C.y*Rp.j)*
+ (direction ? op : op == nullpath ? op : reverse(op));
+}
+
+/*<asyxml><function type="path" signature="arcfromcenter(explicit conic,real,real,int,bool)"><code></asyxml>*/
+path arcfromcenter(explicit conic co, real angle1, real angle2,
+ int n, bool direction=CCW)
+{/*<asyxml></code><documentation>Use arcfromcenter(ellipse,...) or arcfromcenter(hyperbola,...) depending of
+ the eccentricity of 'co'.</documentation></function></asyxml>*/
+ path g;
+ if(co.e < 1)
+ g=arcfromcenter((ellipse)co,angle1,
+ angle2,direction,n);
+ else if(co.e > 1)
+ g=arcfromcenter((hyperbola)co,angle1,
+ angle2,n,direction);
+ else abort("arcfromcenter: does not exist for a parabola.");
+ return g;
+}
+
+/*<asyxml><constant type="polarconicroutine" signature="fromCenter"><code></asyxml>*/
+restricted polarconicroutine fromCenter=arcfromcenter;/*<asyxml></code><documentation></documentation></constant></asyxml>*/
+/*<asyxml><constant type="polarconicroutine" signature="fromFocus"><code></asyxml>*/
+restricted polarconicroutine fromFocus=arcfromfocus;/*<asyxml></code><documentation></documentation></constant></asyxml>*/
+
+/*<asyxml><function type="bqe" signature="equation(ellipse)"><code></asyxml>*/
+bqe equation(ellipse el)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of the ellipse in its coordinate system:
+ bqe.a[0]*x^2 + bqe.a[1]*x*y + bqe.a[2]*y^2 + bqe.a[3]*x + bqe.a[4]*y + bqe.a[5]=0.
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ pair[] pts;
+ for (int i=0; i<360; i+=72)
+ pts.push(locate(angpoint(el,i)));
+
+ real[][] M;
+ real[] x;
+ for (int i=0; i < 5; ++i) {
+ M[i]=new real[] {pts[i].x*pts[i].y,pts[i].y^2,pts[i].x,pts[i].y,1};
+ x[i]=-pts[i].x^2;
+ }
+ real[] coef=solve(M,x);
+ bqe bqe=changecoordsys(coordsys(el),
+ bqe(defaultcoordsys,
+ 1,coef[0],coef[1],coef[2],coef[3],coef[4]));
+ bqe.a=approximate(bqe.a);
+ return bqe;
+}
+
+/*<asyxml><function type="bqe" signature="equation(parabola)"><code></asyxml>*/
+bqe equation(parabola p)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of the parabola in its coordinate system.
+ bqe.a[0]*x^2 + bqe.a[1]*x*y + bqe.a[2]*y^2 + bqe.a[3]*x + bqe.a[4]*y + bqe.a[5]=0
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ coordsys R=canonicalcartesiansystem(p);
+ parabola tp=changecoordsys(R,p);
+ point A=projection(tp.D)*point(R,(0,0));
+ real a=abs(A);
+ return changecoordsys(coordsys(p),
+ bqe(R,0,0,1,-4*a,0,0));
+}
+
+/*<asyxml><function type="bqe" signature="equation(hyperbola)"><code></asyxml>*/
+bqe equation(hyperbola h)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of the hyperbola in its coordinate system.
+ bqe.a[0]*x^2 + bqe.a[1]*x*y + bqe.a[2]*y^2 + bqe.a[3]*x + bqe.a[4]*y + bqe.a[5]=0
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ coordsys R=canonicalcartesiansystem(h);
+ return changecoordsys(coordsys(h),
+ bqe(R,1/h.a^2,0,-1/h.b^2,0,0,-1));
+}
+
+/*<asyxml><operator type="path" signature="cast(ellipse)"><code></asyxml>*/
+path operator cast(ellipse el)
+{/*<asyxml></code><documentation>Cast ellipse to path.</documentation></operator></asyxml>*/
+ if(degenerate(el))
+ abort("Casting degenerated ellipse to path is not possible.");
+ int n=el.e == 0 ? circlenodesnumber(el.a) : ellipsenodesnumber(el.a,el.b);
+ return arcfromcenter(el,0.0,360,CCW,n)&cycle;
+}
+
+/*<asyxml><operator type="path" signature="cast(circle)"><code></asyxml>*/
+path operator cast(circle c)
+{/*<asyxml></code><documentation>Cast circle to path.</documentation></operator></asyxml>*/
+ return (path)((ellipse)c);
+}
+
+/*<asyxml><function type="real[]" signature="bangles(picture,parabola)"><code></asyxml>*/
+real[] bangles(picture pic=currentpicture, parabola p)
+{/*<asyxml></code><documentation>Return the array {ma, Ma} where 'ma' and 'Ma' are respectively
+ the smaller and the larger angles for which the parabola 'p' is included
+ in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/
+ pair bmin,bmax;
+ pair[] b;
+ if (p.bmin == p.bmax) {
+ bmin=pic.userMin();
+ bmax=pic.userMax();
+ } else {
+ bmin=p.bmin;bmax=p.bmax;
+ }
+ if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax)))
+ return new real[] {0,0};
+ b[0]=bmin;
+ b[1]=(bmax.x,bmin.y);
+ b[2]=bmax;
+ b[3]=(bmin.x,bmax.y);
+ real[] eq=changecoordsys(defaultcoordsys,equation(p)).a;
+ pair[] inter;
+ for (int i=0; i < 4; ++i) {
+ pair[] tmp=intersectionpoints(b[i],b[(i+1)%4],eq);
+ for (int j=0; j < tmp.length; ++j) {
+ if(dot(b[i]-tmp[j],b[(i+1)%4]-tmp[j]) <= epsgeo)
+ inter.push(tmp[j]);
+ }
+ }
+ pair F=p.F, V=p.V;
+ real d=degrees(F-V);
+ real[] a=sequence(new real(int n){
+ return (360-d+degrees(inter[n]-F))%360;
+ }, inter.length);
+ real ma=a.length != 0 ? min(a) : 0, Ma= a.length != 0 ? max(a) : 0;
+ return new real[] {ma,Ma};
+}
+
+/*<asyxml><function type="real[][]" signature="bangles(picture,hyperbola)"><code></asyxml>*/
+real[][] bangles(picture pic=currentpicture, hyperbola h)
+{/*<asyxml></code><documentation>Return the array {{ma1, Ma1}, {ma2, Ma2}} where 'maX' and 'MaX' are respectively
+ the smaller and the bigger angles (from h.FX) for which the hyperbola 'h' is included
+ in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/
+ pair bmin,bmax;
+ pair[] b;
+ if (h.bmin == h.bmax) {
+ bmin=pic.userMin();
+ bmax=pic.userMax();
+ } else {
+ bmin=h.bmin;bmax=h.bmax;
+ }
+ if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax)))
+ return new real[][] {{0,0}, {0,0}};
+ b[0]=bmin;
+ b[1]=(bmax.x,bmin.y);
+ b[2]=bmax;
+ b[3]=(bmin.x,bmax.y);
+ real[] eq=changecoordsys(defaultcoordsys,equation(h)).a;
+ pair[] inter0,inter1;
+ pair C=locate(h.C);
+ pair F1=h.F1;
+ for (int i=0; i < 4; ++i) {
+ pair[] tmp=intersectionpoints(b[i],b[(i+1)%4],eq);
+ for (int j=0; j < tmp.length; ++j) {
+ if(dot(b[i]-tmp[j],b[(i+1)%4]-tmp[j]) <= epsgeo) {
+ if(dot(F1-C,tmp[j]-C) > 0) inter0.push(tmp[j]);
+ else inter1.push(tmp[j]);
+ }
+ }
+ }
+ real d=degrees(F1-C);
+ real[] ma, Ma;
+ pair[][] inter=new pair[][] {inter0, inter1};
+ for (int i=0; i < 2; ++i) {
+ real[] a=sequence(new real(int n){
+ return (360-d+degrees(inter[i][n]-F1))%360;
+ },inter[i].length);
+ ma[i]=a.length != 0 ? min(a) : 0;
+ Ma[i]= a.length != 0 ? max(a) : 0;
+ }
+ return new real[][] {{ma[0],Ma[0]}, {ma[1],Ma[1]}};
+}
+
+/*<asyxml><operator type="path" signature="cast(parabola)"><code></asyxml>*/
+path operator cast(parabola p)
+{/*<asyxml></code><documentation>Cast parabola to path.
+ If possible, the returned path is restricted to the actual bounding box
+ of the current picture if the variables 'p.bmin' and 'p.bmax' are not set else
+ the bounding box of box(p.bmin, p.bmax) is used instead.</documentation></operator></asyxml>*/
+ real[] bangles=bangles(p);
+ int n=parabolanodesnumber(p,bangles[0],bangles[1]);
+ return arcfromfocus(p,bangles[0],bangles[1],n,CCW);
+}
+
+
+/*<asyxml><function type="void" signature="draw(picture,Label,circle,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="",circle c,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None,
+ margin margin=NoMargin, Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ if(degenerate(c)) draw(pic,L,c.l,align,p,arrow,legend,marker);
+ else draw(pic,L,(path)c,align,p,arrow,bar,margin,legend,marker);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,ellipse,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="",ellipse el,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None,
+ margin margin=NoMargin, Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation></documentation>Draw the ellipse 'el' if it is not degenerated else draw 'el.l'.</function></asyxml>*/
+ if(degenerate(el)) draw(pic,L,el.l,align,p,arrow,legend,marker);
+ else draw(pic,L,(path)el,align,p,arrow,bar,margin,legend,marker);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,parabola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="",parabola parabola,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None,
+ margin margin=NoMargin, Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation>Draw the parabola 'p' on 'pic' without (if possible) altering the
+ size of picture pic.</documentation></function></asyxml>*/
+ pic.add(new void (frame f, transform t, transform, pair m, pair M) {
+ // Reduce the bounds by the size of the pen and the margins.
+ m -= min(p); M -= max(p);
+ parabola.bmin=inverse(t)*m; parabola.bmax=inverse(t)*M;
+ picture tmp;
+ draw(tmp,L,t*(path) parabola,align,p,arrow,bar,NoMargin,legend,marker);
+ add(f,tmp.fit());
+ });
+ if(pic.userMin.x != pic.userMax.x & pic.userMin.y != pic.userMax.y &
+ !finite(abs(pic.userMin)) & !finite(abs(pic.userMax)))
+ pic.addBox(truepoint(SW), truepoint(NE));
+}
+
+/*<asyxml><operator type="path" signature="cast(hyperbola)"><code></asyxml>*/
+path operator cast(hyperbola h)
+{/*<asyxml></code><documentation>Cast hyperbola to path.
+ If possible, the returned path is restricted to the actual bounding box
+ of the current picture unless the variables 'h.bmin' and 'h.bmax'
+ are set; in this case the bounding box of box(h.bmin, h.bmax) is used instead.
+ Only the branch on the side of 'h.F1' is considered.</documentation></operator></asyxml>*/
+ real[][] bangles=bangles(h);
+ int n=hyperbolanodesnumber(h,bangles[0][0],bangles[0][1]);
+ return arcfromfocus(h,bangles[0][0],bangles[0][1],n,CCW);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,hyperbola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="", hyperbola h,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None,
+ margin margin=NoMargin, Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation>Draw the hyperbola 'h' on 'pic' without (if possible) altering the
+ size of the picture pic.</documentation></function></asyxml>*/
+ pic.add(new void (frame f, transform t, transform, pair m, pair M) {
+ // Reduce the bounds by the size of the pen and the margins.
+ m -= min(p); M -= max(p);
+ h.bmin=inverse(t)*m; h.bmax=inverse(t)*M;
+ picture tmp;
+ draw(tmp,L,t*(path) h,align,p,arrow,bar,NoMargin,legend,marker);
+ hyperbola ht=hyperbola(h.F2,h.F1,h.a);
+ ht.bmin=inverse(t)*m; ht.bmax=inverse(t)*M;
+ draw(tmp,"",t*(path) ht,align,p,arrow,bar,NoMargin,marker);
+ add(f,tmp.fit());
+ });
+ if(pic.userMin.x != pic.userMax.x & pic.userMin.y != pic.userMax.y &
+ !finite(abs(pic.userMin)) & !finite(abs(pic.userMax)))
+ pic.addBox(truepoint(SW), truepoint(NE));
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,explicit conic,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="", explicit conic co,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None,
+ margin margin=NoMargin, Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation>Use one of the routine 'draw(ellipse,...)',
+ 'draw(parabola,...)' or 'draw(hyperbola,...)' depending of the value of eccentricity of 'co'.</documentation></function></asyxml>*/
+ if(co.e == 0)
+ draw(pic,L,(circle)co,align,p,arrow,bar,margin,legend,marker);
+ else
+ if(co.e < 1) draw(pic,L,(ellipse)co,align,p,arrow,bar,margin,legend,marker);
+ else
+ if(co.e == 1) draw(pic,L,(parabola)co,align,p,arrow,bar,margin,legend,marker);
+ else
+ if(co.e > 1) draw(pic,L,(hyperbola)co,align,p,arrow,bar,margin,legend,marker);
+ else abort("draw: unknown conic.");
+}
+
+/*<asyxml><function type="int" signature="conicnodesnumber(conic,real,real)"><code></asyxml>*/
+int conicnodesnumber(conic co, real angle1, real angle2, bool dir=CCW)
+{/*<asyxml></code><documentation>Return the number of node to draw a conic arc.</documentation></function></asyxml>*/
+ int oi;
+ if(co.e == 0) {
+ circle c=(circle)co;
+ oi=circlenodesnumber(c.r,angle1,angle2);
+ } else if(co.e < 1) {
+ ellipse el=(ellipse)co;
+ oi=ellipsenodesnumber(el.a,el.b,angle1,angle2,dir);
+ } else if(co.e == 1) {
+ parabola p=(parabola)co;
+ oi=parabolanodesnumber(p, angle1, angle2);
+ } else {
+ hyperbola h=(hyperbola)co;
+ oi=hyperbolanodesnumber(h, angle1, angle2);
+ }
+ return oi;
+}
+
+/*<asyxml><operator type="path" signature="cast(conic)"><code></asyxml>*/
+path operator cast(conic co)
+{/*<asyxml></code><documentation>Cast conic section to path.</documentation></operator></asyxml>*/
+ if(co.e < 1) return (path)((ellipse)co);
+ if(co.e == 1) return (path)((parabola)co);
+ return (path)((hyperbola)co);
+}
+
+/*<asyxml><function type="bqe" signature="equation(explicit conic)"><code></asyxml>*/
+bqe equation(explicit conic co)
+{/*<asyxml></code><documentation>Return the coefficients of the equation of conic section in its coordinate system:
+ bqe.a[0]*x^2 + bqe.a[1]*x*y + bqe.a[2]*y^2 + bqe.a[3]*x + bqe.a[4]*y + bqe.a[5]=0.
+ One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
+ bqe obqe;
+ if(co.e == 0)
+ obqe=equation((circle)co);
+ else
+ if(co.e < 1) obqe=equation((ellipse)co);
+ else
+ if(co.e == 1) obqe=equation((parabola)co);
+ else
+ if(co.e > 1) obqe=equation((hyperbola)co);
+ else abort("draw: unknown conic.");
+ return obqe;
+}
+
+/*<asyxml><function type="string" signature="conictype(bqe)"><code></asyxml>*/
+string conictype(bqe bqe)
+{/*<asyxml></code><documentation>Returned values are "ellipse" or "parabola" or "hyperbola"
+ depending of the conic section represented by 'bqe'.</documentation></function></asyxml>*/
+ bqe lbqe=changecoordsys(defaultcoordsys,bqe);
+ string os="degenerated";
+ real a=lbqe.a[0], b=lbqe.a[1]/2, c=lbqe.a[2], d=lbqe.a[3]/2, f=lbqe.a[4]/2, g=lbqe.a[5];
+ real delta=a*c*g + b*f*d + d*b*f - (b^2*g + d^2*c + f^2*a);
+ if(abs(delta) < 10*epsgeo) return os;
+ real J=a*c-b^2;
+ real I=a+c;
+ if(J > epsgeo) {
+ if(delta/I < -epsgeo);
+ os="ellipse";
+ } else {
+ if(abs(J) < epsgeo) os="parabola"; else os="hyperbola";
+ }
+ return os;
+}
+
+/*<asyxml><function type="conic" signature="conic(point,point,point,point,point)"><code></asyxml>*/
+conic conic(point M1, point M2, point M3, point M4, point M5)
+{/*<asyxml></code><documentation>Return the conic passing through 'M1', 'M2', 'M3', 'M4' and 'M5' if the conic is not degenerated.</documentation></function></asyxml>*/
+ bqe bqe=bqe(M1,M2,M3,M4,M5);
+ string ct=conictype(bqe);
+ if(ct == "degenerated") abort("conic: degenerated conic passing through five points.");
+ if(ct == "ellipse") return ellipse(bqe);
+ if(ct == "parabola") return parabola(bqe);
+ return hyperbola(bqe);
+}
+
+/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/
+coordsys canonicalcartesiansystem(explicit conic co)
+{/*<asyxml></code><documentation>Return the canonical cartesian system of the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e < 1) return canonicalcartesiansystem((ellipse)co);
+ else if(co.e == 1) return canonicalcartesiansystem((parabola)co);
+ return canonicalcartesiansystem((hyperbola)co);
+}
+
+/*<asyxml><function type="bqe" signature="canonical(bqe)"><code></asyxml>*/
+bqe canonical(bqe bqe)
+{/*<asyxml></code><documentation>Return the bivariate quadratic equation relative to the
+ canonical coordinate system of the conic section represented by 'bqe'.</documentation></function></asyxml>*/
+ string type=conictype(bqe);
+ if(type == "") abort("canonical: the equation can not be performed.");
+ bqe obqe;
+ if(type == "ellipse") {
+ ellipse el=ellipse(bqe);
+ obqe=changecoordsys(canonicalcartesiansystem(el),equation(el));
+ } else {
+ if(type == "parabola") {
+ parabola p=parabola(bqe);
+ obqe=changecoordsys(canonicalcartesiansystem(p),equation(p));
+ } else {
+ hyperbola h=hyperbola(bqe);
+ obqe=changecoordsys(canonicalcartesiansystem(h),equation(h));
+ }
+ }
+ return obqe;
+}
+
+/*<asyxml><function type="conic" signature="conic(bqe)"><code></asyxml>*/
+conic conic(bqe bqe)
+{/*<asyxml></code><documentation>Return the conic section represented by the bivariate quartic equation 'bqe'.</documentation></function></asyxml>*/
+ string type=conictype(bqe);
+ if(type == "") abort("canonical: the equation can not be performed.");
+ conic oc;
+ if(type == "ellipse") {
+ oc=ellipse(bqe);
+ } else {
+ if(type == "parabola") oc=parabola(bqe); else oc=hyperbola(bqe);
+ }
+ return oc;
+}
+
+/*<asyxml><function type="real" signature="arclength(circle)"><code></asyxml>*/
+real arclength(circle c)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return c.r*2*pi;
+}
+
+/*<asyxml><function type="real" signature="focusToCenter(ellipse,real)"><code></asyxml>*/
+real focusToCenter(ellipse el, real a)
+{/*<asyxml></code><documentation>Return the angle relatively to the center of 'el' for the angle 'a'
+ given relatively to the focus of 'el'.</documentation></function></asyxml>*/
+ pair p=point(fromFocus(el,a,a,1,CCW),0);
+ pair c=locate(el.C);
+ real d=degrees(p-c)-el.angle;
+ d=abs(d) < epsgeo ? 0 : d; // Avoid -1e-15
+ return d%(sgnd(a)*360);
+}
+
+/*<asyxml><function type="real" signature="centerToFocus(ellipse,real)"><code></asyxml>*/
+real centerToFocus(ellipse el, real a)
+{/*<asyxml></code><documentation>Return the angle relatively to the focus of 'el' for the angle 'a'
+ given relatively to the center of 'el'.</documentation></function></asyxml>*/
+ pair P=point(fromCenter(el,a,a,1,CCW),0);
+ pair F1=locate(el.F1);
+ pair F2=locate(el.F2);
+ real d=degrees(P-F1)-degrees(F2-F1);
+ d=abs(d) < epsgeo ? 0 : d; // Avoid -1e-15
+ return d%(sgnd(a)*360);
+}
+
+/*<asyxml><function type="real" signature="arclength(ellipse)"><code></asyxml>*/
+real arclength(ellipse el)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return degenerate(el) ? infinity : 4*el.a*elle(pi/2,el.e);
+}
+
+/*<asyxml><function type="real" signature="arclength(ellipse,real,real,bool,polarconicroutine)"><code></asyxml>*/
+real arclength(ellipse el, real angle1, real angle2,
+ bool direction=CCW,
+ polarconicroutine polarconicroutine=currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the length of the arc of the ellipse between 'angle1'
+ and 'angle2'.
+ 'angle1' and 'angle2' must be in the interval ]-360;+oo[ if polarconicroutine=fromFocus,
+ ]-oo;+oo[ if polarconicroutine=fromCenter.</documentation></function></asyxml>*/
+ if(degenerate(el)) return infinity;
+ if(angle1 > angle2) return arclength(el,angle2,angle1,!direction,polarconicroutine);
+ // path g;int n=1000;
+ // if(el.e == 0) g=arcfromcenter(el,angle1,angle2,n,direction);
+ // if(el.e != 1) g=polarconicroutine(el,angle1,angle2,n,direction);
+ // write("with path=",arclength(g));
+ if(polarconicroutine == fromFocus) {
+ // dot(point(fromFocus(el,angle1,angle1,1,CCW),0),2mm+blue);
+ // dot(point(fromFocus(el,angle2,angle2,1,CCW),0),2mm+blue);
+ // write("fromfocus1=",angle1);
+ // write("fromfocus2=",angle2);
+ real gle1=focusToCenter(el,angle1);
+ real gle2=focusToCenter(el,angle2);
+ if((gle1-gle2)*(angle1-angle2) > 0) {
+ angle1=gle1; angle2=gle2;
+ } else {
+ angle1=gle2; angle2=gle1;
+ }
+ // dot(point(fromCenter(el,angle1,angle1,1,CCW),0),1mm+red);
+ // dot(point(fromCenter(el,angle2,angle2,1,CCW),0),1mm+red);
+ // write("fromcenter1=",angle1);
+ // write("fromcenter2=",angle2);
+ }
+ if(angle1 < 0 || angle2 < 0) return arclength(el,180+angle1,180+angle2,direction,fromCenter);
+ real a1=direction ? angle1 : angle2;
+ real a2=direction ? angle2 : angle1+360;
+ real elleq=el.a*elle(pi/2,el.e);
+ real S(real a)
+ {//Return the arclength from 0 to the angle 'a' (in degrees)
+ // given form the center of the ellipse.
+ real gle=atan(el.a*Tan(a)/el.b)+
+ pi*(((a%90 == 0 && a != 0) ? floor(a/90)-1 : floor(a/90)) -
+ ((a%180 == 0) ? 0 : floor(a/180)) -
+ (a%360 == 0 ? floor(a/(360)) : 0));
+ /* // Uncomment to visualize the used branches
+ unitsize(2cm,1cm);
+ import graph;
+
+ real xmin=0, xmax=3pi;
+
+ xlimits( xmin,xmax);
+ ylimits( 0,10);
+ yaxis( "y" ,LeftRight(),RightTicks(pTick=.8red,ptick=lightgrey,extend=true));
+ xaxis( "x-value",BottomTop(),Ticks(Label("$%.2f$",red),Step=pi/2,step=pi/4,pTick=.8red,ptick=lightgrey,extend=true));
+
+ real p2=pi/2;
+ real f(real t)
+ {
+ return atan(0.6*tan(t))+
+ pi*((t%p2 == 0 && t != 0) ? floor(t/p2)-1 : floor(t/p2)) -
+ ((t%pi == 0) ? 0 : pi*floor(t/pi)) - (t%(2pi) == 0 ? pi*floor(t/(2*pi)) : 0);
+ }
+
+ draw(graph(f,xmin,xmax,100));
+ write(degrees(f(pi/2)));
+ write(degrees(f(pi)));
+ write(degrees(f(3pi/2)));
+ write(degrees(f(2pi)));
+ draw(graph(new real(real t){return t;},xmin,xmax,3));
+ */
+ return elleq-el.a*elle(pi/2-gle,el.e);
+ }
+ return S(a2)-S(a1);
+}
+
+/*<asyxml><function type="real" signature="arclength(parabola,real)"><code></asyxml>*/
+real arclength(parabola p, real angle)
+{/*<asyxml></code><documentation>Return the arclength from 180 to 'angle' given from focus in the
+ canonical coordinate system of 'p'.</documentation></function></asyxml>*/
+ real a=p.a; /* In canonicalcartesiansystem(p) the equation of p
+ is x=y^2/(4a) */
+ // integrate(sqrt(1+(x/(2*a))^2),x);
+ real S(real t){return 0.5*t*sqrt(1+t^2/(4*a^2))+a*asinh(t/(2*a));}
+ real R(real gle){return 2*a/(1-Cos(gle));}
+ real t=Sin(angle)*R(angle);
+ return S(t);
+}
+
+/*<asyxml><function type="real" signature="arclength(parabola,real,real)"><code></asyxml>*/
+real arclength(parabola p, real angle1, real angle2)
+{/*<asyxml></code><documentation>Return the arclength from 'angle1' to 'angle2' given from
+ focus in the canonical coordinate system of 'p'</documentation></function></asyxml>*/
+ return arclength(p,angle1)-arclength(p,angle2);
+}
+
+/*<asyxml><function type="real" signature="arclength(parabola p)"><code></asyxml>*/
+real arclength(parabola p)
+{/*<asyxml></code><documentation>Return the length of the arc of the parabola bounded to the bounding
+ box of the current picture.</documentation></function></asyxml>*/
+ real[] b=bangles(p);
+ return arclength(p,b[0],b[1]);
+}
+// *........................CONICS.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.......................ABSCISSA........................*
+/*<asyxml><struct signature="abscissa"><code></asyxml>*/
+struct abscissa
+{/*<asyxml></code><documentation>Provide abscissa structure on a curve used in the routine-like 'point(object,abscissa)'
+ where object can be 'line', 'segment', 'ellipse', 'circle', 'conic'...</documentation><property type="real" signature="x"><code></asyxml>*/
+ real x;/*<asyxml></code><documentation>The abscissa value.</documentation></property><property type="int" signature="system"><code></asyxml>*/
+ int system;/*<asyxml></code><documentation>0=relativesystem; 1=curvilinearsystem; 2=angularsystem; 3=nodesystem</documentation></property><property type="polarconicroutine" signature="polarconicroutine"><code></asyxml>*/
+ polarconicroutine polarconicroutine=fromCenter;/*<asyxml></code><documentation>The routine used with angular system and two foci conic section.
+ Possible values are 'formCenter' and 'formFocus'.</documentation></property></asyxml>*/
+ /*<asyxml><method type="abscissa" signature="copy()"><code></asyxml>*/
+ abscissa copy()
+ {/*<asyxml></code><documentation>Return a copy of this abscissa.</documentation></method></asyxml>*/
+ abscissa oa=new abscissa;
+ oa.x=this.x;
+ oa.system=this.system;
+ oa.polarconicroutine=this.polarconicroutine;
+ return oa;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><constant type="int" signature="relativesystem, curvilinearsystem, angularsystem, nodesystem"><code></asyxml>*/
+restricted int relativesystem=0, curvilinearsystem=1, angularsystem=2, nodesystem=3;/*<asyxml></code><documentation>Constant used to set the abscissa system.</documentation></constant></asyxml>*/
+
+/*<asyxml><operator type="abscissa" signature="cast(explicit position)"><code></asyxml>*/
+abscissa operator cast(explicit position position)
+{/*<asyxml></code><documentation>Cast position to abscissa.
+ If 'position' is relative, the abscissa is relative else it's a curvilinear abscissa.</documentation></operator></asyxml>*/
+ abscissa oarcc;
+ oarcc.x=position.position.x;
+ oarcc.system=position.relative ? relativesystem : curvilinearsystem;
+ return oarcc;
+}
+
+/*<asyxml><operator type="abscissa" signature="+(real,explicit abscissa)"><code></asyxml>*/
+abscissa operator +(real x, explicit abscissa a)
+{/*<asyxml></code><documentation>Provide 'real+abscissa'.
+ Return abscissa b so that b.x=a.x+x.
+ +(explicit abscissa,real), -(real,explicit abscissa) and -(explicit abscissa,real) are also defined.</documentation></operator></asyxml>*/
+ abscissa oa=a.copy();
+ oa.x=a.x+x;
+ return oa;
+}
+abscissa operator +(explicit abscissa a, real x)
+{
+ return x+a;
+}
+abscissa operator +(int x, explicit abscissa a)
+{
+ return ((real)x)+a;
+}
+abscissa operator +(explicit abscissa a, int x)
+{
+ return ((real)x)+a;
+}
+
+/*<asyxml><operator type="abscissa" signature="-(explicit abscissa a)"><code></asyxml>*/
+abscissa operator -(explicit abscissa a)
+{/*<asyxml></code><documentation>Return the abscissa b so that b.x=-a.x.</documentation></operator></asyxml>*/
+ abscissa oa;
+ oa.system=a.system;
+ oa.x=-a.x;
+ return oa;
+}
+
+abscissa operator -(real x, explicit abscissa a)
+{
+ abscissa oa;
+ oa.system=a.system;
+ oa.x=x-a.x;
+ return oa;
+}
+abscissa operator -(explicit abscissa a, real x)
+{
+ abscissa oa;
+ oa.system=a.system;
+ oa.x=a.x-x;
+ return oa;
+}
+abscissa operator -(int x, explicit abscissa a)
+{
+ return ((real)x)-a;
+}
+abscissa operator -(explicit abscissa a, int x)
+{
+ return a-((real)x);
+}
+
+/*<asyxml><operator type="abscissa" signature="*(real,abscissa)"><code></asyxml>*/
+abscissa operator *(real x, explicit abscissa a)
+{/*<asyxml></code><documentation>Provide 'real*abscissa'.
+ Return abscissa b so that b.x=x*a.x.
+ *(explicit abscissa,real), /(real,explicit abscissa) and /(explicit abscissa,real) are also defined.</documentation></operator></asyxml>*/
+ abscissa oa;
+ oa.system=a.system;
+ oa.x=a.x*x;
+ return oa;
+}
+abscissa operator *(explicit abscissa a, real x)
+{
+ return x*a;
+}
+
+abscissa operator *(int x, explicit abscissa a)
+{
+ return ((real)x)*a;
+}
+abscissa operator *(explicit abscissa a, int x)
+{
+ return ((real)x)*a;
+}
+
+abscissa operator /(real x, explicit abscissa a)
+{
+ abscissa oa;
+ oa.system=a.system;
+ oa.x=x/a.x;
+ return oa;
+}
+abscissa operator /(explicit abscissa a, real x)
+{
+ abscissa oa;
+ oa.system=a.system;
+ oa.x=a.x/x;
+ return oa;
+}
+
+abscissa operator /(int x, explicit abscissa a)
+{
+ return ((real)x)/a;
+}
+abscissa operator /(explicit abscissa a, int x)
+{
+ return a/((real)x);
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(real)"><code></asyxml>*/
+abscissa relabscissa(real x)
+{/*<asyxml></code><documentation>Return a relative abscissa.</documentation></function></asyxml>*/
+ return (abscissa)(Relative(x));
+}
+abscissa relabscissa(int x)
+{
+ return (abscissa)(Relative(x));
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(real)"><code></asyxml>*/
+abscissa curabscissa(real x)
+{/*<asyxml></code><documentation>Return a curvilinear abscissa.</documentation></function></asyxml>*/
+ return (abscissa)((position)x);
+}
+abscissa curabscissa(int x)
+{
+ return (abscissa)((position)x);
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(real,polarconicroutine)"><code></asyxml>*/
+abscissa angabscissa(real x, polarconicroutine polarconicroutine=currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return a angular abscissa.</documentation></function></asyxml>*/
+ abscissa oarcc;
+ oarcc.x=x;
+ oarcc.polarconicroutine=polarconicroutine;
+ oarcc.system=angularsystem;
+ return oarcc;
+}
+abscissa angabscissa(int x, polarconicroutine polarconicroutine=currentpolarconicroutine)
+{
+ return angabscissa((real)x, polarconicroutine);
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(real)"><code></asyxml>*/
+abscissa nodabscissa(real x)
+{/*<asyxml></code><documentation>Return an abscissa as time on the path.</documentation></function></asyxml>*/
+ abscissa oarcc;
+ oarcc.x=x;
+ oarcc.system=nodesystem;
+ return oarcc;
+}
+abscissa nodabscissa(int x)
+{
+ return nodabscissa((real)x);
+}
+
+/*<asyxml><operator type="abscissa" signature="cast(real)"><code></asyxml>*/
+abscissa operator cast(real x)
+{/*<asyxml></code><documentation>Cast real to abscissa, precisely 'nodabscissa'.</documentation></operator></asyxml>*/
+ return nodabscissa(x);
+}
+abscissa operator cast(int x)
+{
+ return nodabscissa((real)x);
+}
+
+/*<asyxml><function type="point" signature="point(circle,abscissa)"><code></asyxml>*/
+point point(circle c, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'c' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ coordsys R=c.C.coordsys;
+ if (l.system == nodesystem)
+ return point(R,point((path)c,l.x)/R);
+ if (l.system == relativesystem)
+ return c.C+point(R,R.polar(c.r,2*pi*l.x));
+ if (l.system == curvilinearsystem)
+ return c.C+point(R,R.polar(c.r,l.x/c.r));
+ if (l.system == angularsystem)
+ return c.C+point(R,R.polar(c.r,radians(l.x)));
+ abort("point: bad abscissa system.");
+ return (0,0);
+}
+
+/*<asyxml><function type="point" signature="point(ellipse,abscissa)"><code></asyxml>*/
+point point(ellipse el, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'el' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ if(el.e == 0) return point((circle)el, l);
+ coordsys R=coordsys(el);
+ if (l.system == nodesystem)
+ return point(R,point((path)el,l.x)/R);
+ if (l.system == relativesystem) {
+ return point(el,curabscissa((l.x%1)*arclength(el)));
+ }
+ if (l.system == curvilinearsystem) {
+ real a1=0, a2=360, cx=0;
+ real aout=a1;
+ real x=abs(l.x)%arclength(el);
+ while (abs(cx-x) > epsgeo) {
+ aout=(a1+a2)/2;
+ cx=arclength(el,0,aout,CCW,fromCenter); //fromCenter is speeder
+ if(cx > x) a2=(a1+a2)/2; else a1=(a1+a2)/2;
+ }
+ path pel=fromCenter(el,sgn(l.x)*aout,sgn(l.x)*aout,1,CCW);
+ return point(R,point(pel,0)/R);
+ }
+ if (l.system == angularsystem) {
+ return point(R,point(l.polarconicroutine(el,l.x,l.x,1,CCW),0)/R);
+ }
+ abort("point: bad abscissa system.");
+ return (0,0);
+}
+
+/*<asyxml><function type="point" signature="point(parabola,abscissa)"><code></asyxml>*/
+point point(parabola p, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'p' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ coordsys R=coordsys(p);
+ if (l.system == nodesystem)
+ return point(R,point((path)p,l.x)/R);
+ if (l.system == relativesystem) {
+ real[] b=bangles(p);
+ real al=sgn(l.x) > 0 ? arclength(p,180,b[1]) : arclength(p,180,b[0]);
+ return point(p,curabscissa(abs(l.x)*al));
+ }
+ if (l.system == curvilinearsystem) {
+ real a1=1e-3,a2=360-1e-3, cx=infinity;
+ while (abs(cx-l.x) > epsgeo) {
+ cx=arclength(p,180,(a1+a2)/2);
+ if(cx > l.x) a2=(a1+a2)/2; else a1=(a1+a2)/2;
+ }
+ path pp=fromFocus(p,a1,a1,1,CCW);
+ return point(R,point(pp,0)/R);
+ }
+ if (l.system == angularsystem) {
+ return point(R,point(fromFocus(p, l.x, l.x, 1, CCW), 0)/R);
+ }
+ abort("point: bad abscissa system.");
+ return (0,0);
+}
+
+/*<asyxml><function type="point" signature="point(hyperbola,abscissa)"><code></asyxml>*/
+point point(hyperbola h, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'h' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
+ coordsys R=coordsys(h);
+ if (l.system == nodesystem)
+ return point(R,point((path)h,l.x)/R);
+ if (l.system == relativesystem) {
+ abort("point(hyperbola,relativeSystem) is not implemented...
+Try relpoint((path)your_hyperbola,x);");
+ }
+ if (l.system == curvilinearsystem) {
+ abort("point(hyperbola,curvilinearSystem) is not implemented...");
+ }
+ if (l.system == angularsystem) {
+ return point(R,point(l.polarconicroutine(h,l.x,l.x,1,CCW),0)/R);
+ }
+ abort("point: bad abscissa system.");
+ return (0,0);
+}
+
+/*<asyxml><function type="abscissa" signature="point(conic,point)"><code></asyxml>*/
+point point(explicit conic co, abscissa l)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e == 0) return point((circle)co, l);
+ if(co.e < 1) return point((ellipse)co, l);
+ if(co.e == 1) return point((parabola)co, l);
+ return point((hyperbola)co, l);
+}
+
+
+/*<asyxml><function type="point" signature="point(line,abscissa)"><code></asyxml>*/
+point point(line l, abscissa x)
+{/*<asyxml></code><documentation>Return the point of 'l' which has the abscissa 'l.x' according to the abscissa system 'l.system'.
+ Note that the origin is l.A, and point(l, relabscissa(x)) returns l.A + x.x*vector(l.B-l.A).</documentation></function></asyxml>*/
+ coordsys R=l.A.coordsys;
+ if (x.system == nodesystem)
+ return l.A+(x.x < 0 ? 0 : x.x > 1 ? 1 : x.x)*vector(l.B-l.A);
+ if (x.system == relativesystem)
+ return l.A+x.x*vector(l.B-l.A);
+ if (x.system == curvilinearsystem)
+ return l.A+x.x*l.u;
+ if (x.system == angularsystem)
+ abort("point: what the meaning of angular abscissa on line ?.");
+ abort("point: bad abscissa system.");
+ return (0,0);
+}
+
+/*<asyxml><function type="point" signature="point(line,real)"><code></asyxml>*/
+point point(line l, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node l.A and l.B (x <= 0 means l.A, x >=1 means l.B).</documentation></function></asyxml>*/
+ return point(l,nodabscissa(x));
+}
+point point(line l, explicit int x)
+{
+ return point(l,nodabscissa(x));
+}
+
+/*<asyxml><function type="circle" signature="point(explicit circle,explicit real)"><code></asyxml>*/
+point point(explicit circle c, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x)+1.</documentation></function></asyxml>*/
+ return point(c,nodabscissa(x));
+}
+point point(explicit circle c, explicit int x)
+{
+ return point(c,nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit ellipse,explicit real)"><code></asyxml>*/
+point point(explicit ellipse el, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x)+1.</documentation></function></asyxml>*/
+ return point(el,nodabscissa(x));
+}
+point point(explicit ellipse el, explicit int x)
+{
+ return point(el,nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit parabola,explicit real)"><code></asyxml>*/
+point point(explicit parabola p, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x)+1.</documentation></function></asyxml>*/
+ return point(p,nodabscissa(x));
+}
+point point(explicit parabola p, explicit int x)
+{
+ return point(p,nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit hyperbola,explicit real)"><code></asyxml>*/
+point point(explicit hyperbola h, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x)+1.</documentation></function></asyxml>*/
+ return point(h,nodabscissa(x));
+}
+point point(explicit hyperbola h, explicit int x)
+{
+ return point(h,nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="point(explicit conic,explicit real)"><code></asyxml>*/
+point point(explicit conic co, explicit real x)
+{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x)+1.</documentation></function></asyxml>*/
+ point op;
+ if(co.e == 0) op=point((circle)co,nodabscissa(x));
+ else if(co.e < 1) op=point((ellipse)co,nodabscissa(x));
+ else if(co.e == 1) op=point((parabola)co,nodabscissa(x));
+ else op=point((hyperbola)co,nodabscissa(x));
+ return op;
+}
+point point(explicit conic co,explicit int x)
+{
+ return point(co,(real)x);
+}
+
+/*<asyxml><function type="point" signature="relpoint(line,real)"><code></asyxml>*/
+point relpoint(line l, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'l' (0 means l.A,
+ 1 means l.B, x means l.A+x*vector(l.B-l.A) ).</documentation></function></asyxml>*/
+ return point(l, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit circle,real)"><code></asyxml>*/
+point relpoint(explicit circle c, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'c' (0 means origin, 1 means end).
+ Origin is c.center+c.r*(1,0).</documentation></function></asyxml>*/
+ return point(c, Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit ellipse,real)"><code></asyxml>*/
+point relpoint(explicit ellipse el, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'el' (0 means origin, 1 means end).</documentation></function></asyxml>*/
+ return point(el,Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit parabola,real)"><code></asyxml>*/
+point relpoint(explicit parabola p, real x)
+{/*<asyxml></code><documentation>Return the relative point of the path of the parabola
+ bounded by the bounding box of the current picture.
+ 0 means origin, 1 means end, where the origin is the vertex of 'p'.</documentation></function></asyxml>*/
+ return point(p,Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit hyperbola,real)"><code></asyxml>*/
+point relpoint(explicit hyperbola h, real x)
+{/*<asyxml></code><documentation>Not yet implemented... <look href="point(hyperbola,abscissa)"/></documentation></function></asyxml>*/
+ return point(h,Relative(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(explicit conic,explicit real)"><code></asyxml>*/
+point relpoint(explicit conic co, explicit real x)
+{/*<asyxml></code><documentation>Return the relative point of 'co' (0 means origin, 1 means end).</documentation></function></asyxml>*/
+ point op;
+ if(co.e == 0) op=point((circle)co,Relative(x));
+ else if(co.e < 1) op=point((ellipse)co,Relative(x));
+ else if(co.e == 1) op=point((parabola)co,Relative(x));
+ else op=point((hyperbola)co,Relative(x));
+ return op;
+}
+point relpoint(explicit conic co, explicit int x)
+{
+ return relpoint(co,(real)x);
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit circle,real)"><code></asyxml>*/
+point angpoint(explicit circle c, real x)
+{/*<asyxml></code><documentation>Return the point of 'c' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/
+ return point(c,angabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit ellipse,real,polarconicroutine)"><code></asyxml>*/
+point angpoint(explicit ellipse el, real x,
+ polarconicroutine polarconicroutine=currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the point of 'el' in the direction 'x'
+ measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ return el.e == 0 ? angpoint((circle) el, x) : point(el,angabscissa(x,polarconicroutine));
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit parabola,real)"><code></asyxml>*/
+point angpoint(explicit parabola p, real x)
+{/*<asyxml></code><documentation>Return the point of 'p' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/
+ return point(p,angabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="angpoint(explicit hyperbola,real,polarconicroutine)"><code></asyxml>*/
+point angpoint(explicit hyperbola h, real x,
+ polarconicroutine polarconicroutine=currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the point of 'h' in the direction 'x'
+ measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ return point(h,angabscissa(x,polarconicroutine));
+}
+
+/*<asyxml><function type="point" signature="curpoint(line,real)"><code></asyxml>*/
+point curpoint(line l, real x)
+{/*<asyxml></code><documentation>Return the point of 'l' which has the curvilinear abscissa 'x'.
+ Origin is l.A.</documentation></function></asyxml>*/
+ return point(l, curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(explicit circle,real)"><code></asyxml>*/
+point curpoint(explicit circle c, real x)
+{/*<asyxml></code><documentation>Return the point of 'c' which has the curvilinear abscissa 'x'.
+ Origin is c.center+c.r*(1,0).</documentation></function></asyxml>*/
+ return point(c, curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(explicit ellipse,real)"><code></asyxml>*/
+point curpoint(explicit ellipse el, real x)
+{/*<asyxml></code><documentation>Return the point of 'el' which has the curvilinear abscissa 'el'.</documentation></function></asyxml>*/
+ return point(el,curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(explicit parabola,real)"><code></asyxml>*/
+point curpoint(explicit parabola p, real x)
+{/*<asyxml></code><documentation>Return the point of 'p' which has the curvilinear abscissa 'x'.
+ Origin is the vertex of 'p'.</documentation></function></asyxml>*/
+ return point(p,curabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(conic,real)"><code></asyxml>*/
+point curpoint(conic co, real x)
+{/*<asyxml></code><documentation>Return the point of 'co' which has the curvilinear abscissa 'x'.</documentation></function></asyxml>*/
+ point op;
+ if(co.e == 0) op=point((circle)co,curabscissa(x));
+ else if(co.e < 1) op=point((ellipse)co,curabscissa(x));
+ else if(co.e == 1) op=point((parabola)co,curabscissa(x));
+ else op=point((hyperbola)co,curabscissa(x));
+ return op;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(circle,point)"><code></asyxml>*/
+abscissa angabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("angabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system=angularsystem;
+ oa.x=degrees(M-c.C);
+ if(oa.x < 0) oa.x+=360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(ellipse,point,polarconicroutine)"><code></asyxml>*/
+abscissa angabscissa(ellipse el, point M,
+ polarconicroutine polarconicroutine=currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the ellipse 'el' according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("angabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system=angularsystem;
+ oa.polarconicroutine=polarconicroutine;
+ oa.x=polarconicroutine == fromCenter ? degrees(M-el.C) : degrees(M-el.F1);
+ oa.x -= el.angle;
+ if(oa.x < 0) oa.x += 360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(hyperbola,point,polarconicroutine)"><code></asyxml>*/
+abscissa angabscissa(hyperbola h, point M,
+ polarconicroutine polarconicroutine=currentpolarconicroutine)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the hyperbola 'h' according to 'polarconicroutine'.</documentation></function></asyxml>*/
+ if(!(M @ h)) abort("angabscissa: the point is not on the hyperbola.");
+ abscissa oa;
+ oa.system=angularsystem;
+ oa.polarconicroutine=polarconicroutine;
+ oa.x=polarconicroutine == fromCenter ? degrees(M-h.C) : degrees(M-h.F1)+180;
+ oa.x -= h.angle;
+ if(oa.x < 0) oa.x += 360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(parabola,point)"><code></asyxml>*/
+abscissa angabscissa(parabola p, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/
+ if(!(M @ p)) abort("angabscissa: the point is not on the parabola.");
+ abscissa oa;
+ oa.system=angularsystem;
+ oa.polarconicroutine=fromFocus;// Not used
+ oa.x=degrees(M-p.F);
+ oa.x -= p.angle;
+ if(oa.x < 0) oa.x += 360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(conic,point)"><code></asyxml>*/
+abscissa angabscissa(explicit conic co, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e == 0) return angabscissa((circle)co,M);
+ if(co.e < 1) return angabscissa((ellipse)co,M);
+ if(co.e == 1) return angabscissa((parabola)co,M);
+ return angabscissa((hyperbola)co,M);
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(line,point)"><code></asyxml>*/
+abscissa curabscissa(line l, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
+ if(!(M @ extend(l))) abort("curabscissa: the point is not on the line.");
+ abscissa oa;
+ oa.system=curvilinearsystem;
+ oa.x=sgn(dot(M-l.A, l.B-l.A))*abs(M-l.A);
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(circle,point)"><code></asyxml>*/
+abscissa curabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("curabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system=curvilinearsystem;
+ oa.x=pi*angabscissa(c,M).x*c.r/180;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(ellipse,point)"><code></asyxml>*/
+abscissa curabscissa(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("curabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system=curvilinearsystem;
+ real a=angabscissa(el,M,fromCenter).x;
+ oa.x=arclength(el,0,a,fromCenter);
+ oa.polarconicroutine=fromCenter;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(parabola,point)"><code></asyxml>*/
+abscissa curabscissa(parabola p, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/
+ if(!(M @ p)) abort("curabscissa: the point is not on the parabola.");
+ abscissa oa;
+ oa.system=curvilinearsystem;
+ real a=angabscissa(p,M).x;
+ oa.x=arclength(p,180,a);
+ oa.polarconicroutine=fromFocus; // Not used.
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(conic,point)"><code></asyxml>*/
+abscissa curabscissa(conic co, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e > 1) abort("curabscissa: not implemented for this hyperbola.");
+ if(co.e == 0) return curabscissa((circle)co,M);
+ if(co.e < 1) return curabscissa((ellipse)co,M);
+ return curabscissa((parabola)co,M);
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(line,point)"><code></asyxml>*/
+abscissa nodabscissa(line l, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
+ if(!(M @ (segment)l)) abort("nodabscissa: the point is not on the segment.");
+ abscissa oa;
+ oa.system=nodesystem;
+ oa.x=abs(M-l.A)/abs(l.A-l.B);
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(circle,point)"><code></asyxml>*/
+abscissa nodabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("nodabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system=nodesystem;
+ oa.x=intersect((path)c,locate(M))[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(ellipse,point)"><code></asyxml>*/
+abscissa nodabscissa(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("nodabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system=nodesystem;
+ oa.x=intersect((path)el,M)[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(parabola,point)"><code></asyxml>*/
+abscissa nodabscissa(parabola p, point M)
+{/*<asyxml></code><documentation>Return the node abscissa OF 'M' on the parabola 'p'.</documentation></function></asyxml>*/
+ if(!(M @ p)) abort("nodabscissa: the point is not on the parabola.");
+ abscissa oa;
+ oa.system=nodesystem;
+ path pg=p;
+ real[] t=intersect(pg,M,1e-5);
+ if(t.length == 0) abort("nodabscissa: the point is not on the path of the parabola.");
+ oa.x=t[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(conic,point)"><code></asyxml>*/
+abscissa nodabscissa(conic co, point M)
+{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
+ if(co.e > 1) abort("nodabscissa: not implemented for hyperbola.");
+ if(co.e == 0) return nodabscissa((circle)co,M);
+ if(co.e < 1) return nodabscissa((ellipse)co,M);
+ return nodabscissa((parabola)co,M);
+}
+
+
+/*<asyxml><function type="abscissa" signature="relabscissa(line,point)"><code></asyxml>*/
+abscissa relabscissa(line l, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
+ if(!(M @ extend(l))) abort("relabscissa: the point is not on the line.");
+ abscissa oa;
+ oa.system=relativesystem;
+ oa.x=sgn(dot(M-l.A, l.B-l.A))*abs(M-l.A)/abs(l.A-l.B);
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(circle,point)"><code></asyxml>*/
+abscissa relabscissa(circle c, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
+ if(!(M @ c)) abort("relabscissa: the point is not on the circle.");
+ abscissa oa;
+ oa.system=relativesystem;
+ oa.x=angabscissa(c,M).x/360;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(ellipse,point)"><code></asyxml>*/
+abscissa relabscissa(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
+ if(!(M @ el)) abort("relabscissa: the point is not on the ellipse.");
+ abscissa oa;
+ oa.system=relativesystem;
+ oa.x=curabscissa(el,M).x/arclength(el);
+ oa.polarconicroutine=fromFocus;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(conic,point)"><code></asyxml>*/
+abscissa relabscissa(conic co, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa of 'M'
+ on the conic 'co'.</documentation></function></asyxml>*/
+ write("PASS");
+ if(co.e > 1) abort("relabscissa: not implemented for hyperbola and parabola.");
+ if(co.e == 1) return relabscissa((parabola)co,M);
+ if(co.e == 0) return relabscissa((circle)co,M);
+ return relabscissa((ellipse)co,M);
+}
+// *.......................ABSCISSA........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.........................ARCS..........................*
+/*<asyxml><struct signature="arc"><code></asyxml>*/
+struct arc {
+ /*<asyxml></code><documentation>Implement oriented ellipse (included circle) arcs.
+ All the calculus with this structure will be as exact as Asymptote can do.
+ For a full precision, you must not cast 'arc' to 'path' excepted for drawing routines.
+ </documentation><property type="ellipse" signature="el"><code></asyxml>*/
+ ellipse el;/*<asyxml></code><documentation>The support of the arc.</documentation></property><property type="real" signature="angle0"><code></asyxml>*/
+ restricted real angle0=0;/*<asyxml></code><documentation>Internal use: rotating a circle does not modify the origin point, this variable stocks the eventual angle rotation. This value is not used for ellipses which are not circles.</documentation></property><property type="real" signature="angle1,angle2"><code></asyxml>*/
+ restricted real angle1, angle2;/*<asyxml></code><documentation>Values (in degrees) in ]-360,360[.</documentation></property><property type="bool" signature="direction"><code></asyxml>*/
+ bool direction=CCW;/*<asyxml></code><documentation>The arc will be drawn from 'angle1' to 'angle2' rotating in the direction 'direction'.</documentation></property><property type="polarconicroutine" signature="polarconicroutine"><code></asyxml>*/
+ polarconicroutine polarconicroutine=currentpolarconicroutine;/*<asyxml></code><documentation>The routine to which the angles refer.
+ If 'el' is a circle 'fromCenter' is always used.</documentation></property></asyxml>*/
+
+ /*<asyxml><method type="void" signature="setangles(real,real,real)"><code></asyxml>*/
+ void setangles(real a0, real a1, real a2)
+ {/*<asyxml></code><documentation>Set the angles.</documentation></method></asyxml>*/
+ this.angle0=a0%(sgnd(a0)*360);
+ this.angle1=a1%(sgnd(a1)*360);
+ this.angle2=a2%(sgnd(2)*360);
+ }
+
+ /*<asyxml><method type="void" signature="init(ellipse,real,real,real,polarconicroutine,bool)"><code></asyxml>*/
+ void init(ellipse el, real angle0=0, real angle1, real angle2,
+ polarconicroutine polarconicroutine,
+ bool direction=CCW)
+ {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/
+ if(abs(angle1-angle2) > 360) abort("arc: |angle1-angle2| > 360.");
+ this.el=el;
+ this.setangles(angle0, angle1, angle2);
+ this.polarconicroutine=polarconicroutine;
+ this.direction=direction;
+ }
+
+ /*<asyxml><method type="arc" signature="copy()"><code></asyxml>*/
+ arc copy()
+ {/*<asyxml></code><documentation>Copy the arc.</documentation></method></asyxml>*/
+ arc oa=new arc;
+ oa.el=this.el;
+ oa.direction=this.direction;
+ oa.polarconicroutine=this.polarconicroutine;
+ oa.angle1=this.angle1;
+ oa.angle2=this.angle2;
+ oa.angle0=this.angle0;
+ return oa;
+ }
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="polarconicroutine" signature="polarconicroutine(ellipse)"><code></asyxml>*/
+polarconicroutine polarconicroutine(conic co)
+{/*<asyxml></code><documentation>Return the default routine used to draw a conic.</documentation></function></asyxml>*/
+ if(co.e == 0) return fromCenter;
+ if(co.e == 1) return fromFocus;
+ return currentpolarconicroutine;
+}
+
+/*<asyxml><function type="arc" signature="arc(ellipse,real,real,polarconicroutine,bool)"><code></asyxml>*/
+arc arc(ellipse el, real angle1, real angle2,
+ polarconicroutine polarconicroutine=polarconicroutine(el),
+ bool direction=CCW)
+{/*<asyxml></code><documentation>Return the ellipse arc from 'angle1' to 'angle2' with respect to 'polarconicroutine' and rotating in the direction 'direction'.</documentation></function></asyxml>*/
+ arc oa;
+ oa.init(el,0,angle1,angle2,polarconicroutine,direction);
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="complementary(arc)"><code></asyxml>*/
+arc complementary(arc a)
+{/*<asyxml></code><documentation>Return the complementary of 'a'.</documentation></function></asyxml>*/
+ arc oa;
+ oa.init(a.el,a.angle0,a.angle2,a.angle1,a.polarconicroutine,a.direction);
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="reverse(arc)"><code></asyxml>*/
+arc reverse(arc a)
+{/*<asyxml></code><documentation>Return arc 'a' oriented in reverse direction.</documentation></function></asyxml>*/
+ arc oa;
+ oa.init(a.el,a.angle0,a.angle2,a.angle1,a.polarconicroutine,!a.direction);
+ return oa;
+}
+
+/*<asyxml><function type="real" signature="degrees(arc)"><code></asyxml>*/
+real degrees(arc a)
+{/*<asyxml></code><documentation>Return the measure in degrees of the oriented arc 'a'.</documentation></function></asyxml>*/
+ real or;
+ real da=a.angle2-a.angle1;
+ if(a.direction) {
+ or=a.angle1 < a.angle2 ? da : 360+da;
+ } else {
+ or=a.angle1 < a.angle2 ? -360+da : da;
+ }
+ return or;
+}
+
+/*<asyxml><function type="real" signature="angle(a)"><code></asyxml>*/
+real angle(arc a)
+{/*<asyxml></code><documentation>Return the measure in radians of the oriented arc 'a'.</documentation></function></asyxml>*/
+ return radians(degrees(a));
+}
+
+/*<asyxml><function type="int" signature="arcnodesnumber(explicit arc)"><code></asyxml>*/
+int arcnodesnumber(explicit arc a)
+{/*<asyxml></code><documentation>Return the number of nodes to draw the arc 'a'.</documentation></function></asyxml>*/
+ return ellipsenodesnumber(a.el.a,a.el.b,a.angle1,a.angle2,a.direction);
+}
+
+private path arctopath(arc a, int n)
+{
+ if(a.el.e == 0) return arcfromcenter(a.el,a.angle0+a.angle1,a.angle0+a.angle2,a.direction,n);
+ if(a.el.e != 1) return a.polarconicroutine(a.el,a.angle1,a.angle2,n,a.direction);
+ return arcfromfocus(a.el,a.angle1,a.angle2,n,a.direction);
+}
+
+/*<asyxml><function type="point" signature="angpoint(arc,real)"><code></asyxml>*/
+point angpoint(arc a, real angle)
+{/*<asyxml></code><documentation>Return the point given by its angular position (in degrees) relative to the arc 'a'.
+ If 'angle > degrees(a)' or 'angle < 0' the returned point is on the extended arc.</documentation></function></asyxml>*/
+ pair p;
+ if(a.el.e == 0) {
+ real gle=a.angle0+a.angle1+(a.direction ? angle : -angle);
+ p=point(arcfromcenter(a.el,gle,gle,CCW,1),0);
+ }
+ else {
+ real gle=a.angle1+(a.direction ? angle : -angle);
+ p=point(a.polarconicroutine(a.el,gle,gle,1,CCW),0);
+ }
+ return point(coordsys(a.el),p/coordsys(a.el));
+}
+
+/*<asyxml><operator type="path" signature="cast(explicit arc)"><code></asyxml>*/
+path operator cast(explicit arc a)
+{/*<asyxml></code><documentation>Cast arc to path.</documentation></operator></asyxml>*/
+ return arctopath(a,arcnodesnumber(a));
+}
+
+/*<asyxml><operator type="guide" signature="cast(explicit arc)"><code></asyxml>*/
+guide operator cast(explicit arc a)
+{/*<asyxml></code><documentation>Cast arc to guide.</documentation></operator></asyxml>*/
+ return arctopath(a,arcnodesnumber(a));
+}
+
+/*<asyxml><operator type="arc" signature="*(transform,explicit arc)"><code></asyxml>*/
+arc operator *(transform t, explicit arc a)
+{/*<asyxml></code><documentation>Provide transform*arc.</documentation></operator></asyxml>*/
+ pair[] P, PP;
+ path g=arctopath(a,3);
+ real a0, a1=a.angle1, a2=a.angle2, ap1, ap2;
+ bool dir=a.direction;
+ P[0]=t*point(g,0);
+ P[1]=t*point(g,2);
+ ellipse el=t*a.el;
+ arc oa;
+ a0=(a.angle0+angle(shiftless(t)))%360;
+ pair C;
+ if(a.polarconicroutine == fromCenter) C=el.C; else C=el.F1;
+ real d=abs(locate(el.F2-el.F1)) > epsgeo ?
+ degrees(locate(el.F2-el.F1)) : a0+degrees(el.C.coordsys.i);
+ ap1=(degrees(P[0]-C,false)-d)%360;
+ ap2=(degrees(P[1]-C,false)-d)%360;
+ oa.init(el,a0,ap1,ap2,a.polarconicroutine,dir);
+ g=arctopath(oa,3);
+ PP[0]=point(g,0);
+ PP[1]=point(g,2);
+ if((a1-a2)*(ap1-ap2) < 0) {// Handle reflection.
+ dir=!a.direction;
+ oa.init(el,a0,ap1,ap2,a.polarconicroutine,dir);
+ }
+ return oa;
+}
+
+/*<asyxml><operator type="arc" signature="*(real,explicit arc)"><code></asyxml>*/
+arc operator *(real x, explicit arc a)
+{/*<asyxml></code><documentation>Provide real*arc.
+ Return the arc subtracting and adding '(x-1)*degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/
+ real a1, a2, gle;
+ gle=(x-1)*degrees(a)/2;
+ a1=a.angle1-gle;
+ a2=a.angle2+gle;
+ arc oa;
+ oa.init(a.el,a.angle0,a1,a2,a.polarconicroutine,a.direction);
+ return oa;
+}
+arc operator *(int x, explicit arc a){return (real)x*a;}
+/*<asyxml><operator type="arc" signature="/(real,explicit arc)"><code></asyxml>*/
+arc operator /(explicit arc a, real x)
+{/*<asyxml></code><documentation>Provide arc/real.
+ Return the arc subtracting and adding '(1/x-1)*degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/
+ return (1/x)*a;
+}
+/*<asyxml><operator type="arc" signature="+(explicit arc,point)"><code></asyxml>*/
+arc operator +(explicit arc a, point M)
+{/*<asyxml></code><documentation>Provide arc+point.
+ Return shifted arc.
+ 'operator +(explicit arc,point)', 'operator +(explicit arc,vector)' and 'operator -(explicit arc,vector)' are also defined.</documentation></operator></asyxml>*/
+ return shift(M)*a;
+}
+arc operator -(explicit arc a, point M){return a+(-M);}
+arc operator +(explicit arc a, vector v){return shift(locate(v))*a;}
+arc operator -(explicit arc a, vector v){return a+(-v);}
+
+
+/*<asyxml><operator type="bool" signature="@(point,arc)"><code></asyxml>*/
+bool operator @(point M, arc a)
+{/*<asyxml></code><documentation>Return true iff 'M' is on the arc 'a'.</documentation></operator></asyxml>*/
+ if (!(M @ a.el)) return false;
+ coordsys R=defaultcoordsys;
+ path ap=arctopath(a,3);
+ line l=line(point(R,point(ap,0)),point(R,point(ap,2)));
+ return sameside(M, point(R,point(ap,1)), l);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,Label,arc,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, Label L="", arc a,
+ align align=NoAlign, pen p=currentpen,
+ arrowbar arrow=None, arrowbar bar=None, margin margin=NoMargin,
+ Label legend="", marker marker=nomarker)
+{/*<asyxml></code><documentation>Draw 'arc' adding the pen returned by 'addpenarc(p)' to the pen 'p'.
+ <look href="#addpenarc"/></documentation></function></asyxml>*/
+ draw(pic,L,(path)a,align,addpenarc(p),arrow,bar,margin,legend,marker);
+}
+
+/*<asyxml><function type="real" signature="arclength(arc)"><code></asyxml>*/
+real arclength(arc a)
+{/*<asyxml></code><documentation>The arc length of 'a'.</documentation></function></asyxml>*/
+ return arclength(a.el, a.angle1, a.angle2, a.direction, a.polarconicroutine);
+}
+
+private point ppoint(arc a, real x)
+{// Return the point of the arc proportionally to its length.
+ point oP;
+ if(a.el.e == 0) { // Case of circle.
+ oP=angpoint(a,x*abs(degrees(a)));
+ } else { // Ellipse and not circle.
+ if(!a.direction) {
+ transform t=reflect(line(a.el.F1,a.el.F2));
+ return t*ppoint(t*a,x);
+ }
+
+ real angle1=a.angle1, angle2=a.angle2;
+ if(a.polarconicroutine == fromFocus) {
+ // dot(point(fromFocus(a.el,angle1,angle1,1,CCW),0),2mm+blue);
+ // dot(point(fromFocus(a.el,angle2,angle2,1,CCW),0),2mm+blue);
+ // write("fromfocus1=",angle1);
+ // write("fromfocus2=",angle2);
+ real gle1=focusToCenter(a.el,angle1);
+ real gle2=focusToCenter(a.el,angle2);
+ if((gle1-gle2)*(angle1-angle2) > 0) {
+ angle1=gle1; angle2=gle2;
+ } else {
+ angle1=gle2; angle2=gle1;
+ }
+ // write("fromcenter1=",angle1);
+ // write("fromcenter2=",angle2);
+ // dot(point(fromCenter(a.el,angle1,angle1,1,CCW),0),1mm+red);
+ // dot(point(fromCenter(a.el,angle2,angle2,1,CCW),0),1mm+red);
+ }
+
+ if(angle1 > angle2) {
+ arc ta=a.copy();
+ ta.polarconicroutine=fromCenter;
+ ta.setangles(a0=a.angle0,a1=angle1-360,a2=angle2);
+ return ppoint(ta,x);
+ }
+ ellipse co=a.el;
+ real gle, a1, a2, cx=0;
+ bool direction;
+ if(x >= 0) {
+ a1=angle1;
+ a2=a1+360;
+ direction=CCW;
+ } else {
+ a1=angle1-360;
+ a2=a1-360;
+ direction=CW;
+ }
+ gle=a1;
+ real L=arclength(co,angle1,angle2,a.direction,fromCenter);
+ real tx=L*abs(x)%arclength(co);
+ real aout=a1;
+ while(abs(cx-tx) > epsgeo) {
+ aout=(a1+a2)/2;
+ cx=abs(arclength(co,gle,aout,direction,fromCenter));
+ if(cx > tx) a2=(a1+a2)/2 ; else a1=(a1+a2)/2;
+ }
+ pair p=point(arcfromcenter(co,aout,aout,CCW,1), 0);
+ oP=point(coordsys(co), p/coordsys(co));
+ }
+ return oP;
+}
+
+/*<asyxml><function type="point" signature="point(arc,abscissa)"><code></asyxml>*/
+point point(arc a, abscissa l)
+{/*<asyxml></code><documentation>Return the point of 'a' which has the abscissa 'l.x'
+ according to the abscissa system 'l.system'.
+ Note that 'a.polarconicroutine' is used instead of 'l.polarconicroutine'.
+ <look href="#struct abscissa"/></documentation></function></asyxml>*/
+ real posx;
+ arc ta=a.copy();
+ ellipse co=a.el;
+ if (l.system == relativesystem) {
+ posx=l.x;
+ } else
+ if (l.system == curvilinearsystem) {
+ real tl;
+ if(co.e == 0) {
+ tl=curabscissa(a.el,angpoint(a.el,a.angle0+a.angle1)).x;
+ return curpoint(a.el,tl + (a.direction ? l.x : -l.x));
+ } else {
+ tl=curabscissa(a.el,angpoint(a.el,a.angle1,a.polarconicroutine)).x;
+ return curpoint(a.el,tl + (a.direction ? l.x : -l.x));
+ }
+ } else
+ if (l.system == nodesystem) {
+ coordsys R=coordsys(co);
+ return point(R,point((path)a,l.x)/R);
+ } else
+ if (l.system == angularsystem) {
+ return angpoint(a,l.x);
+ } else abort("point: bad abscissa system.");
+ return ppoint(ta, posx);
+}
+
+
+/*<asyxml><function type="point" signature="point(arc,real)"><code></asyxml>*/
+point point(arc a,real x)
+{/*<asyxml></code><documentation>Return the point between node floor(t) and floor(t)+1.</documentation></function></asyxml>*/
+ return point(a, nodabscissa(x));
+}
+pair point(explicit arc a, int x)
+{
+ return point(a, nodabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="relpoint(arc,real)"><code></asyxml>*/
+point relpoint(arc a, real x)
+{/*<asyxml></code><documentation>Return the relative point of 'a'.
+ If x > 1 or x < 0, the returned point is on the extended arc.</documentation></function></asyxml>*/
+ return point(a, relabscissa(x));
+}
+
+/*<asyxml><function type="point" signature="curpoint(arc,real)"><code></asyxml>*/
+point curpoint(arc a, real x)
+{/*<asyxml></code><documentation>Return the point of 'a' which has the curvilinear abscissa 'x'.
+ If x < 0 or x > arclength(a), the returned point is on the extended arc.</documentation></function></asyxml>*/
+ return point(a, curabscissa(x));
+}
+
+/*<asyxml><function type="abscissa" signature="angabscissa(arc,point)"><code></asyxml>*/
+abscissa angabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the angular abscissa of 'M' according to the arc 'a'.</documentation></function></asyxml>*/
+ if(!(M @ a.el))
+ abort("angabscissa: the point is not on the extended arc.");
+ abscissa oa;
+ oa.system=angularsystem;
+ oa.polarconicroutine=a.polarconicroutine;
+ real am=angabscissa(a.el,M,a.polarconicroutine).x;
+ oa.x=(am-a.angle1-(a.el.e == 0 ? a.angle0 : 0))%360;
+ oa.x=a.direction ? oa.x : 360-oa.x;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="curabscissa(arc,point)"><code></asyxml>*/
+abscissa curabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the curvilinear abscissa according to the arc 'a'.</documentation></function></asyxml>*/
+ ellipse el=a.el;
+ if(!(M @ el))
+ abort("angabscissa: the point is not on the extended arc.");
+ abscissa oa;
+ oa.system=curvilinearsystem;
+ real xm=curabscissa(el,M).x;
+ real a0=el.e == 0 ? a.angle0 : 0;
+ real am=curabscissa(el,angpoint(el,a.angle1+a0,a.polarconicroutine)).x;
+ real l=arclength(el);
+ oa.x=(xm-am)%l;
+ oa.x=a.direction ? oa.x : l-oa.x;
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="nodabscissa(arc,point)"><code></asyxml>*/
+abscissa nodabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the node abscissa according to the arc 'a'.</documentation></function></asyxml>*/
+ if(!(M @ a))
+ abort("nodabscissa: the point is not on the arc.");
+ abscissa oa;
+ oa.system=nodesystem;
+ oa.x=intersect((path)a,M)[0];
+ return oa;
+}
+
+/*<asyxml><function type="abscissa" signature="relabscissa(arc,point)"><code></asyxml>*/
+abscissa relabscissa(arc a, point M)
+{/*<asyxml></code><documentation>Return the relative abscissa according to the arc 'a'.</documentation></function></asyxml>*/
+ ellipse el=a.el;
+ if(!( M @ el))
+ abort("relabscissa: the point is not on the prolonged arc.");
+ abscissa oa;
+ oa.system=relativesystem;
+ oa.x=curabscissa(a,M).x/arclength(a);
+ return oa;
+}
+
+/*<asyxml><function type="void" signature="markarc(picture,Label,int,real,real,arc,arrowbar,pen,pen,margin,marker)"><code></asyxml>*/
+void markarc(picture pic=currentpicture,
+ Label L="",
+ int n=1, real radius=0, real space=0,
+ arc a,
+ pen sectorpen=currentpen,
+ pen markpen=sectorpen,
+ margin margin=NoMargin,
+ arrowbar arrow=None,
+ marker marker=nomarker)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ real Da=degrees(a);
+ pair p1=point(a,0);
+ pair p2=relpoint(a,1);
+ pair c=a.polarconicroutine == fromCenter ? locate(a.el.C) : locate(a.el.F1);
+ if(radius == 0) radius=markangleradius(markpen);
+ if(abs(Da) > 180) radius=-radius;
+ radius=(a.direction ? 1 : -1)*sgnd(Da)*radius;
+ draw(c--p1^^c--p2,sectorpen);
+ markangle(pic=pic,L=L,n=n,radius=radius,space=space,
+ A=p1,O=c,B=p2,
+ arrow=arrow,p=markpen,margin=margin,
+ marker=marker);
+}
+// *.........................ARCS..........................*
+// *=======================================================*
+
+// *=======================================================*
+// *........................MASSES.........................*
+/*<asyxml><struct signature="mass"><code></asyxml>*/
+struct mass {/*<asyxml></code><documentation></documentation><property type="point" signature="M"><code></asyxml>*/
+ point M;/*<asyxml></code><documentation></documentation></property><property type="real" signature="m"><code></asyxml>*/
+ real m;/*<asyxml></code><documentation></documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="mass" signature="mass(point,real)"><code></asyxml>*/
+mass mass(point M, real m)
+{/*<asyxml></code><documentation>Constructor of mass point.</documentation></function></asyxml>*/
+ mass om;
+ om.M=M;
+ om.m=m;
+ return om;
+}
+
+/*<asyxml><operator type="point" signature="cast(mass)"><code></asyxml>*/
+point operator cast(mass m)
+{/*<asyxml></code><documentation>Cast mass point to point.</documentation></operator></asyxml>*/
+ point op;
+ op=m.M;
+ op.m=m.m;
+ return op;
+}
+/*<asyxml><function type="point" signature="point(explicit mass)"><code></asyxml>*/
+point point(explicit mass m){return m;}/*<asyxml></code><documentation>Cast
+ 'm' to point</documentation></function></asyxml>*/
+
+/*<asyxml><operator type="mass" signature="cast(point)"><code></asyxml>*/
+mass operator cast(point M)
+{/*<asyxml></code><documentation>Cast point to mass point.</documentation></operator></asyxml>*/
+ mass om;
+ om.M=M;
+ om.m=M.m;
+ return om;
+}
+/*<asyxml><function type="mass" signature="mass(explicit point)"><code></asyxml>*/
+mass mass(explicit point P)
+{/*<asyxml></code><documentation>Cast 'P' to mass.</documentation></function></asyxml>*/
+ return mass(P,P.m);
+}
+
+/*<asyxml><operator type="point[]" signature="cast(mass[])"><code></asyxml>*/
+point[] operator cast(mass[] m)
+{/*<asyxml></code><documentation>Cast mass[] to point[].</documentation></operator></asyxml>*/
+ point[] op;
+ for(mass am : m) op.push(point(am));
+ return op;
+}
+
+/*<asyxml><operator type="mass[]" signature="cast(point[])"><code></asyxml>*/
+mass[] operator cast(point[] P)
+{/*<asyxml></code><documentation>Cast point[] to mass[].</documentation></operator></asyxml>*/
+ mass[] om;
+ for(point op : P) om.push(mass(op));
+ return om;
+}
+
+/*<asyxml><function type="mass" signature="mass(coordsys,explicit pair,real)"><code></asyxml>*/
+mass mass(coordsys R, explicit pair p, real m)
+{/*<asyxml></code><documentation>Return the mass which has coordinates
+ 'p' with respect to 'R' and weight 'm'.</documentation></function></asyxml>*/
+ return point(R,p,m);// Using casting.
+}
+
+/*<asyxml><operator type="mass" signature="cast(pair)"><code></asyxml>*/
+mass operator cast(pair m){return mass((point)m,1);}/*<asyxml></code><documentation>Cast pair to mass point.</documentation></operator></asyxml>*/
+/*<asyxml><operator type="path" signature="cast(mass)"><code></asyxml>*/
+path operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass point to path.</documentation></operator></asyxml>*/
+/*<asyxml><operator type="guide" signature="cast(mass)"><code></asyxml>*/
+guide operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass to guide.</documentation></operator></asyxml>*/
+
+/*<asyxml><operator type="mass" signature="+(mass,mass)"><code></asyxml>*/
+mass operator +(mass M1, mass M2)
+{/*<asyxml></code><documentation>Provide mass+mass.
+ mass-mass is also defined.</documentation></operator></asyxml>*/
+ return mass(M1.M+M2.M,M1.m+M2.m);
+}
+mass operator -(mass M1, mass M2)
+{
+ return mass(M1.M-M2.M,M1.m-M2.m);
+}
+
+/*<asyxml><operator type="mass" signature="*(real,mass)"><code></asyxml>*/
+mass operator *(real x, explicit mass M)
+{/*<asyxml></code><documentation>Provide real*mass.
+ The resulted mass is the mass of 'M' multiplied by 'x' .
+ mass/real, mass+real and mass-real are also defined.</documentation></operator></asyxml>*/
+ return mass(M.M,x*M.m);
+}
+mass operator *(int x, explicit mass M){return mass(M.M,x*M.m);}
+mass operator /(explicit mass M,real x){return mass(M.M,M.m/x);}
+mass operator /(explicit mass M,int x){return mass(M.M,M.m/x);}
+mass operator +(explicit mass M,real x){return mass(M.M,M.m+x);}
+mass operator +(explicit mass M,int x){return mass(M.M,M.m+x);}
+mass operator -(explicit mass M,real x){return mass(M.M,M.m-x);}
+mass operator -(explicit mass M,int x){return mass(M.M,M.m-x);}
+/*<asyxml><operator type="mass" signature="*(transform,mass)"><code></asyxml>*/
+mass operator *(transform t, mass M)
+{/*<asyxml></code><documentation>Provide transform*mass.</documentation></operator></asyxml>*/
+ return mass(t*M.M,M.m);
+}
+
+/*<asyxml><function type="mass" signature="masscenter(... mass[])"><code></asyxml>*/
+mass masscenter(... mass[] M)
+{/*<asyxml></code><documentation>Return the center of the masses 'M'.</documentation></function></asyxml>*/
+ point[] P;
+ for (int i=0; i < M.length; ++i)
+ P.push(M[i].M);
+ P=standardizecoordsys(currentcoordsys,true ... P);
+ real m=M[0].m;
+ point oM=M[0].m*P[0];
+ for (int i=1; i < M.length; ++i) {
+ oM+=M[i].m*P[i];
+ m+=M[i].m;
+ }
+ if (m == 0) abort("masscenter: the sum of masses is null.");
+ return mass(oM/m,m);
+}
+
+/*<asyxml><function type="string" signature="massformat(string,string,mass)"><code></asyxml>*/
+string massformat(string format=defaultmassformat,
+ string s, mass M)
+{/*<asyxml></code><documentation>Return the string formated by 'format' with the mass value.
+ In the parameter 'format', %L will be replaced by 's'.
+ <look href="#defaultmassformat"/>.</documentation></function></asyxml>*/
+ return format == "" ? s :
+ format(replace(format,"%L",replace(s,"$","")),M.m);
+}
+
+/*<asyxml><function type="void" signature="label(picture, Label,explicit mass,align,string,pen,filltype)"><code></asyxml>*/
+void label(picture pic=currentpicture, Label L, explicit mass M,
+ align align=NoAlign, string format=defaultmassformat,
+ pen p=nullpen, filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw label returned by massformat(format,L,M) at coordinates of M.
+ <look href="#massformat(string,string,mass)"/>.</documentation></function></asyxml>*/
+ Label lL=L.copy();
+ lL.s=massformat(format,lL.s,M);
+ Label L=Label(lL,M.M,align,p,filltype);
+ add(pic,L);
+}
+
+/*<asyxml><function type="void" signature="dot(picture,Label,explicit mass,align,string,pen)"><code></asyxml>*/
+void dot(picture pic=currentpicture, Label L, explicit mass M, align align=NoAlign,
+ string format=defaultmassformat, pen p=currentpen)
+{/*<asyxml></code><documentation>Draw a dot with label 'L' as
+ label(picture, Label,explicit mass,align,string,pen,filltype) does.
+ <look href="#label(picture, Label,mass,align,string,pen,filltype)"/>.</documentation></function></asyxml>*/
+ Label lL=L.copy();
+ lL.s=massformat(format,lL.s,M);
+ lL.position(locate(M.M));
+ lL.align(align,E);
+ lL.p(p);
+ dot(pic,M.M,p);
+ add(pic,lL);
+}
+// *........................MASSES.........................*
+// *=======================================================*
+
+// *=======================================================*
+// *.......................TRIANGLES.......................*
+/*<asyxml><function type="point" signature="orthocentercenter(point,point,point)"><code></asyxml>*/
+point orthocentercenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the orthocenter of the triangle ABC.</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B,C);
+ coordsys R=P[0].coordsys;
+ pair pp=extension(A, projection(P[1],P[2])*P[0], B, projection(P[0],P[2])*P[1]);
+ return point(R,pp/R);
+}
+
+/*<asyxml><function type="point" signature="centroid(point,point,point)"><code></asyxml>*/
+point centroid(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the centroid of the triangle ABC.</documentation></function></asyxml>*/
+ return (A+B+C)/3;
+}
+
+/*<asyxml><function type="point" signature="incenter(point,point,point)"><code></asyxml>*/
+point incenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the center of the incircle of the triangle ABC.</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B,C);
+ coordsys R=P[0].coordsys;
+ pair a=A, b=B, c=C;
+ pair pp=extension(a, a+dir(a--b,a--c), b, b+dir(b--a,b--c));
+ return point(R,pp/R);
+}
+
+/*<asyxml><function type="real" signature="inradius(point,point,point)"><code></asyxml>*/
+real inradius(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle ABC.</documentation></function></asyxml>*/
+ point IC=incenter(A,B,C);
+ return abs(IC-projection(A,B)*IC);
+}
+
+/*<asyxml><function type="circle" signature="incircle(point,point,point)"><code></asyxml>*/
+circle incircle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the incircle of the triangle ABC.</documentation></function></asyxml>*/
+ point IC=incenter(A, B, C);
+ return circle(IC,abs(IC-projection(A,B)*IC));
+}
+
+/*<asyxml><function type="point" signature="excenter(point,point,point)"><code></asyxml>*/
+point excenter(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the center of the excircle of the triangle tangent with (AB).</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B,C);
+ coordsys R=P[0].coordsys;
+ pair a=A, b=B, c=C;
+ pair pp=extension(a, a+rotate(90)*dir(a--b,a--c), b, b+rotate(90)*dir(b--a,b--c));
+ return point(R,pp/R);
+}
+
+/*<asyxml><function type="real" signature="exradius(point,point,point)"><code></asyxml>*/
+real exradius(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the radius of the excircle of the triangle ABC with (AB).</documentation></function></asyxml>*/
+ point EC=excenter(A,B,C);
+ return abs(EC-projection(A,B)*EC);
+}
+
+/*<asyxml><function type="circle" signature="excircle(point,point,point)"><code></asyxml>*/
+circle excircle(point A, point B, point C)
+{/*<asyxml></code><documentation>Return the excircle of the triangle ABC tangent with (AB).</documentation></function></asyxml>*/
+ point center=excenter(A,B,C);
+ real radius=abs(center-projection(B,C)*center);
+ return circle(center,radius);
+}
+
+private int[] numarray={1,2,3};
+numarray.cyclic(true);
+
+/*<asyxml><struct signature="triangle"><code></asyxml>*/
+struct triangle {/*<asyxml></code><documentation></documentation></asyxml>*/
+
+ /*<asyxml><struct signature="vertex"><code></asyxml>*/
+ struct vertex {/*<asyxml></code><documentation>Structure used to communicate the vertex of a triangle.</documentation><property type="int" signature="n"><code></asyxml>*/
+ int n;/*<asyxml></code><documentation>1 means VA, 2 means VB, 3 means VC, 4 means VA etc...</documentation></property><property type="triangle" signature="triangle"><code></asyxml>*/
+ triangle t;/*<asyxml></code><documentation>The triangle to which the vertex refers.</documentation></property></asyxml>*/
+ }/*<asyxml></struct></asyxml>*/
+
+ /*<asyxml><property type="point" signature="A, B, C"><code></asyxml>*/
+ restricted point A, B, C;/*<asyxml></code><documentation>The vertices of the triangle (as point).</documentation></property><property type="vertex" signature="VA, VB, VC"><code></asyxml>*/
+ restricted vertex VA, VB, VC;/*<asyxml></code><documentation>The vertices of the triangle (as vertex).
+ Note that the vertex structure contains the triangle to wish it refers.</documentation></property></asyxml>*/
+ VA.n=1;VB.n=2;VC.n=3;
+
+ /*<asyxml><method type="vertex" signature="vertex(int)"><code></asyxml>*/
+ vertex vertex(int n)
+ {/*<asyxml></code><documentation>Return numbered vertex.
+ 'n' is 1 means VA, 2 means VB, 3 means VC, 4 means VA etc...</documentation></method></asyxml>*/
+ n = numarray[n-1];
+ if(n == 1) return VA;
+ else if(n == 2) return VB;
+ return VC;
+ }
+
+ /*<asyxml><method type="point" signature="point(int)"><code></asyxml>*/
+ point point(int n)
+ {/*<asyxml></code><documentation>Return numbered point.
+ n is 1 means A, 2 means B, 3 means C, 4 means A etc...</documentation></method></asyxml>*/
+ n = numarray[n-1];
+ if(n == 1) return A;
+ else if(n == 2) return B;
+ return C;
+ }
+
+ /*<asyxml><method type="void" signature="init(point,point,point)"><code></asyxml>*/
+ void init(point A, point B, point C)
+ {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/
+ point[] P=standardizecoordsys(A,B,C);
+ this.A=P[0];
+ this.B=P[1];
+ this.C=P[2];
+ VA.t=this; VB.t=this; VC.t=this;
+ }
+
+ /*<asyxml><method type="void" signature="operator init(point,point,point)"><code></asyxml>*/
+ void operator init(point A, point B, point C)
+ {/*<asyxml></code><documentation>For backward compatibility.
+ Provide the routine 'triangle(point A, point B, point C)'.</documentation></method></asyxml>*/
+ this.init(A,B,C);
+ }
+
+ /*<asyxml><method type="void" signature="init(real,real,real,real,point)"><code></asyxml>*/
+ void operator init(real b, real alpha, real c, real angle=0, point A=(0,0))
+ {/*<asyxml></code><documentation>For backward compatibility.
+ Provide the routine 'triangle(real b, real alpha, real c, real angle=0, point A=(0,0))
+ which returns the triangle ABC rotated by 'angle' (in degrees) and where b=AC, degrees(A)=alpha, AB=c.</documentation></method></asyxml>*/
+ coordsys R=A.coordsys;
+ this.init(A,A+R.polar(c,radians(angle)),A+R.polar(b,radians(angle+alpha)));
+ }
+
+ /*<asyxml><method type="real" signature="a(),b(),c()"><code></asyxml>*/
+ real a()
+ {/*<asyxml></code><documentation>Return the length BC.
+ b() and c() are also defined and return the length AC and AB respectively.</documentation></method></asyxml>*/
+ return length(C-B);
+ }
+ real b() {return length(A-C);}
+ real c() {return length(B-A);}
+
+ private real det(pair a, pair b) {return a.x*b.y-a.y*b.x;}
+
+ /*<asyxml><method type="real" signature="area()"><code></asyxml>*/
+ real area()
+ {/*<asyxml></code><documentation></documentation></method></asyxml>*/
+ pair a=locate(A), b=locate(B), c=locate(C);
+ return 0.5*abs(det(a,b)+det(b,c)+det(c,a));
+ }
+
+ /*<asyxml><method type="real" signature="alpha(),beta(),gamma()"><code></asyxml>*/
+ real alpha()
+ {/*<asyxml></code><documentation>Return the measure (in degrees) of the angle A.
+ beta() and gamma() are also defined and return the measure of the angles B and C respectively.</documentation></method></asyxml>*/
+ return degrees(acos((b()^2+c()^2-a()^2)/(2b()*c())));
+ }
+ real beta() {return degrees(acos((c()^2+a()^2-b()^2)/(2c()*a())));}
+ real gamma() {return degrees(acos((a()^2+b()^2-c()^2)/(2a()*b())));}
+
+ /*<asyxml><method type="path" signature="Path()"><code></asyxml>*/
+ path Path()
+ {/*<asyxml></code><documentation>The path of the triangle.</documentation></method></asyxml>*/
+ return A--C--B--cycle;
+ }
+
+ /*<asyxml><struct signature="side"><code></asyxml>*/
+ struct side
+ {/*<asyxml></code><documentation>Structure used to communicate the side of a triangle.</documentation><property type="int" signature="n"><code></asyxml>*/
+ int n;/*<asyxml></code><documentation>1 or 0 means [AB], -1 means [BA], 2 means [BC], -2 means [CB] etc.</documentation></property><property type="triangle" signature="triangle"><code></asyxml>*/
+ triangle t;/*<asyxml></code><documentation>The triangle to which the side refers.</documentation></property></asyxml>*/
+ }/*<asyxml></struct></asyxml>*/
+
+ /*<asyxml><property type="side" signature="AB"><code></asyxml>*/
+ side AB;/*<asyxml></code><documentation>For the routines using the structure 'side', triangle.AB means 'side AB'.
+ BA, AC, CA etc are also defined.</documentation></property></asyxml>*/
+ AB.n=1; AB.t=this;
+ side BA; BA.n=-1; BA.t=this;
+ side BC; BC.n=2; BC.t=this;
+ side CB; CB.n=-2; CB.t=this;
+ side CA; CA.n=3; CA.t=this;
+ side AC; AC.n=-3; AC.t=this;
+
+ /*<asyxml><method type="side" signature="side(int)"><code></asyxml>*/
+ side side(int n)
+ {/*<asyxml></code><documentation>Return numbered side.
+ n is 1 means AB, -1 means BA, 2 means BC, -2 means CB, etc.</documentation></method></asyxml>*/
+ if(n == 0) abort('Invalid side number.');
+ int an=numarray[abs(n)-1];
+ if(an == 1) return n > 0 ? AB : BA;
+ else if(an == 2) return n > 0 ? BC : CB;
+ return n > 0 ? CA : AC;
+ }
+
+ /*<asyxml><method type="line" signature="line(int)"><code></asyxml>*/
+ line line(int n)
+ {/*<asyxml></code><documentation>Return the numbered line.</documentation></method></asyxml>*/
+ if(n == 0) abort('Invalid line number.');
+ int an=numarray[abs(n)-1];
+ if(an == 1) return n > 0 ? line(A,B) : line(B,A);
+ else if(an == 2) return n > 0 ? line(B,C) : line(C,B);
+ return n > 0 ? line(C,A) : line(A,C);
+ }
+
+}/*<asyxml></struct></asyxml>*/
+
+from triangle unravel side; // The structure 'side' is now available outside the triangle structure.
+from triangle unravel vertex; // The structure 'vertex' is now available outside the triangle structure.
+
+triangle[] operator ^^(triangle[] t1, triangle t2)
+{
+ triangle[] T;
+ for (int i=0; i < t1.length; ++i) T.push(t1[i]);
+ T.push(t2);
+ return T;
+}
+
+triangle[] operator ^^(... triangle[] t)
+{
+ triangle[] T;
+ for (int i=0; i < t.length; ++i) {
+ T.push(t[i]);
+ }
+ return T;
+}
+
+/*<asyxml><operator type="line" signature="cast(side)"><code></asyxml>*/
+line operator cast(side side)
+{/*<asyxml></code><documentation>Cast side to (infinite) line.
+ Most routine with line parameters works with side parameters.
+ One can use the code 'segment(a_side)' to obtain a line segment.</documentation></operator></asyxml>*/
+ triangle t=side.t;
+ return t.line(side.n);
+}
+
+/*<asyxml><function type="line" signature="line(explicit side)"><code></asyxml>*/
+line line(explicit side side)
+{/*<asyxml></code><documentation>Return 'side' as line.</documentation></function></asyxml>*/
+ return (line)side;
+}
+
+/*<asyxml><function type="segment" signature="segment(explicit side)"><code></asyxml>*/
+segment segment(explicit side side)
+{/*<asyxml></code><documentation>Return 'side' as segment.</documentation></function></asyxml>*/
+ return (segment)(line)side;
+}
+
+/*<asyxml><operator type="point" signature="cast(vertex)"><code></asyxml>*/
+point operator cast(vertex V)
+{/*<asyxml></code><documentation>Cast vertex to point.
+ Most routine with point parameters works with vertex parameters.</documentation></operator></asyxml>*/
+ return V.t.point(V.n);
+}
+
+/*<asyxml><function type="point" signature="point(explicit vertex)"><code></asyxml>*/
+point point(explicit vertex V)
+{/*<asyxml></code><documentation>Return the point corresponding to the vertex 'V'.</documentation></function></asyxml>*/
+ return (point)V;
+}
+
+/*<asyxml><function type="side" signature="opposite(vertex)"><code></asyxml>*/
+side opposite(vertex V)
+{/*<asyxml></code><documentation>Return the opposite side of vertex 'V'.</documentation></function></asyxml>*/
+ return V.t.side(numarray[abs(V.n)]);
+}
+
+/*<asyxml><function type="vertex" signature="opposite(side)"><code></asyxml>*/
+vertex opposite(side side)
+{/*<asyxml></code><documentation>Return the opposite vertex of side 'side'.</documentation></function></asyxml>*/
+ return side.t.vertex(numarray[abs(side.n)+1]);
+}
+
+/*<asyxml><function type="point" signature="midpoint(side)"><code></asyxml>*/
+point midpoint(side side)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return midpoint(segment(side));
+}
+
+/*<asyxml><operator type="triangle" signature="*(transform,triangle)"><code></asyxml>*/
+triangle operator *(transform T, triangle t)
+{/*<asyxml></code><documentation>Provide transform*triangle.</documentation></operator></asyxml>*/
+ return triangle(T*t.A,T*t.B,T*t.C);
+}
+
+/*<asyxml><function type="triangle" signature="triangleAbc(real,real,real,real,point)"><code></asyxml>*/
+triangle triangleAbc(real alpha, real b, real c, real angle=0, point A=(0,0))
+{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BAC=alpha, AC=b and AB=c.</documentation></function></asyxml>*/
+ triangle T;
+ coordsys R=A.coordsys;
+ T.init(A,A+R.polar(c,radians(angle)),A+R.polar(b,radians(angle+alpha)));
+ return T;
+}
+
+/*<asyxml><function type="triangle" signature="triangleabc(real,real,real,real,point)"><code></asyxml>*/
+triangle triangleabc(real a, real b, real c, real angle=0, point A=(0,0))
+{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BC=a, AC=b and AB=c.</documentation></function></asyxml>*/
+ triangle T;
+ coordsys R=A.coordsys;
+ T.init(A,A+R.polar(c,radians(angle)),A+R.polar(b,radians(angle)+acos((b^2+c^2-a^2)/(2*b*c))));
+ return T;
+}
+
+/*<asyxml><function type="triangle" signature="triangle(line,line,line)"><code></asyxml>*/
+triangle triangle(line l1, line l2, line l3)
+{/*<asyxml></code><documentation>Return the triangle defined by three line.</documentation></function></asyxml>*/
+ point P1,P2,P3;
+ P1=intersectionpoint(l1,l2);
+ P2=intersectionpoint(l1,l3);
+ P3=intersectionpoint(l2,l3);
+ if(!(defined(P1) && defined(P2) && defined(P3))) abort("triangle: two lines are parallel.");
+ return triangle(P1,P2,P3);
+}
+
+/*<asyxml><function type="point" signature="foot(vertex)"><code></asyxml>*/
+point foot(vertex V)
+{/*<asyxml></code><documentation>Return the endpoint of the altitude from V.</documentation></function></asyxml>*/
+ return projection((line)opposite(V))*((point)V);
+}
+
+/*<asyxml><function type="point" signature="foot(side)"><code></asyxml>*/
+point foot(side side)
+{/*<asyxml></code><documentation>Return the endpoint of the altitude on 'side'.</documentation></function></asyxml>*/
+ return projection((line)side)*point(opposite(side));
+}
+
+/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/
+line altitude(vertex V)
+{/*<asyxml></code><documentation>Return the altitude passing through 'V'.</documentation></function></asyxml>*/
+ return line(point(V),foot(V));
+}
+
+/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/
+line altitude(side side)
+{/*<asyxml></code><documentation>Return the altitude cutting 'side'.</documentation></function></asyxml>*/
+ return altitude(opposite(side));
+}
+
+/*<asyxml><function type="point" signature="orthocentercenter(triangle)"><code></asyxml>*/
+point orthocentercenter(triangle t)
+{/*<asyxml></code><documentation>Return the orthocenter of the triangle t.</documentation></function></asyxml>*/
+ return orthocentercenter(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="point" signature="centroid(triangle)"><code></asyxml>*/
+point centroid(triangle t)
+{/*<asyxml></code><documentation>Return the centroid of the triangle 't'.</documentation></function></asyxml>*/
+ return (t.A+t.B+t.C)/3;
+}
+
+/*<asyxml><function type="point" signature="circumcenter(triangle)"><code></asyxml>*/
+point circumcenter(triangle t)
+{/*<asyxml></code><documentation>Return the circumcenter of the triangle 't'.</documentation></function></asyxml>*/
+ return circumcenter(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="circle" signature="circle(triangle)"><code></asyxml>*/
+circle circle(triangle t)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/
+ return circle(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="circle" signature="circumcircle(triangle)"><code></asyxml>*/
+circle circumcircle(triangle t)
+{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/
+ return circle(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="point" signature="incenter(triangle)"><code></asyxml>*/
+point incenter(triangle t)
+{/*<asyxml></code><documentation>Return the center of the incircle of the triangle 't'.</documentation></function></asyxml>*/
+ return incenter(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="real" signature="inradius(triangle)"><code></asyxml>*/
+real inradius(triangle t)
+{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle 't'.</documentation></function></asyxml>*/
+ return inradius(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="circle" signature="incircle(triangle)"><code></asyxml>*/
+circle incircle(triangle t)
+{/*<asyxml></code><documentation>Return the the incircle of the triangle 't'.</documentation></function></asyxml>*/
+ return incircle(t.A,t.B,t.C);
+}
+
+/*<asyxml><function type="point" signature="excenter(side,triangle)"><code></asyxml>*/
+point excenter(side side)
+{/*<asyxml></code><documentation>Return the center of the excircle tangent with the side 'side' of its triangle.
+ side=0 means AB, 1 means AC, other means BC.
+ One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
+ point op;
+ triangle t=side.t;
+ int n=numarray[abs(side.n)-1];
+ if(n == 1) op=excenter(t.A,t.B,t.C);
+ else if(n == 2) op=excenter(t.B,t.C,t.A);
+ else op=excenter(t.C,t.A,t.B);
+ return op;
+}
+
+/*<asyxml><function type="real" signature="exradius(side,triangle)"><code></asyxml>*/
+real exradius(side side)
+{/*<asyxml></code><documentation>Return radius of the excircle tangent with the side 'side' of its triangle.
+ side=0 means AB, 1 means BC, other means CA.
+ One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
+ real or;
+ triangle t=side.t;
+ int n=numarray[abs(side.n)-1];
+ if(n == 1) or=exradius(t.A,t.B,t.C);
+ else if(n == 2) or=exradius(t.B,t.C,t.A);
+ else or=exradius(t.A,t.C,t.B);
+ return or;
+}
+
+/*<asyxml><function type="circle" signature="excircle(side,triangle)"><code></asyxml>*/
+circle excircle(side side)
+{/*<asyxml></code><documentation>Return the excircle tangent with the side 'side' of its triangle.
+ side=0 means AB, 1 means AC, other means BC.
+ One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
+ circle oc;
+ int n=numarray[abs(side.n)-1];
+ triangle t=side.t;
+ if(n == 1) oc=excircle(t.A,t.B,t.C);
+ else if(n == 2) oc=excircle(t.B,t.C,t.A);
+ else oc=excircle(t.A,t.C,t.B);
+ return oc;
+}
+
+/*<asyxml><struct signature="trilinear"><code></asyxml>*/
+struct trilinear
+{/*<asyxml></code><documentation>Trilinear coordinates 'a:b:c' relative to triangle 't'.
+ <url href="http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation><property type="real" signature="a,b,c"><code></asyxml>*/
+ real a,b,c;/*<asyxml></code><documentation>The trilinear coordinates.</documentation></property><property type="triangle" signature="t"><code></asyxml>*/
+ triangle t;/*<asyxml></code><documentation>The reference triangle.</documentation></property></asyxml>*/
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="trilinear" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/
+trilinear trilinear(triangle t, real a, real b, real c)
+{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'.
+ <url href="http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
+ trilinear ot;
+ ot.a=a; ot.b=b; ot.c=c;
+ ot.t=t;
+ return ot;
+}
+
+/*<asyxml><function type="trilinear" signature="trilinear(triangle,point)"><code></asyxml>*/
+trilinear trilinear(triangle t, point M)
+{/*<asyxml></code><documentation>Return the trilinear coordinates of 'M' relative to 't'.
+ <url href="http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
+ trilinear ot;
+ pair m=locate(M);
+ int sameside(pair A, pair B, pair m, pair p)
+ {// Return 1 if 'm' and 'p' are same side of line (AB) else return -1.
+ pair mil=(A+B)/2;
+ pair mA=rotate(90,mil)*A;
+ pair mB=rotate(-90,mil)*A;
+ return (abs(m-mA) <= abs(m-mB)) == (abs(p-mA) <= abs(p-mB)) ? 1 : -1;
+ }
+ real det(pair a, pair b) {return a.x*b.y-a.y*b.x;}
+ real area(pair a, pair b, pair c){return 0.5*abs(det(a,b)+det(b,c)+det(c,a));}
+ pair A=t.A, B=t.B, C=t.C;
+ real t1=area(B,C,m), t2=area(C,A,m), t3=area(A,B,m);
+ ot.a=sameside(B,C,A,m)*t1/t.a();
+ ot.b=sameside(A,C,B,m)*t2/t.b();
+ ot.c=sameside(A,B,C,m)*t3/t.c();
+ ot.t=t;
+ return ot;
+}
+
+/*<asyxml><function type="void" signature="write(trilinear)"><code></asyxml>*/
+void write(trilinear tri)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ write(format("%f : ", tri.a)+format("%f : ", tri.b)+format("%f",tri.c));
+}
+
+/*<asyxml><function type="point" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/
+point point(trilinear tri)
+{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'.
+ <url href="http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
+ triangle t=tri.t;
+ return masscenter(0.5*t.a()*mass(t.A,tri.a),
+ 0.5*t.b()*mass(t.B,tri.b),
+ 0.5*t.c()*mass(t.C,tri.c));
+}
+
+/*<asyxml><function type="int[]" signature="tricoef(side)"><code></asyxml>*/
+int[] tricoef(side side)
+{/*<asyxml></code><documentation>Return an array of integer (values are 0 or 1) which represents 'side'.
+ For example, side=t.BC will be represented by {0,1,1}.</documentation></function></asyxml>*/
+ int[] oi;
+ int n=numarray[abs(side.n)-1];
+ oi.push((n == 1 || n == 3) ? 1 : 0);
+ oi.push((n == 1 || n == 2) ? 1 : 0);
+ oi.push((n == 2 || n == 3) ? 1 : 0);
+ return oi;
+}
+
+/*<asyxml><operator type="point" signature="cast(trilinear)"><code></asyxml>*/
+point operator cast(trilinear tri)
+{/*<asyxml></code><documentation>Cast trilinear to point.
+ One may use the routine 'point(trilinear)' to force the casting.</documentation></operator></asyxml>*/
+ return point(tri);
+}
+
+/*<asyxml><typedef type="centerfunction" return="real" params="real,real,real"><code></asyxml>*/
+typedef real centerfunction(real,real,real);/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></typedef></asyxml>*/
+
+/*<asyxml><function type="trilinear" signature="trilinear(triangle,centerfunction,real,real,real)"><code></asyxml>*/
+trilinear trilinear(triangle t, centerfunction f, real a=t.a(), real b=t.b(), real c=t.c())
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></function></asyxml>*/
+ return trilinear(t,f(a,b,c),f(b,c,a),f(c,a,b));
+}
+
+/*<asyxml><function type="point" signature="symmedian(triangle)"><code></asyxml>*/
+point symmedian(triangle t)
+{/*<asyxml></code><documentation>Return the symmedian point of 't'.</documentation></function></asyxml>*/
+ point A,B,C;
+ real a=t.a(), b=t.b(), c=t.c();
+ A=trilinear(t, 0, b, c);
+ B=trilinear(t, a, 0, c);
+ return intersectionpoint(line(t.A,A),line(t.B,B));
+}
+
+/*<asyxml><function type="point" signature="symmedian(side)"><code></asyxml>*/
+point symmedian(side side)
+{/*<asyxml></code><documentation>The symmedian point on the side 'side'.</documentation></function></asyxml>*/
+ triangle t=side.t;
+ int n=numarray[abs(side.n)-1];
+ if(n == 1) return trilinear(t, t.a(), t.b(), 0);
+ if(n == 2) return trilinear(t, 0, t.b(), t.c());
+ return trilinear(t, t.a(), 0, t.c());
+}
+
+/*<asyxml><function type="line" signature="symmedian(vertex)"><code></asyxml>*/
+line symmedian(vertex V)
+{/*<asyxml></code><documentation>Return the symmedian passing through 'V'.</documentation></function></asyxml>*/
+ return line(point(V),symmedian(V.t));
+}
+
+/*<asyxml><function type="triangle" signature="cevian(triangle,point)"><code></asyxml>*/
+triangle cevian(triangle t, point P)
+{/*<asyxml></code><documentation>Return the Cevian triangle with respect of 'P'
+ <url href="http://mathworld.wolfram.com/CevianTriangle.html"/>.</documentation></function></asyxml>*/
+ trilinear tri=trilinear(t,locate(P));
+ point A=point(trilinear(t,0,tri.b,tri.c));
+ point B=point(trilinear(t,tri.a,0,tri.c));
+ point C=point(trilinear(t,tri.a,tri.b,0));
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="point" signature="cevian(side,point)"><code></asyxml>*/
+point cevian(side side, point P)
+{/*<asyxml></code><documentation>Return the Cevian point on 'side' with respect of 'P'.</documentation></function></asyxml>*/
+ triangle t=side.t;
+ trilinear tri=trilinear(t,locate(P));
+ int[] s=tricoef(side);
+ return point(trilinear(t,s[0]*tri.a, s[1]*tri.b, s[2]*tri.c));
+}
+
+/*<asyxml><function type="line" signature="cevian(vertex,point)"><code></asyxml>*/
+line cevian(vertex V, point P)
+{/*<asyxml></code><documentation>Return line passing through 'V' and its Cevian image with respect of 'P'.</documentation></function></asyxml>*/
+ return line(point(V), cevian(opposite(V), P));
+}
+
+/*<asyxml><function type="point" signature="gergonne(triangle)"><code></asyxml>*/
+point gergonne(triangle t)
+{/*<asyxml></code><documentation>Return the Gergonne point of 't'.</documentation></function></asyxml>*/
+ real f(real a, real b, real c){return 1/(a*(b+c-a));}
+ return point(trilinear(t,f));
+}
+
+/*<asyxml><function type="point[]" signature="fermat(triangle)"><code></asyxml>*/
+point[] fermat(triangle t)
+{/*<asyxml></code><documentation>Return the Fermat points of 't'.</documentation></function></asyxml>*/
+ point[] P;
+ real A=t.alpha(), B=t.beta(), C=t.gamma();
+ P.push(point(trilinear(t,1/Sin(A+60), 1/Sin(B+60), 1/Sin(C+60))));
+ P.push(point(trilinear(t,1/Sin(A-60), 1/Sin(B-60), 1/Sin(C-60))));
+ return P;
+}
+
+/*<asyxml><function type="point" signature="isotomicconjugate(triangle,point)"><code></asyxml>*/
+point isotomicconjugate(triangle t, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
+ if(!inside(t.Path(),locate(M))) abort("isotomic: the point must be inside the triangle.");
+ trilinear tr=trilinear(t,M);
+ return point(trilinear(t,1/(t.a()^2*tr.a),1/(t.b()^2*tr.b),1/(t.c()^2*tr.c)));
+}
+
+/*<asyxml><function type="line" signature="isotomic(vertex,point)"><code></asyxml>*/
+line isotomic(vertex V, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsotomicConjugate.html"/>.</documentation></function></asyxml>*/
+ side op=opposite(V);
+ return line(V,rotate(180,midpoint(op))*cevian(op,M));
+}
+
+/*<asyxml><function type="point" signature="isotomic(side,point)"><code></asyxml>*/
+point isotomic(side side, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
+ return intersectionpoint(isotomic(opposite(side),M), side);
+}
+
+/*<asyxml><function type="triangle" signature="isotomic(triangle,point)"><code></asyxml>*/
+triangle isotomic(triangle t, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
+ return triangle(isotomic(t.BC,M),isotomic(t.CA,M),isotomic(t.AB,M));
+}
+
+/*<asyxml><function type="point" signature="isogonalconjugate(triangle,point)"><code></asyxml>*/
+point isogonalconjugate(triangle t, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ trilinear tr=trilinear(t,M);
+ return point(trilinear(t,1/tr.a,1/tr.b,1/tr.c));
+}
+
+/*<asyxml><function type="point" signature="isogonal(side,point)"><code></asyxml>*/
+point isogonal(side side, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ return cevian(side,isogonalconjugate(side.t,M));
+}
+
+/*<asyxml><function type="line" signature="isogonal(vertex,point)"><code></asyxml>*/
+line isogonal(vertex V, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ return line(V,isogonal(opposite(V),M));
+}
+
+/*<asyxml><function type="triangle" signature="isogonal(triangle,point)"><code></asyxml>*/
+triangle isogonal(triangle t, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
+ return triangle(isogonal(t.BC,M),isogonal(t.CA,M),isogonal(t.AB,M));
+}
+
+/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/
+triangle pedal(triangle t, point M)
+{/*<asyxml></code><documentation>Return the pedal triangle of 'M' in 't'.
+ <url href="http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/
+ return triangle(projection(t.BC)*M,projection(t.AC)*M,projection(t.AB)*M);
+}
+
+/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/
+line pedal(side side, point M)
+{/*<asyxml></code><documentation>Return the pedal line of 'M' cutting 'side'.
+ <url href="http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/
+ return line(M, projection(side)*M);
+}
+
+/*<asyxml><function type="triangle" signature="antipedal(triangle,point)"><code></asyxml>*/
+triangle antipedal(triangle t, point M)
+{/*<asyxml></code><documentation><url href="http://mathworld.wolfram.com/AntipedalTriangle.html"/></documentation></function></asyxml>*/
+ trilinear Tm=trilinear(t,M);
+ real a=Tm.a, b=Tm.b, c=Tm.c;
+ real CA=Cos(t.alpha()), CB=Cos(t.beta()), CC=Cos(t.gamma());
+ point A=trilinear(t,-(b+a*CC)*(c+a*CB),(c+a*CB)*(a+b*CC),(b+a*CC)*(a+c*CB));
+ point B=trilinear(t,(c+b*CA)*(b+a*CC),-(c+b*CA)*(a+b*CC),(a+b*CC)*(b+c*CA));
+ point C=trilinear(t,(b+c*CA)*(c+a*CB),(a+c*CB)*(c+b*CA),-(a+c*CB)*(b+c*CA));
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/
+triangle extouch(triangle t)
+{/*<asyxml></code><documentation>Return the extouch triangle of the triangle 't'.
+ The extouch triangle of 't' is the triangle formed by the points
+ of tangency of a triangle 't' with its excircles.</documentation></function></asyxml>*/
+ point A,B,C;
+ real a=t.a(), b=t.b(), c=t.c();
+ A=trilinear(t, 0, (a-b+c)/b, (a+b-c)/c);
+ B=trilinear(t, (-a+b+c)/a, 0, (a+b-c)/c);
+ C=trilinear(t, (-a+b+c)/a, (a-b+c)/b, 0);
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/
+triangle incentral(triangle t)
+{/*<asyxml></code><documentation>Return the incentral triangle of the triangle 't'.
+ It is the triangle whose vertices are determined by the intersections of the
+ reference triangle's angle bisectors with the respective opposite sides.</documentation></function></asyxml>*/
+ point A,B,C;
+ // real a=t.a(), b=t.b(), c=t.c();
+ A=trilinear(t, 0, 1, 1);
+ B=trilinear(t, 1, 0, 1);
+ C=trilinear(t, 1, 1, 0);
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="triangle" signature="extouch(side)"><code></asyxml>*/
+triangle extouch(side side)
+{/*<asyxml></code><documentation>Return the triangle formed by the points of tangency of the triangle referenced by 'side' with its excircles.
+ One vertex of the returned triangle is on the segment 'side'.</documentation></function></asyxml>*/
+ triangle t=side.t;
+ transform p1=projection((line)t.AB);
+ transform p2=projection((line)t.AC);
+ transform p3=projection((line)t.BC);
+ point EP=excenter(side);
+ return triangle(p3*EP,p2*EP,p1*EP);
+}
+
+/*<asyxml><function type="point" signature="bisectorpoint(side)"><code></asyxml>*/
+point bisectorpoint(side side)
+{/*<asyxml></code><documentation>The intersection point of the angle bisector from the
+ opposite point of 'side' with the side 'side'.</documentation></function></asyxml>*/
+ triangle t=side.t;
+ int n=numarray[abs(side.n)-1];
+ if(n == 1) return trilinear(t, 1, 1, 0);
+ if(n == 2) return trilinear(t, 0, 1, 1);
+ return trilinear(t, 1, 0, 1);
+}
+
+/*<asyxml><function type="line" signature="bisector(vertex,real)"><code></asyxml>*/
+line bisector(vertex V, real angle=0)
+{/*<asyxml></code><documentation>Return the interior bisector passing through 'V' rotated by angle (in degrees)
+ around 'V'.</documentation></function></asyxml>*/
+ return rotate(angle,point(V))*line(point(V),incenter(V.t));
+}
+
+/*<asyxml><function type="line" signature="bisector(side)"><code></asyxml>*/
+line bisector(side side)
+{/*<asyxml></code><documentation>Return the bisector of the line segment 'side'.</documentation></function></asyxml>*/
+ return bisector(segment(side));
+}
+
+/*<asyxml><function type="point" signature="intouch(side)"><code></asyxml>*/
+point intouch(side side)
+{/*<asyxml></code><documentation>The point of tangency on the side 'side' of its incircle.</documentation></function></asyxml>*/
+ triangle t=side.t;
+ real a=t.a(), b=t.b(), c=t.c();
+ int n=numarray[abs(side.n)-1];
+ if(n == 1) return trilinear(t, b*c/(-a+b+c),a*c/(a-b+c), 0);
+ if(n == 2) return trilinear(t, 0, a*c/(a-b+c), a*b/(a+b-c));
+ return trilinear(t, b*c/(-a+b+c), 0, a*b/(a+b-c));
+}
+
+/*<asyxml><function type="triangle" signature="intouch(triangle)"><code></asyxml>*/
+triangle intouch(triangle t)
+{/*<asyxml></code><documentation>Return the intouch triangle of the triangle 't'.
+ The intouch triangle of 't' is the triangle formed by the points
+ of tangency of a triangle 't' with its incircles.</documentation></function></asyxml>*/
+ point A,B,C;
+ real a=t.a(), b=t.b(), c=t.c();
+ A=trilinear(t, 0, a*c/(a-b+c), a*b/(a+b-c));
+ B=trilinear(t, b*c/(-a+b+c), 0, a*b/(a+b-c));
+ C=trilinear(t, b*c/(-a+b+c), a*c/(a-b+c), 0);
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="triangle" signature="tangential(triangle)"><code></asyxml>*/
+triangle tangential(triangle t)
+{/*<asyxml></code><documentation>Return the tangential triangle of the triangle 't'.
+ The tangential triangle of 't' is the triangle formed by the lines
+ tangent to the circumcircle of the given triangle 't' at its vertices.</documentation></function></asyxml>*/
+ point A,B,C;
+ real a=t.a(), b=t.b(), c=t.c();
+ A=trilinear(t, -a, b, c);
+ B=trilinear(t, a, -b, c);
+ C=trilinear(t, a, b, -c);
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="triangle" signature="medial(triangle t)"><code></asyxml>*/
+triangle medial(triangle t)
+{/*<asyxml></code><documentation>Return the triangle whose vertices are midpoints of the sides of 't'.</documentation></function></asyxml>*/
+ return triangle(midpoint(t.BC),midpoint(t.AC),midpoint(t.AB));
+}
+
+/*<asyxml><function type="line" signature="median(vertex)"><code></asyxml>*/
+line median(vertex V)
+{/*<asyxml></code><documentation>Return median from 'V'.</documentation></function></asyxml>*/
+ return line(point(V),midpoint(segment(opposite(V))));
+}
+
+/*<asyxml><function type="line" signature="median(side)"><code></asyxml>*/
+line median(side side)
+{/*<asyxml></code><documentation>Return median from the opposite vertex of 'side'.</documentation></function></asyxml>*/
+ return median(opposite(side));
+}
+
+/*<asyxml><function type="triangle" signature="orthic(triangle)"><code></asyxml>*/
+triangle orthic(triangle t)
+{/*<asyxml></code><documentation>Return the triangle whose vertices are endpoints of the altitudes from each of the vertices of 't'.</documentation></function></asyxml>*/
+ return triangle(foot(t.BC),foot(t.AC),foot(t.AB));
+}
+
+/*<asyxml><function type="triangle" signature="symmedial(triangle)"><code></asyxml>*/
+triangle symmedial(triangle t)
+{/*<asyxml></code><documentation>Return the symmedial triangle of 't'.</documentation></function></asyxml>*/
+ point A,B,C;
+ real a=t.a(), b=t.b(), c=t.c();
+ A=trilinear(t, 0, b, c);
+ B=trilinear(t, a, 0, c);
+ C=trilinear(t, a, b, 0);
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="triangle" signature="anticomplementary(triangle)"><code></asyxml>*/
+triangle anticomplementary(triangle t)
+{/*<asyxml></code><documentation>Return the triangle which has the given triangle 't' as its medial triangle.</documentation></function></asyxml>*/
+ real a=t.a(), b=t.b(), c=t.c();
+ real ab=a*b, bc=b*c, ca=c*a;
+ point A=trilinear(t, -bc, ca, ab);
+ point B=trilinear(t, bc, -ca, ab);
+ point C=trilinear(t, bc, ca, -ab);
+ return triangle(A,B,C);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,line,bool)"><code></asyxml>*/
+point[] intersectionpoints(triangle t, line l, bool extended=false)
+{/*<asyxml></code><documentation>Return the intersection points.
+ If 'extended' is true, the sides are lines else the sides are segments.
+ intersectionpoints(line,triangle,bool) is also defined.</documentation></function></asyxml>*/
+ point[] OP;
+ void addpoint(point P)
+ {
+ if(defined(P)) {
+ bool exist=false;
+ for (int i=0; i < OP.length; ++i) {
+ if(P == OP[i]) {exist=true; break;}
+ }
+ if(!exist) OP.push(P);
+ }
+ }
+ if(extended) {
+ for (int i=0; i < 3; ++i) {
+ addpoint(intersectionpoint(t.line(i),l));
+ }
+ } else {
+ for (int i=0; i < 3; ++i) {
+ addpoint(intersectionpoint((segment)t.line(i),l));
+ }
+ }
+ return OP;
+}
+
+point[] intersectionpoints(line l, triangle t, bool extended=false)
+{
+ return intersectionpoints(t, l, extended);
+}
+
+/*<asyxml><function type="vector" signature="dir(vertex)"><code></asyxml>*/
+vector dir(vertex V)
+{/*<asyxml></code><documentation>The direction (towards the outside of the triangle) of the interior angle bisector of 'V'.</documentation></function></asyxml>*/
+ triangle t=V.t;
+ if(V.n == 1) return vector(defaultcoordsys,(-dir(t.A--t.B,t.A--t.C)));
+ if(V.n == 2) return vector(defaultcoordsys,(-dir(t.B--t.A,t.B--t.C)));
+ return vector(defaultcoordsys,(-dir(t.C--t.A,t.C--t.B)));
+}
+
+/*<asyxml><function type="void" signature="lvoid label(picture,Label,vertex,pair,real,pen,filltype)"><code></asyxml>*/
+void label(picture pic=currentpicture, Label L, vertex V,
+ pair align=dir(V),
+ real alignFactor=1,
+ pen p=nullpen, filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw 'L' on picture 'pic' at vertex 'V' aligned by 'alignFactor*align'.</documentation></function></asyxml>*/
+ label(pic,L,locate(point(V)),alignFactor*align,p,filltype);
+}
+
+/*<asyxml><function type="void" signature="label(picture,Label,Label,Label,triangle,real,real,pen,filltype)"><code></asyxml>*/
+void label(picture pic=currentpicture, Label LA="$A$",
+ Label LB="$B$", Label LC="$C$",
+ triangle t,
+ real alignAngle=0,
+ real alignFactor=1,
+ pen p=nullpen, filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw labels LA, LB and LC aligned in the rotated (by 'alignAngle' in degrees) direction
+ (towards the outside of the triangle) of the interior angle bisector of vertices.
+ One can individually modify the alignment by setting the Label parameter 'align'.</documentation></function></asyxml>*/
+ Label lla=LA.copy();
+ lla.align(lla.align,rotate(alignAngle)*locate(dir(t.VA)));
+ label(pic,LA,t.VA,align=lla.align.dir,alignFactor=alignFactor,p,filltype);
+ Label llb=LB.copy();
+ llb.align(llb.align,rotate(alignAngle)*locate(dir(t.VB)));
+ label(pic,llb,t.VB,align=llb.align.dir,alignFactor=alignFactor,p,filltype);
+ Label llc=LC.copy();
+ llc.align(llc.align,rotate(alignAngle)*locate(dir(t.VC)));
+ label(pic,llc,t.VC,align=llc.align.dir,alignFactor=alignFactor,p,filltype);
+}
+
+/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,Label,Label,Label,triangle,pen,filltype)"><code></asyxml>*/
+void show(picture pic=currentpicture,
+ Label LA="$A$", Label LB="$B$", Label LC="$C$",
+ Label La="$a$", Label Lb="$b$", Label Lc="$c$",
+ triangle t, pen p=currentpen, filltype filltype=NoFill)
+{/*<asyxml></code><documentation>Draw triangle and labels of sides and vertices.</documentation></function></asyxml>*/
+ pair a=locate(t.A), b=locate(t.B), c=locate(t.C);
+ draw(pic,a--b--c--cycle,p);
+ label(pic,LA,a,-dir(a--b,a--c),p,filltype);
+ label(pic,LB,b,-dir(b--a,b--c),p,filltype);
+ label(pic,LC,c,-dir(c--a,c--b),p,filltype);
+ pair aligna=I*unit(c-b), alignb=I*unit(c-a), alignc=I*unit(b-a);
+ pair mAB=locate(midpoint(t.AB)), mAC=locate(midpoint(t.AC)), mBC=locate(midpoint(t.BC));
+ draw(pic,La,b--c, align=rotate(dot(a-mBC,aligna) > 0 ? 180 :0)*aligna,p);
+ draw(pic,Lb,a--c, align=rotate(dot(b-mAC,alignb) > 0 ? 180 :0)*alignb,p);
+ draw(pic,Lc,a--b, align=rotate(dot(c-mAB,alignc) > 0 ? 180 :0)*alignc,p);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,triangle,pen,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, triangle t, pen p=currentpen, marker marker=nomarker)
+{/*<asyxml></code><documentation>Draw sides of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
+ draw(pic,t.Path(),p,marker);
+}
+
+/*<asyxml><function type="void" signature="draw(picture,triangle[],pen,marker)"><code></asyxml>*/
+void draw(picture pic=currentpicture, triangle[] t, pen p=currentpen, marker marker=nomarker)
+{/*<asyxml></code><documentation>Draw sides of the triangles 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
+ for(int i=0; i < t.length; ++i) draw(pic,t[i],p,marker);
+}
+
+/*<asyxml><function type="void" signature="drawline(picture,triangle,pen)"><code></asyxml>*/
+void drawline(picture pic=currentpicture, triangle t, pen p=currentpen)
+{/*<asyxml></code><documentation>Draw lines of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
+ draw(t,p);
+ draw(pic,line(t.A,t.B),p);
+ draw(pic,line(t.A,t.C),p);
+ draw(pic,line(t.B,t.C),p);
+}
+
+/*<asyxml><function type="void" signature="dot(picture,triangle,pen)"><code></asyxml>*/
+void dot(picture pic=currentpicture, triangle t, pen p=currentpen)
+{/*<asyxml></code><documentation>Draw a dot at each vertex of 't'.</documentation></function></asyxml>*/
+ dot(pic, t.A^^t.B^^t.C, p);
+}
+// *.......................TRIANGLES.......................*
+// *=======================================================*
+
+// *=======================================================*
+// *.......................INVERSIONS......................*
+/*<asyxml><function type="point" signature="inverse(real k,point,point)"><code></asyxml>*/
+point inverse(real k, point A, point M)
+{/*<asyxml></code><documentation>Return the inverse point of 'M' with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
+ return A+k/conj(M-A);
+}
+
+/*<asyxml><function type="point" signature="radicalcenter(circle,circle)"><code></asyxml>*/
+point radicalcenter(circle c1, circle c2)
+{/*<asyxml></code><documentation><url href="http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(c1.C,c2.C);
+ real k=c1.r^2-c2.r^2;
+ pair C1=locate(c1.C);
+ pair C2=locate(c2.C);
+ pair oop=C2-C1;
+ pair K=(abs(oop) == 0) ?
+ (infinity,infinity) :
+ midpoint(C1--C2)+0.5*k*oop/dot(oop,oop);
+ return point(P[0].coordsys,K/P[0].coordsys);
+}
+
+/*<asyxml><function type="line" signature="radicalline(circle,circle)"><code></asyxml>*/
+line radicalline(circle c1, circle c2)
+{/*<asyxml></code><documentation><url href="http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
+ if (c1.C == c2.C) abort("radicalline: the centers must be distinct");
+ return perpendicular(radicalcenter(c1,c2),line(c1.C,c2.C));
+}
+
+/*<asyxml><function type="point" signature="radicalcenter(circle,circle,circle)"><code></asyxml>*/
+point radicalcenter(circle c1, circle c2, circle c3)
+{/*<asyxml></code><documentation><url href="http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
+ return intersectionpoint(radicalline(c1,c2),radicalline(c1,c3));
+}
+
+/*<asyxml><struct signature="inversion"><code></asyxml>*/
+struct inversion
+{/*<asyxml></code><documentation>http://mathworld.wolfram.com/Inversion.html</documentation></asyxml>*/
+ point C;
+ real k;
+}/*<asyxml></struct></asyxml>*/
+
+/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/
+inversion inversion(real k, point C)
+{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/
+ inversion oi;
+ oi.k=k;
+ oi.C=C;
+ return oi;
+}
+/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/
+inversion inversion(point C, real k)
+{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/
+ return inversion(k,C);
+}
+
+/*<asyxml><function type="inversion" signature="inversion(circle,circle)"><code></asyxml>*/
+inversion inversion(circle c1, circle c2, real sgn=1)
+{/*<asyxml></code><documentation>Return the inversion which transforms 'c1' to
+ . 'c2' and positive inversion radius if 'sgn > 0';
+ . 'c2' and negative inversion radius if 'sgn < 0';
+ . 'c1' and 'c2' to 'c2' if 'sgn = 0'.</documentation></function></asyxml>*/
+ if(sgn == 0) {
+ point O=radicalcenter(c1,c2);
+ return inversion(O^c1, O);
+ }
+ real a=abs(c1.r/c2.r);
+ if(sgn > 0) {
+ point O=c1.C+a/abs(1-a)*(c2.C-c1.C);
+ return inversion(a*abs(abs(O-c2.C)^2-c2.r^2),O);
+ }
+ point O=c1.C+a/abs(1+a)*(c2.C-c1.C);
+ return inversion(-a*abs(abs(O-c2.C)^2-c2.r^2),O);
+}
+
+/*<asyxml><function type="inversion" signature="inversion(circle,circle,circle)"><code></asyxml>*/
+inversion inversion(circle c1, circle c2, circle c3)
+{/*<asyxml></code><documentation>Return the inversion which transform 'c1' to 'c1', 'c2' to 'c2' and 'c3' to 'c3'.</documentation></function></asyxml>*/
+ point Rc=radicalcenter(c1,c2,c3);
+ return inversion(Rc, Rc^c1);
+}
+
+circle operator cast(inversion i){return circle(i.C, sgn(i.k)*sqrt(abs(i.k)));}
+/*<asyxml><function type="circle" signature="circle(inversion)"><code></asyxml>*/
+circle circle(inversion i)
+{/*<asyxml></code><documentation>Return the inversion circle of 'i'.</documentation></function></asyxml>*/
+ return i;
+}
+
+inversion operator cast(circle c)
+{
+ return inversion(sgn(c.r)*c.r^2, c.C);
+}
+/*<asyxml><function type="inversion" signature="inversion(circle)"><code></asyxml>*/
+inversion inversion(circle c)
+{/*<asyxml></code><documentation>Return the inversion represented by the circle of 'c'.</documentation></function></asyxml>*/
+ return c;
+}
+
+/*<asyxml><operator type="point" signature="*(inversion, point)"><code></asyxml>*/
+point operator *(inversion i, point P)
+{/*<asyxml></code><documentation>Provide inversion*point.</documentation></operator></asyxml>*/
+ return inverse(i.k,i.C,P);
+}
+
+/*<asyxml><function type="circle" signature="inverse(real,point,line)"><code></asyxml>*/
+circle inverse(real k, point A, line l)
+{/*<asyxml></code><documentation>Return the inverse circle of 'l' with
+ respect to point 'A' and inversion radius 'k'.</documentation></function></asyxml>*/
+ if(A @ l) {
+ write("Warning: the inversion of the line is not a circle.");
+ write("The returned circle has an infinite radius, cirlce.l have been set.");
+ circle C=circle(A, infinity);
+ C.l=l;
+ return C;
+ }
+ point Ap=inverse(k,A,l.A), Bp=inverse(k,A,l.B);
+ return circle(A,Ap,Bp);
+}
+
+/*<asyxml><operator type="circle" signature="*(inversion,line)"><code></asyxml>*/
+circle operator *(inversion i, line l)
+{/*<asyxml></code><documentation>Provide inversion*line for lines that don't pass through the inversion center.</documentation></operator></asyxml>*/
+ return inverse(i.k,i.C,l);
+}
+
+/*<asyxml><function type="circle" signature="inverse(real,point,circle)"><code></asyxml>*/
+circle inverse(real k, point A, circle c)
+{/*<asyxml></code><documentation>Return the inverse circle of 'c' with
+ respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
+ if(degenerate(c)) return inverse(k,A,c.l);
+ if(A @ c) {
+ write("Warning: the inversion of the circle is not a circle.");
+ write("The returned circle has an infinite radius, cirlce.l have been set.");
+ point M=rotate(180,c.C)*A, Mp=rotate(90,c.C)*A;
+ circle oc=circle(A,infinity);
+ oc.l=line(inverse(k,A,M),inverse(k,A,Mp));
+ return oc;
+ }
+ point[] P=standardizecoordsys(A,c.C);
+ real s=k/((P[1].x-P[0].x)^2+(P[1].y-P[0].y)^2-c.r^2);
+ return circle(P[0]+s*(P[1]-P[0]),abs(s)*c.r);
+}
+
+/*<asyxml><operator type="circle" signature="*(inversion,circle)"><code></asyxml>*/
+circle operator *(inversion i, circle c)
+{/*<asyxml></code><documentation>Provide inversion*circle.</documentation></operator></asyxml>*/
+ return inverse(i.k,i.C,c);
+}
+// *.......................INVERSIONS......................*
+// *=======================================================*
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,circle)"><code></asyxml>*/
+point[] intersectionpoints(line l, circle c)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(circle,line) is also defined.</documentation></function></asyxml>*/
+ if(degenerate(c)) return new point[]{intersectionpoint(l,c.l)};
+ point[] op;
+ coordsys R=samecoordsys(l.A,c.C) ?
+ l.A.coordsys : defaultcoordsys;
+ coordsys Rp=defaultcoordsys;
+ circle cc=circle(changecoordsys(Rp,c.C),c.r);
+ point proj=projection(l)*c.C;
+ if(proj @ cc) { // The line is a tangente of the circle.
+ if(proj @ l) op.push(proj);// line may be a segement...
+ } else {
+ coordsys Rc=cartesiansystem(c.C,(1,0),(0,1));
+ line ll=changecoordsys(Rc,l);
+ pair[] P=intersectionpoints(ll.A.coordinates, ll.B.coordinates,
+ 1, 0, 1, 0, 0, -c.r^2);
+ for (int i=0; i<P.length; ++i) {
+ point inter=changecoordsys(R,point(Rc,P[i]));
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(circle c, line l)
+{
+ return intersectionpoints(l,c);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(line l, ellipse el)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(ellipse,line) is also defined.</documentation></function></asyxml>*/
+ if(el.e == 0) return intersectionpoints(l,(circle)el);
+ if(degenerate(el)) return new point[]{intersectionpoint(l,el.l)};
+ point[] op;
+ coordsys R=samecoordsys(l.A,el.C) ? l.A.coordsys : defaultcoordsys;
+ coordsys Rp=defaultcoordsys;
+ line ll=changecoordsys(Rp,l);
+ ellipse ell=changecoordsys(Rp,el);
+ circle C=circle(ell.C,ell.a);
+ point[] Ip=intersectionpoints(ll,C);
+ if (Ip.length > 0 &&
+ (perpendicular(ll,line(ell.F1,Ip[0])) ||
+ perpendicular(ll,line(ell.F2,Ip[0])))) {
+ // http://www.mathcurve.com/courbes2d/ellipse/ellipse.shtml
+ // Définition tangentielle par antipodaire de cercle.
+ // 'l' is a tangent of 'el'
+ transform t=scale(el.a/el.b,el.F1,el.F2,el.C,rotate(90,el.C)*el.F1);
+ point inter=inverse(t)*intersectionpoints(C,t*ll)[0];
+ if(inter @ l) op.push(inter);
+ } else {
+ coordsys Rc=canonicalcartesiansystem(el);
+ line ll=changecoordsys(Rc,l);
+ pair[] P=intersectionpoints(ll.A.coordinates, ll.B.coordinates,
+ 1/el.a^2, 0, 1/el.b^2, 0, 0, -1);
+ for (int i=0; i<P.length; ++i) {
+ point inter=changecoordsys(R,point(Rc,P[i]));
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(ellipse el, line l)
+{
+ return intersectionpoints(l,el);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,parabola)"><code></asyxml>*/
+point[] intersectionpoints(line l, parabola p)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(parabola,line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ coordsys R=coordsys(p);
+ bool tgt=false;
+ line ll=changecoordsys(R,l),
+ lv=parallel(p.V,p.D);
+ point M=intersectionpoint(lv,ll), tgtp;
+ if(finite(M)) {// Test if 'l' is tangent to 'p'
+ line l1=bisector(line(M,p.F));
+ line l2=rotate(90,M)*lv;
+ point P=intersectionpoint(l1,l2);
+ tgtp=rotate(180,P)*p.F;
+ tgt=(tgtp @ l);
+ }
+ if(tgt) {
+ if(tgtp @ l) op.push(tgtp);
+ } else {
+ real[] eq=changecoordsys(defaultcoordsys,equation(p)).a;
+ pair[] tp=intersectionpoints(locate(l.A),locate(l.B),eq);
+ point inter;
+ for (int i=0; i < tp.length; ++i) {
+ inter=point(R,tp[i]/R);
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(parabola p, line l)
+{
+ return intersectionpoints(l,p);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(line l, hyperbola h)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(hyperbola,line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ coordsys R=coordsys(h);
+ point A=intersectionpoint(l,h.A1), B=intersectionpoint(l,h.A2);
+ point M=midpoint(segment(A,B));
+ bool tgt=M @ h;
+ if(tgt) {
+ if(M @ l) op.push(M);
+ } else {
+ real[] eq=changecoordsys(defaultcoordsys,equation(h)).a;
+ pair[] tp=intersectionpoints(locate(l.A),locate(l.B),eq);
+ point inter;
+ for (int i=0; i < tp.length; ++i) {
+ inter=point(R,tp[i]/R);
+ if(inter @ l) op.push(inter);
+ }
+ }
+ return op;
+}
+
+point[] intersectionpoints(hyperbola h, line l)
+{
+ return intersectionpoints(l,h);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,conic)"><code></asyxml>*/
+point[] intersectionpoints(line l, conic co)
+{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
+ intersectionpoints(conic,line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ if(co.e < 1) op=intersectionpoints((ellipse)co,l);
+ else
+ if(co.e == 1) op=intersectionpoints((parabola)co,l);
+ else op=intersectionpoints((hyperbola)co,l);
+ return op;
+}
+
+point[] intersectionpoints(conic co, line l)
+{
+ return intersectionpoints(l,co);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(conic,conic)"><code></asyxml>*/
+point[] intersectionpoints(conic co1, conic co2)
+{/*<asyxml></code><documentation>Return the intersection points of the two conics.</documentation></function></asyxml>*/
+ if(degenerate(co1)) return intersectionpoints(co1.l[0],co2);
+ if(degenerate(co2)) return intersectionpoints(co1,co2.l[0]);
+ return intersectionpoints(equation(co1), equation(co2));
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,conic,bool)"><code></asyxml>*/
+point[] intersectionpoints(triangle t, conic co, bool extended=false)
+{/*<asyxml></code><documentation>Return the intersection points.
+ If 'extended' is true, the sides are lines else the sides are segments.
+ intersectionpoints(conic,triangle,bool) is also defined.</documentation></function></asyxml>*/
+ if(degenerate(co)) return intersectionpoints(t,co.l[0],extended);
+ point[] OP;
+ void addpoint(point P[])
+ {
+ for (int i=0; i < P.length; ++i) {
+ if(defined(P[i])) {
+ bool exist=false;
+ for (int j=0; j < OP.length; ++j) {
+ if(P[i] == OP[j]) {exist=true; break;}
+ }
+ if(!exist) OP.push(P[i]);
+ }}}
+ if(extended) {
+ for (int i=0; i < 3; ++i) {
+ addpoint(intersectionpoints(t.line(i),co));
+ }
+ } else {
+ for (int i=0; i < 3; ++i) {
+ addpoint(intersectionpoints((segment)t.line(i),co));
+ }
+ }
+ return OP;
+}
+
+point[] intersectionpoints(conic co, triangle t, bool extended=false)
+{
+ return intersectionpoints(t,co,extended);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l,b);
+ // if(degenerate(b)) return intersectionpoints(a,b.l);;
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,circle)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, circle b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l,b);
+ // if(degenerate(b)) return intersectionpoints(a,b.l);;
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(circle a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints(b,a);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,parabola)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l,b);
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints(b,a);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(ellipse a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ // if(degenerate(a)) return intersectionpoints(a.l,b);
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,ellipse)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, ellipse b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints(b,a);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,parabola)"><code></asyxml>*/
+point[] intersectionpoints(circle a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,circle)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, circle b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(circle a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,circle)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, circle b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,parabola)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(parabola a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,parabola)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, parabola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,hyperbola)"><code></asyxml>*/
+point[] intersectionpoints(hyperbola a, hyperbola b)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ return intersectionpoints((conic)a,(conic)b);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(circle,circle)"><code></asyxml>*/
+point[] intersectionpoints(circle c1, circle c2)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ if(degenerate(c1))
+ return degenerate(c2) ?
+ new point[]{intersectionpoint(c1.l,c2.l)} : intersectionpoints(c1.l,c2);
+ if(degenerate(c2)) return intersectionpoints(c1,c2.l);
+ return (c1.C == c2.C) ?
+ new point[] :
+ intersectionpoints(radicalline(c1,c2),c1);
+}
+
+/*<asyxml><function type="line" signature="tangent(circle,abscissa)"><code></asyxml>*/
+line tangent(circle c, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'c' at 'point(c,x)'.</documentation></function></asyxml>*/
+ if(c.r == 0) abort("tangent: a circle with a radius equals zero has no tangent.");
+ point M=point(c,x);
+ return line(rotate(90,M)*c.C,M);
+}
+
+/*<asyxml><function type="line[]" signature="tangents(circle,point)"><code></asyxml>*/
+line[] tangents(circle c, point M)
+{/*<asyxml></code><documentation>Return the tangents of 'c' passing through 'M'.</documentation></function></asyxml>*/
+ line[] ol;
+ if(inside(c,M)) return ol;
+ if(M @ c) {
+ ol.push(tangent(c,relabscissa(c,M)));
+ } else {
+ circle cc=circle(c.C,M);
+ point[] inter=intersectionpoints(c,cc);
+ for (int i=0; i<inter.length; ++i)
+ ol.push(tangents(c,inter[i])[0]);
+ }
+ return ol;
+}
+
+/*<asyxml><function type="point" signature="point(circle,point)"><code></asyxml>*/
+point point(circle c, point M)
+{/*<asyxml></code><documentation>Return the intersection point of 'c'
+ with the half-line '[c.C M)'.</documentation></function></asyxml>*/
+ return intersectionpoints(c, line(c.C,false,M))[0];
+}
+
+/*<asyxml><function type="line" signature="tangent(circle,point)"><code></asyxml>*/
+line tangent(circle c, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'c' at the
+ intersection point of the half-line'[c.C M)'.</documentation></function></asyxml>*/
+ return tangents(c,point(c,M))[0];
+}
+
+/*<asyxml><function type="point" signature="point(circle,explicit vector)"><code></asyxml>*/
+point point(circle c, explicit vector v)
+{/*<asyxml></code><documentation>Return the intersection point of 'c'
+ with the half-line '[c.C v)'.</documentation></function></asyxml>*/
+ return point(c,c.C+v);
+}
+
+/*<asyxml><function type="line" signature="tangent(circle,explicit vector)"><code></asyxml>*/
+line tangent(circle c, explicit vector v)
+{/*<asyxml></code><documentation>Return the tangent of 'c' at the
+ point M so that vec(c.C M) is collinear to 'v' with the same sense.</documentation></function></asyxml>*/
+ line ol=tangent(c,c.C+v);
+ return dot(ol.v,v) > 0 ? ol : reverse(ol);
+}
+
+/*<asyxml><function type="line" signature="tangent(ellipse,abscissa)"><code></asyxml>*/
+line tangent(ellipse el, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'el' at 'point(el,x)'.</documentation></function></asyxml>*/
+ point M=point(el,x);
+ line l1=line(el.F1,M);
+ line l2=line(el.F2,M);
+ line ol=(l1 == l2) ? perpendicular(M,l1) : bisector(l1,l2,90,false);
+ return ol;
+}
+
+/*<asyxml><function type="line[]" signature="tangents(ellipse,point)"><code></asyxml>*/
+line[] tangents(ellipse el, point M)
+{/*<asyxml></code><documentation>Return the tangents of 'el' passing through 'M'.</documentation></function></asyxml>*/
+ line[] ol;
+ if(inside(el,M)) return ol;
+ if(M @ el) {
+ ol.push(tangent(el,relabscissa(el,M)));
+ } else {
+ point Mp=samecoordsys(M,el.F2) ?
+ M : changecoordsys(el.F2.coordsys,M);
+ circle c=circle(Mp,abs(el.F1-Mp));
+ circle cc=circle(el.F2,2*el.a);
+ point[] inter=intersectionpoints(c,cc);
+ for (int i=0; i<inter.length; ++i) {
+ line tl=line(inter[i],el.F2,false);
+ point[] P=intersectionpoints(tl,el);
+ ol.push(line(Mp,P[0]));
+ }
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="tangent(parabola,abscissa)"><code></asyxml>*/
+line tangent(parabola p, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'p' at 'point(p,x)' (use the Wells method).</documentation></function></asyxml>*/
+ line lt=rotate(90,p.V)*line(p.V,p.F);
+ point P=point(p,x);
+ if(P == p.V) return lt;
+ point M=midpoint(segment(P,p.F));
+ line l=rotate(90,M)*line(P,p.F);
+ return line(P,projection(lt)*M);
+}
+
+/*<asyxml><function type="line[]" signature="tangents(parabola,point)"><code></asyxml>*/
+line[] tangents(parabola p, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'p' at 'M' (use the Wells method).</documentation></function></asyxml>*/
+ line[] ol;
+ if(inside(p,M)) return ol;
+ if(M @ p) {
+ ol.push(tangent(p,angabscissa(p,M)));
+ }
+ else {
+ point Mt=changecoordsys(coordsys(p),M);
+ circle c=circle(Mt,p.F);
+ line l=rotate(90,p.V)*line(p.V,p.F);
+ point[] R=intersectionpoints(l,c);
+ for (int i=0; i < R.length; ++i) {
+ ol.push(line(Mt,R[i]));
+ }
+ // An other method: http://www.du.edu/~jcalvert/math/parabola.htm
+ // point[] R=intersectionpoints(p.directrix,c);
+ // for (int i=0; i < R.length; ++i) {
+ // ol.push(bisector(segment(p.F,R[i])));
+ // }
+ }
+ return ol;
+}
+
+/*<asyxml><function type="line" signature="tangent(hyperbola,abscissa)"><code></asyxml>*/
+line tangent(hyperbola h, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'h' at 'point(p,x)'.</documentation></function></asyxml>*/
+ point M=point(h,x);
+ line ol=bisector(line(M,h.F1),line(M,h.F2));
+ if(sameside(h.F1,h.F2,ol) || ol == line(h.F1,h.F2)) ol=rotate(90,M)*ol;
+ return ol;
+}
+
+/*<asyxml><function type="line[]" signature="tangents(hyperbola,point)"><code></asyxml>*/
+line[] tangents(hyperbola h, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'h' at 'M'.</documentation></function></asyxml>*/
+ line[] ol;
+ if(M @ h) {
+ ol.push(tangent(h,angabscissa(h,M,fromCenter)));
+ } else {
+ coordsys cano=canonicalcartesiansystem(h);
+ bqe bqe=changecoordsys(cano,equation(h));
+ real a=abs(1/(bqe.a[5]*bqe.a[0])), b=abs(1/(bqe.a[5]*bqe.a[2]));
+ point Mp=changecoordsys(cano,M);
+ real x0=Mp.x, y0=Mp.y;
+ if(abs(x0) > epsgeo) {
+ real c0=a*y0^2/(b*x0)^2-1/b,
+ c1=2*a*y0/(b*x0^2), c2=a/x0^2-1;
+ real[] sol=quadraticroots(c0,c1,c2);
+ for (real y:sol) {
+ point tmp=changecoordsys(coordsys(h), point(cano,(a*(1+y*y0/b)/x0,y)));
+ ol.push(line(M,tmp));
+ }
+ } else if(abs(y0) > epsgeo) {
+ real y=-b/y0, x=sqrt(a*(1+b/y0^2));
+ ol.push(line(M,changecoordsys(coordsys(h),point(cano,(x,y)))));
+ ol.push(line(M,changecoordsys(coordsys(h),point(cano,(-x,y)))));
+ }}
+ return ol;
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(conic,arc)"><code></asyxml>*/
+point[] intersectionpoints(conic co, arc a)
+{/*<asyxml></code><documentation>intersectionpoints(arc,circle) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ point[] tp=intersectionpoints(co,(conic)a.el);
+ for (int i=0; i<tp.length; ++i)
+ if(tp[i] @ a) op.push(tp[i]);
+ return op;
+}
+
+point[] intersectionpoints(arc a, conic co)
+{
+ return intersectionpoints(co,a);
+}
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(arc,arc)"><code></asyxml>*/
+point[] intersectionpoints(arc a1, arc a2)
+{/*<asyxml></code><documentation></documentation></function></asyxml>*/
+ point[] op;
+ point[] tp=intersectionpoints(a1.el,a2.el);
+ for (int i=0; i<tp.length; ++i)
+ if(tp[i] @ a1 && tp[i] @ a2) op.push(tp[i]);
+ return op;
+}
+
+
+/*<asyxml><function type="point[]" signature="intersectionpoints(line,arc)"><code></asyxml>*/
+point[] intersectionpoints(line l, arc a)
+{/*<asyxml></code><documentation>intersectionpoints(arc,line) is also defined.</documentation></function></asyxml>*/
+ point[] op;
+ point[] tp=intersectionpoints(a.el,l);
+ for (int i=0; i<tp.length; ++i)
+ if(tp[i] @ a && tp[i] @ l) op.push(tp[i]);
+ return op;
+}
+
+point[] intersectionpoints(arc a, line l)
+{
+ return intersectionpoints(l,a);
+}
+
+/*<asyxml><function type="point" signature="arcsubtendedcenter(point,point,real)"><code></asyxml>*/
+point arcsubtendedcenter(point A, point B, real angle)
+{/*<asyxml></code><documentation>Return the center of the arc retuned
+ by the 'arcsubtended' routine.</documentation></function></asyxml>*/
+ point OM;
+ point[] P=standardizecoordsys(A,B);
+ angle=angle%(sgnd(angle)*180);
+ line bis=bisector(P[0],P[1]);
+ line AB=line(P[0],P[1]);
+ return intersectionpoint(bis,rotate(90-angle,A)*AB);
+}
+
+/*<asyxml><function type="arc" signature="arcsubtended(point,point,real)"><code></asyxml>*/
+arc arcsubtended(point A, point B, real angle)
+{/*<asyxml></code><documentation>Return the arc circle from which the segment AB is saw with
+ the angle 'angle'.
+ If the point 'M' is on this arc, the oriented angle (MA,MB) is
+ equal to 'angle'.</documentation></function></asyxml>*/
+ point[] P=standardizecoordsys(A,B);
+ line AB=line(P[0],P[1]);
+ angle=angle%(sgnd(angle)*180);
+ point C=arcsubtendedcenter(P[0],P[1],angle);
+ real BC=degrees(B-C)%360;
+ real AC=degrees(A-C)%360;
+ return arc(circle(C,abs(B-C)),BC,AC, angle > 0 ? CCW : CW);
+}
+
+/*<asyxml><function type="arc" signature="arccircle(point,point,point)"><code></asyxml>*/
+arc arccircle(point A, point M, point B)
+{/*<asyxml></code><documentation>Return the CCW arc circle 'AB' passing through 'M'.</documentation></function></asyxml>*/
+ circle tc=circle(A,M,B);
+ real a=degrees(A-tc.C);
+ real b=degrees(B-tc.C);
+ arc oa=arc(tc,a,b);
+ if(!(M @ oa)) oa.direction=!oa.direction;
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="arc(ellipse,abscissa,abscissa,bool)"><code></asyxml>*/
+arc arc(ellipse el, explicit abscissa x1, explicit abscissa x2, bool direction=CCW)
+{/*<asyxml></code><documentation>Return the arc from 'point(c,x1)' to 'point(c,x2)' in the direction 'direction'.</documentation></function></asyxml>*/
+ real a=degrees(point(el,x1)-el.C);
+ real b=degrees(point(el,x2)-el.C);
+ arc oa=arc(el,a,b,fromCenter,direction);
+ return oa;
+}
+
+/*<asyxml><function type="arc" signature="arc(ellipse,point,point,bool)"><code></asyxml>*/
+arc arc(ellipse el, point M, point N, bool direction=CCW)
+{/*<asyxml></code><documentation>Return the arc from 'M' to 'N' in the direction 'direction'.
+ The points 'M' and 'N' must belong to the ellipse 'el'.</documentation></function></asyxml>*/
+ return arc(el, relabscissa(el,M), relabscissa(el,N), direction);
+}
+
+/*<asyxml><function type="arc" signature="arccircle(point,point,real,bool)"><code></asyxml>*/
+arc arccircle(point A, point B, real angle, bool direction=CCW)
+{/*<asyxml></code><documentation>Return the arc circle centered on A
+ from B to rotate(angle,A)*B in the direction 'direction'.</documentation></function></asyxml>*/
+ point M=rotate(angle,A)*B;
+ return arc(circle(A,abs(A-B)),B,M,direction);
+}
+
+
+/*<asyxml><function type="arc" signature="arc(explicit arc,abscissa,abscissa)"><code></asyxml>*/
+arc arc(explicit arc a, abscissa x1, abscissa x2)
+{/*<asyxml></code><documentation>Return the arc from 'point(a,x1)' to 'point(a,x2)' traversed in the direction of the arc direction.</documentation></function></asyxml>*/
+ real a1=angabscissa(a.el, point(a,x1), a.polarconicroutine).x;
+ real a2=angabscissa(a.el, point(a,x2), a.polarconicroutine).x;
+ return arc(a.el, a1, a2, a.polarconicroutine, a.direction);
+}
+
+/*<asyxml><function type="arc" signature="arc(explicit arc,point,point)"><code></asyxml>*/
+arc arc(explicit arc a, point M, point N)
+{/*<asyxml></code><documentation>Return the arc from 'M' to 'N'.
+ The points 'M' and 'N' must belong to the arc 'a'.</documentation></function></asyxml>*/
+ return arc(a, relabscissa(a,M), relabscissa(a,N));
+}
+
+/*<asyxml><function type="arc" signature="inverse(real,point,segment)"><code></asyxml>*/
+arc inverse(real k, point A, segment s)
+{/*<asyxml></code><documentation>Return the inverse arc circle of 's'
+ with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
+ point Ap=inverse(k,A,s.A), Bp=inverse(k,A,s.B),
+ M=inverse(k,A,midpoint(s));
+ return arccircle(Ap,M,Bp);
+}
+
+/*<asyxml><operator type="arc" signature="*(inversion,segment)"><code></asyxml>*/
+arc operator *(inversion i, segment s)
+{/*<asyxml></code><documentation>Provide
+ inversion*segment.</documentation></operator></asyxml>*/
+ return inverse(i.k,i.C,s);
+}
+
+/*<asyxml><operator type="path" signature="*(inversion,triangle)"><code></asyxml>*/
+path operator *(inversion i, triangle t)
+{/*<asyxml></code><documentation>Provide inversion*triangle.</documentation></operator></asyxml>*/
+ return (path)(i*segment(t.AB))--
+ (path)(i*segment(t.BC))--
+ (path)(i*segment(t.CA))--cycle;
+}
+
+/*<asyxml><function type="path" signature="compassmark(pair,pair,real,real)"><code></asyxml>*/
+path compassmark(pair O, pair A, real position, real angle=10)
+{/*<asyxml></code><documentation>Return an arc centered on O with the angle 'angle' so that the position
+ of 'A' on this arc makes an angle 'position*angle'.</documentation></function></asyxml>*/
+ real a=degrees(A-O);
+ real pa=(a-position*angle)%360,
+ pb=(a-(position-1)*angle)%360;
+ real t1=intersect(unitcircle,(0,0)--2*dir(pa))[0];
+ real t2=intersect(unitcircle,(0,0)--2*dir(pb))[0];
+ int n=length(unitcircle);
+ if(t1 >= t2) t1 -= n;
+ return shift(O)*scale(abs(O-A))*subpath(unitcircle,t1,t2);
+}
+
+/*<asyxml><function type="line" signature="tangent(explicit arc,abscissa)"><code></asyxml>*/
+line tangent(explicit arc a, abscissa x)
+{/*<asyxml></code><documentation>Return the tangent of 'a' at 'point(a,x)'.</documentation></function></asyxml>*/
+ abscissa ag=angabscissa(a,point(a,x));
+ return tangent(a.el,ag+a.angle1+(a.el.e == 0 ? a.angle0 : 0));
+}
+
+/*<asyxml><function type="line" signature="tangent(explicit arc,point)"><code></asyxml>*/
+line tangent(explicit arc a, point M)
+{/*<asyxml></code><documentation>Return the tangent of 'a' at 'M'.
+ The points 'M' must belong to the arc 'a'.</documentation></function></asyxml>*/
+ return tangent(a, angabscissa(a,M));
+}
+
+// *=======================================================*
+// *.......Routines for compatibility with original geometry module........*
+
+path square(pair z1, pair z2)
+{
+ pair v=z2-z1;
+ pair z3=z2+I*v;
+ pair z4=z3-v;
+ return z1--z2--z3--z4--cycle;
+}
+
+// Draw a perpendicular symbol at z aligned in the direction align
+// relative to the path z--z+dir.
+void perpendicular(picture pic=currentpicture, pair z, pair align,
+ pair dir=E, real size=0, pen p=currentpen,
+ margin margin=NoMargin, filltype filltype=NoFill)
+{
+ perpendicularmark(pic,(point) z,align,dir,size,p,margin,filltype);
+}
+
+
+// Draw a perpendicular symbol at z aligned in the direction align
+// relative to the path z--z+dir(g,0)
+void perpendicular(picture pic=currentpicture, pair z, pair align, path g,
+ real size=0, pen p=currentpen, margin margin=NoMargin,
+ filltype filltype=NoFill)
+{
+ perpendicularmark(pic,(point) z,align,dir(g,0),size,p,margin,filltype);
+}
+
+// Return an interior arc BAC of triangle ABC, given a radius r > 0.
+// If r < 0, return the corresponding exterior arc of radius |r|.
+path arc(explicit pair B, explicit pair A, explicit pair C,
+ real r=arrowfactor)
+{
+ return arc(A,r,degrees(B-A),degrees(C-A));
+}
+
+// *.......End of compatibility routines........*
+// *=======================================================*
+
+// *........................FOOTER.........................*
+// *=======================================================*